T
Telephony SOPKnowledge Base
Search
← All topics

Call Routing Strategies — Round-Robin, Skill-Based, Geo, Time-of-Day

runnable

Seven primary inbound call routing strategies: simple forward, ring-all, hunt group (sequential), round-robin (load-balanced), skill-based, geo-based, and time-of-day. Each has distinct SWML/LaML patterns and tradeoffs in answer rate, agent fairness, and customer experience. Skill-based requires external state (CRM or DB); the others can run purely in SWML.

call-routinghunt-groupround-robinskill-basedring-strategysignalwire
Agent trigger phrases: call routing strategies · ring strategy · hunt group SWML · round robin call routing · skill based routing · geo routing voice · call queue strategy

Call Routing Strategies

The decision tree from "phone rings" to "human answers" has 7 canonical strategies. Each has different math, different agent fairness, and different customer-experience trade-offs.

1. Simple forward

One number forwards to one destination. The simplest case.

- connect:
    to: +13105551111
    timeout: 30

When to use: single-agent operations, small businesses with one phone line, after-hours forwarding to on-call. Trade-off: zero redundancy — if the destination doesn't answer, the call fails.

2. Ring all (simultaneous)

Ring every agent at once. First to answer wins.

- connect:
    to:
      - +13105551111
      - +13105552222
      - +13105553333
    timeout: 30

When to use: small teams (3-5 agents), urgent service categories where first-available wins (emergency plumbing, locksmiths). Trade-off: disruptive — every phone rings every call. Agents annoyed. Higher answer rate than hunt group but worse for agent focus.

3. Hunt group (sequential / find-me-follow-me)

Try agent 1, then agent 2, then agent 3 in sequence. Each gets the full ring timeout before falling through.

- connect:
    to: +13105551111
    timeout: 15
- connect:
    to: +13105552222
    timeout: 15
- connect:
    to: +13105553333
    timeout: 15
- play: say:All agents are busy, please leave a message
- record_call: {}

When to use: strict seniority hierarchy (senior agent gets first shot), or after-hours escalation chains (try office → try cell → try backup). Trade-off: unfair — agent 1 takes all the calls, agents 2-3 starve.

4. Round-robin (load balanced)

Cycle through agents so each gets roughly equal call volume. Requires state (which agent was last called).

Stateful pattern with external store

- request:
    url: https://your.api/get-next-agent?queue=sales
    method: GET
    save: routing
- connect:
    to: "${routing.next_agent}"
    timeout: 30
- request:
    url: https://your.api/release-agent
    method: POST
    body:
      agent: "${routing.next_agent}"
      call_id: "${call.id}"

Your API tracks the rotation and returns the next agent in line. On call end, mark the agent available again.

Variation: weighted round-robin

Agents with higher capacity get more calls per cycle:

agents = [
    ("+13105551111", 3),  # gets 3 of every 6 calls
    ("+13105552222", 2),  # gets 2 of every 6 calls
    ("+13105553333", 1),  # gets 1 of every 6 calls
]

When to use: call centers with fungible agent skills, fairness matters. Trade-off: requires external state, slightly slower routing decision.

5. Skill-based routing

Match the caller to an agent with relevant expertise (language, certification, product knowledge). Requires caller-intent capture and agent-skill metadata.

Capture intent via IVR or AI

- prompt:
    play: say:Press 1 for billing, 2 for technical support, 3 for new sales
    max_digits: 1
- request:
    url: https://your.api/route?skill=${prompt_value}
    method: GET
    save: route
- connect:
    to: "${route.agent}"
    timeout: 30

Capture intent via AI receptionist

- ai:
    prompt:
      text: |
        Identify the caller's reason. Call route_to_agent with one of:
        billing, technical, sales, retention.
    SWAIG:
      functions:
        - function: route_to_agent
          parameters:
            type: object
            properties:
              skill: { type: string, enum: ["billing", "technical", "sales", "retention"] }
          data_map:
            webhooks:
              - url: https://your.api/route

When to use: multi-product or multi-language operations, complex L1/L2/L3 support tiers. Trade-off: highest ops complexity, requires accurate skill tagging in the agent directory.

6. Geo-based routing

Route by caller's location — area code, IP/ANI lookup, or stated location.

