# Phone routing control panel for LiveKit voice agents

> See every phone number, the route it takes and the voice agent that answers, then change routing safely with a real test call and a full change log.

- Workflow type: app
- Services: LiveKit, Slack Bot, General Input Database
- Categories: Operations, Engineering
- Published: 2026-08-31

## What it does

- One table maps every phone number to the connection it arrives on, the rule that matches it, the room it creates and the voice agent that answers, with your outbound carrier connections on a second tab.
- Create, edit and delete routing rules and phone connections right from the screen, with the current settings prefilled and a required reason note on every change.
- Hit "Test this route" to place a real call to a number you type in and bring the voice agent onto it, so you can hear the route work before you rely on it.
- Hit "Audit my routing" and a background agent checks the whole setup for numbers nothing answers, rules pointing at connections that no longer exist, and catch-all rules quietly overriding a number-specific one, then writes what it finds back into the app.

## What you'll need

- A LiveKit project with phone calling already set up, plus the project URL, key and secret from your project settings.
- A LiveKit key that is allowed to manage telephony settings. If it is not, the app says so in plain language instead of showing an error code.
- A Slack workspace and the channel where change notices should be posted.
- The name of the voice agent that should answer inbound calls, and a phone number you can safely ring for test calls.

## Prompt

Build me a phone routing control panel for LiveKit telephony so the person who owns our phone numbers can see and change call routing without hand writing API calls. The audience is whoever owns telephony config for a team running LiveKit SIP voice agents.

The main screen is one table, the number map. One row per phone number, with columns for the number, the inbound trunk it lands on, the dispatch rule that matches it, the room naming pattern that rule creates, and the agent name it hands off to. Build the table in a handler that calls List SIP Inbound Trunks, List SIP Outbound Trunks and List SIP Dispatch Rules and joins them: numbers come from each inbound trunk's number list, a rule attaches to a row when its trunk ids include that trunk or when it names the number explicitly, and a rule with no trunk ids is a catch-all that applies to every trunk. Show the room prefix or pattern and the agent name from the rule's room configuration. Flag rows inline when no rule matches the number, when the only match is a catch-all rather than a number scoped rule, and when a rule points at a trunk id that is not in the trunk list.

A second tab lists outbound trunks and their carrier details from List SIP Outbound Trunks, showing the trunk name, carrier address, associated numbers, auth username and transport. Clicking a row opens the full record via Get SIP Outbound Trunk, and inbound rows on the first tab do the same through Get SIP Inbound Trunk.

From the same screen I can manage config. Create, edit and delete routing rules with Create SIP Dispatch Rule, Update SIP Dispatch Rule and Delete SIP Dispatch Rule. Add or edit trunks with Create SIP Inbound Trunk, Update SIP Inbound Trunk, Create SIP Outbound Trunk and Update SIP Outbound Trunk, and remove either kind with Delete SIP Trunk. Every create, edit and delete opens a form with the current values prefilled and a required reason note that cannot be skipped, and deletes ask for confirmation naming exactly what will be removed and which numbers it affects.

A "Test this route" button on each row places a real outbound call to a number I type in, using Create SIP Participant through the outbound trunk I pick, then attaches the agent to that same room with Create Agent Dispatch, so I can confirm the route rings and the agent answers before I trust it. Show the resulting room name and participant identity in the app, and keep the last few test results per row with the time, the number dialed and whether the call and the agent dispatch both succeeded.

An "Audit my routing" button runs a background agent that reads every trunk and rule with List SIP Inbound Trunks, List SIP Outbound Trunks and List SIP Dispatch Rules and writes its findings back into the app, rendered as a findings panel beside the table. It looks for three things specifically: phone numbers on a trunk with no rule matching them, rules pointing at trunk ids that no longer exist, and catch-all rules that silently shadow a number scoped rule. That last one is a known LiveKit trap: a dispatch rule scoped to specific phone numbers on a shared inbound trunk can silently fail to match, while the same rule written as a trunk wide catch-all fires instantly, so routing breaks with no error surfaced anywhere. See https://github.com/livekit/agents/issues/5847 and https://github.com/livekit/sip/issues/213, with reference docs at https://docs.livekit.io/sip/dispatch-rule/ and https://docs.livekit.io/telephony/start/sip-trunk-setup/. Each finding should name the affected number or rule, say why it matters and give the concrete fix, and findings persist so I can compare one audit against the last.

Every config change is written to the General Input Database as a change log entry with who made it, the timestamp, the object changed, the previous values, the new values and the required reason note. The app has a change log view I can filter by number, rule or person. The same entry is posted to a Slack channel I configure using Slack Bot Send a Message, formatted as a short readable summary naming the person, the object, what changed from what to what, and the reason. LiveKit keeps no history of who changed routing, so this app is the record.

SIP operations need a token carrying the sip.admin grant. When a call comes back permission denied, surface a clear message saying the LiveKit key is not allowed to manage telephony settings and what needs to change, rather than a raw Twirp error code. Do the same for any rejected create or update: keep the form open with the values I typed and show in plain language what the API refused.

## How to customize

- Pick which Slack channel gets change notices, and whether every edit or only deletions gets announced.
- Set the room naming pattern new calls should use, and the default voice agent a new rule hands off to.
- Adjust what the audit treats as a problem, for example whether a catch-all rule covering a number is a warning or an error in your setup.
- Save a default test number so the test call form is prefilled.

## FAQ

### Why do I need this when LiveKit already has a dashboard?

Nothing in LiveKit shows you one row per phone number tying that number to the connection it lands on, the rule that matches it and the agent that answers. This app builds that view for you, and it also keeps the history of who changed routing and why, which LiveKit does not record at all.

### Does the test button actually ring a real phone?

Yes. It places a genuine outbound call to the number you enter through the carrier connection you choose and brings your voice agent onto the same call, so you hear the route working end to end. It uses real call minutes, so use a number you own.

### What is a catch-all rule and why does the audit care about it?

A catch-all rule applies to every number on a connection rather than a specific one. It is a well known trap in LiveKit that a rule written for specific phone numbers on a shared connection can quietly fail to match, while the same rule written as a catch-all works instantly. Routing then breaks with no error shown anywhere, so the audit calls out every place a catch-all is silently covering a number that has its own rule.

### Can I see who changed a routing rule last month?

Yes. Every create, edit and delete is saved with the person, the time, the previous settings, the new settings and the reason they typed in, and the app has a change log you can filter by number, rule or person.

### Do I need to be an engineer to use this?

Not to read the table, run the audit or test a route. Adding a brand new carrier connection still needs the address and sign-in details your phone provider gave you, so keep those handy or ask whoever set up the account.

Use this prompt in General Input: https://www.generalinput.com/prompts/phone-routing-control-panel-for-livekit-voice-agents