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.
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.
What does this prompt do?
- 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 do I need to use this?
- 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.
How can I customize it?
- 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.
FAQs
Why do I need this when LiveKit already has a dashboard?
Does the test button actually ring a real phone?
What is a catch-all rule and why does the audit care about it?
Can I see who changed a routing rule last month?
Do I need to be an engineer to use this?
Related templates
Every weekday at 10am, an AI voice agent phones your most overdue Xero accounts, logs what each customer promised, and reports back to finance in Slack.
Every logo, photo, video cut and ad export in one searchable grid, with previews, campaign tags and rights expiry dates at a glance.
Twice a day we spot the people clicking your pricing and demo pages, create a follow-up task for their owner, and post a ranked recap to Slack.
One board showing every dashboard and Look with its folder, owner and favorite count, so you can find dead content and retire it safely.
Watch every live room on one screen, remove disruptive guests, start recordings and end stuck sessions without anyone opening a terminal.
Every Monday we find leads who never bought, research a real reason to reach out, and send a short personal email instead of another check in.
Stop guessing which rule answers which phone number.
Put your LiveKit call routing on one screen, prove it works with a real test call, and keep a record of every change.