- request:
    url: https://your.api/geo-route?phone=${call.from}
    method: GET
    save: geo
- connect:
    to: "${geo.regional_office}"
    timeout: 30

The API does an area-code-to-region lookup or queries an LRN database for higher accuracy.

When to use: multi-office businesses where local presence matters, regulated industries needing in-state agents (insurance, real estate). Trade-off: area code is unreliable for mobile users; LRN lookups cost money.

7. Time-of-day routing

Different destinations during different hours. See business hours logic for the full pattern.

- cond:
    when: "is_business_hours()"
    then:
      - connect:
          to: +13105551111
    else:
      - connect:
          to: +13105559999  # after-hours service

When to use: any operation with non-24/7 hours.

Hybrid strategies

Real production routers combine strategies. Common hybrids:

Time + skill + round-robin

inbound call
  ├─ if after hours → voicemail
  ├─ if business hours
  │    └─ IVR captures skill
  │         └─ round-robin among agents with that skill
  │              └─ if no skill match → general queue

Geo + skill

inbound call
  └─ geo-route to regional office
       └─ skill-based routing within that office
            └─ overflow to nearest neighboring office

Ring-all-then-hunt

inbound call
  └─ ring-all 3 senior agents (15 sec timeout)
       └─ if no answer, hunt through junior agents
            └─ if no answer, voicemail

Comparison matrix

| Strategy | Setup complexity | Answer rate | Agent fairness | Customer experience | |---|---|---|---|---| | Simple forward | Trivial | Low | N/A | Mediocre (no redundancy) | | Ring all | Low | Highest | Poor (everyone disrupted) | Good (fastest answer) | | Hunt group | Low | Medium | Very poor | OK (with reasonable timeouts) | | Round-robin | Medium | Medium-High | Excellent | Good | | Skill-based | High | Medium-High | Configurable | Excellent | | Geo-based | Medium | Medium | Variable | Good (local touch) | | Time-of-day | Low | N/A | N/A | Critical for hours-of-operation |

Per-call decision time

Routing decision time impacts customer experience. Targets:

| Pattern | Decision overhead | |---|---| | SWML inline cond | < 50ms | | SWML + external API | 100-500ms | | SWML + AI classification | 2-5 seconds | | Multi-step IVR + API | 5-15 seconds |

Caller perception: silence under 1 second feels normal; 1-3 seconds feels like a hiccup; 3+ seconds feels broken unless filled with "one moment" TTS.

State management for stateful strategies

Round-robin and skill-based need state. Three storage options:

| Option | Latency | Durability | Complexity | |---|---|---|---| | Redis | < 5ms | Volatile | Low | | Supabase | 30-100ms | Durable | Low | | SignalWire global_data | inline | Per-call only (not persistent across calls) | Trivial | | In-memory cache | < 1ms | Lost on restart | Low |

For round-robin, a Redis counter with atomic increment is the cleanest pattern. For skill-based, durable state (Supabase) so agent skills survive restarts.

SignalWire-specific: AI agent as router

Use an AI agent as the routing layer:

- ai:
    prompt:
      text: |
        You are a phone receptionist. Identify the caller's needs and route them.
        Use route_caller to direct them based on their intent.
    SWAIG:
      functions:
        - function: route_caller
          parameters:
            type: object
            properties:
              department: { type: string, enum: ["sales", "support", "billing"] }
              urgency: { type: string, enum: ["normal", "urgent"] }
          data_map:
            webhooks:
              - url: https://your.api/ai-route

The AI handles disambiguation, language detection, and intent capture — much better than DTMF IVR for complex routing.

Common pitfalls

  • No fallback — every strategy needs a fallback (voicemail, queue, general line). When the primary fails, dead air is the worst outcome.
  • Ring timeout too long — 30 seconds feels like an eternity when nothing happens. 12-18 seconds is the sweet spot.
  • Round-robin without release — agents marked busy after a call but never released, queue clogs.
  • Skill-based with stale skills — agent skills change, the routing table doesn't. Add an admin UI for self-service skill updates.
  • Geo-based on mobile-only callers — area code accuracy is poor for cell users. Ask the caller for location during the IVR.

Related patterns

References

  • SignalWire SWML connect verb documentation
  • ACD (Automatic Call Distributor) literature — standard call-center routing models