> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.voicebip.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.voicebip.com/_mcp/server.

# Multi-Agent Routing

> One number, many specialised agents. Route each caller to the right agent by what they say — "sales" vs "support" — using keyword or regex rules with a catch-all default.

**What you build:** a single phone number fronting several purpose-built agents. A routing rule inspects the caller's **first utterance** and hands the call to the matching agent — so you keep one crisp system prompt per job instead of one bloated agent trying to do everything.

```
Caller dials +234… ─▶ speaks first line ─▶ routing rules (by priority, first match wins)
                         │ says "sales" / "buy"     ─▶ agt_sales
                         │ says "support" / "help"   ─▶ agt_support
                         └ (no keyword matched)      ─▶ default ─▶ agt_support
```

**Who it's for:** any team that has outgrown a single agent. This is Voicebip's answer to "squads/workflows" — code-first rules matched on speech, not a visual builder.

> **Note**
>
> Routing matches the **caller's spoken first utterance** with `keyword` or `regex` rules (plus a `default` catch-all). It is not a DTMF/IVR menu and not a visual flow canvas. Model each branch as a rule targeting a dedicated agent.

## Prerequisites

* Two or more [agents](/use-cases/inbound-support-kb), each with a focused `system_prompt`.
* A number bound to serve inbound calls (`GET /v1/numbers` for its `number_id`).

## Step 1 — Build the specialised agents

Keep each one narrow. For example a sales agent and a support agent grounded in the FAQ KB:

```bash
curl -X POST https://api.voicebip.com/v1/agents -H "Authorization: Bearer pk_xxx" \
  -H "Content-Type: application/json" -d '{
    "display_name": "Acme Sales", "ai_provider": "hosted",
    "system_prompt": "You qualify inbound sales leads: budget, timeline, use case. Book a demo or transfer to a rep.",
    "tts_voice": "<voice_id>" }'
# → agt_sales_…

curl -X POST https://api.voicebip.com/v1/agents -H "Authorization: Bearer pk_xxx" \
  -H "Content-Type: application/json" -d '{
    "display_name": "Acme Support", "ai_provider": "hosted",
    "knowledge_base_id": "kb_8sd2k1fapvq3",
    "system_prompt": "You answer support questions from the knowledge base. Escalate billing disputes.",
    "tts_voice": "<voice_id>" }'
# → agt_support_…
```

## Step 2 — Create routing rules

Rules are evaluated by `priority` (lowest first); the first match wins. `match_type` is `keyword`, `regex`, or `default`:

```bash
curl -X POST https://api.voicebip.com/v1/routing-rules \
  -H "Authorization: Bearer pk_xxx" -H "Content-Type: application/json" \
  -d '{
    "number_id": "num_7hq2k9wz1p4c",
    "priority": 10,
    "match_type": "keyword",
    "match_value": "sales",
    "target_agent_id": "agt_sales_…"
  }'
```

| `match_type` | Matches on                                       | `match_value` |         |          |
| ------------ | ------------------------------------------------ | ------------- | ------- | -------- |
| `keyword`    | The word appears in the caller's first utterance | `"sales"`     |         |          |
| `regex`      | Regex pattern against the first utterance        | \`"(buy       | pricing | demo)"\` |
| `default`    | Everything unmatched (no `match_value`)          | —             |         |          |

Add a `default` rule with a high `priority` number so no caller falls through:

```json
{ "number_id": "num_7hq2k9wz1p4c", "priority": 1000, "match_type": "default", "target_agent_id": "agt_support_…" }
```

## Step 3 — List, update, delete

```bash
curl "https://api.voicebip.com/v1/routing-rules" -H "Authorization: Bearer pk_xxx"
curl -X PATCH https://api.voicebip.com/v1/routing-rules/rule_… \
  -H "Authorization: Bearer pk_xxx" -H "Content-Type: application/json" -d '{ "priority": 5 }'
curl -X DELETE https://api.voicebip.com/v1/routing-rules/rule_… -H "Authorization: Bearer pk_xxx"
```

## Handing off mid-conversation

Routing chooses the *entry* agent. To move a caller between agents/humans **during** a call, the current agent uses the built-in `transfer_call` tool (see [Tool-calling](/use-cases/tool-calling)) — e.g. support escalates a billing dispute to a human queue.

## Test it (sandbox)

```bash
# Simulate an inbound call and confirm your rules resolve
curl -X POST https://api.voicebip.com/v1/webhooks/test \
  -H "Authorization: Bearer pk_test_xxx" -H "Content-Type: application/json" \
  -d '{ "webhook_url": "https://you.example.com/webhooks", "event_type": "call.initiated" }'
```

Check `GET /v1/analytics` for per-agent volume once traffic flows.

## Troubleshooting

| Symptom                      | Likely cause                                                                               |
| ---------------------------- | ------------------------------------------------------------------------------------------ |
| Everyone hits the same agent | Only the `default` matched — check that keyword/regex rules have lower `priority` numbers. |
| A rule never fires           | A lower-`priority` (earlier) rule already matched — first match wins.                      |
| Keyword never matches        | The caller didn't say the exact word; add synonyms as a `regex` rule.                      |

## Next steps

* Give each agent live actions → [Tool-calling](/use-cases/tool-calling).
* Ground the support agent in docs → [Inbound support + KB](/use-cases/inbound-support-kb).
* Serve callers by language — route to `ha`/`yo`/`ig` agents on language keywords.