One voice agent can route callers for 50 locations by reading the number they dialed. Pre-Call Data Fetch sends that number to your endpoint, which returns the branch's hours, local time and staff line before the greeting. A brand-wide number asks for a ZIP code instead. Dograh, an open-source AI voice agent platform, supports this setup.
Key Takeaways
- The dialed number is the strongest location signal. Area codes are weak.
- Return local time and an open-now flag from the lookup, per branch.
- A transfer to a branch's staff needs a fallback for when nobody answers.
This post is part of our guide to AI voice agents for franchises and multi-location businesses, where every site keeps its own number. Here we route on that number in Dograh, with a lookup before hello, one prompt for every branch, a ZIP prompt for brand-wide lines and staff transfers.
The number that rang tells you the branch
The most reliable routing signal is the number the caller dialed, which says more than anything about the caller. On every phone call, Dograh records it as called_number, and the caller's own number as caller_number. When each branch has its own number, the dialed number names the branch before anyone speaks. In our post on international SIP trunks, the dialed number does the same job for lines in different countries.
Most calls arrive that way, because Google's Business Profile guidelines ask for a number "that connects to your individual business location". The caller's own number is a much weaker hint. Mobile numbers move with their owners, and area codes cross territory lines, so a 303 number may be calling from Florida. Use it to find the caller's record, never to pick their branch.
Getting the branch right matters more when one operator owns the neighbors. The multi-unit operators in FRANdata's figures make up 19.3% of franchisees yet hold 58.8% of franchised locations, so the branch next door is often the same business and a fine place to send an overflow call.
Load the branch's details before hello
Pre-Call Data Fetch turns the dialed number into everything the agent needs to know about that branch. You set it at the bottom of the Start Call node to Inbound calls only, with your Endpoint URL and an optional credential. When a call connects, Dograh posts the call's numbers to your endpoint. The fields to read are to_number, the branch line that rang, and from_number, the caller:
{
"event": "call_inbound",
"call_inbound": {
"agent_id": 123,
"workflow_run_id": 456,
"from_number": "+1415555XXXX",
"to_number": "+1720555XXXX"
}
}
What this does: It tells your endpoint that someone at +1415555XXXX rang the Denver branch's number. Your endpoint looks that number up in its own branch table.
Your endpoint answers with the branch's details under initial_context. Include local_time and open_now worked out in the branch's own time zone, and transfer_destination for its staff line:
{
"initial_context": {
"location_id": "den-02",
"location_name": "Denver Colfax",
"address": "1200 Main St, Denver",
"hours_today": "8 am to 6 pm",
"local_time": "4:35 pm",
"open_now": true,
"services": "oil changes, brakes and tires",
"transfer_destination": "+1720555XXXX"
}
}
What this does: It loads the Denver branch into the call. The agent can now greet the caller with the branch name, say it closes at 6 pm and hand over to that branch's staff line.
Meanwhile the caller hears ringing, and the greeting holds for up to 10 seconds until the lookup answers. If your endpoint fails or runs out of time, the call carries on without the branch details, so answer from a fast branch table rather than a slow CRM query. Each request carries an Idempotency-Key header, and Dograh sends it once per call.
Open Source Alternative to Vapi / Retell
Self-hosted voice agent platform — no per-minute fees
dograh-hq/dograh
Star on GitHub
One prompt for every branch
With the branch loaded, one prompt serves all 50. The greeting and the prompt read the fields directly, such as "Thanks for calling {{location_name | our team}}" in the Greeting Text. The part after the bar is a fallback, spoken if the lookup failed, because a missing value is otherwise spoken as nothing at all.
Time is where a shared prompt trips. Dograh has built-in time variables such as {{current_time_America/Denver}}, but the zone is written into the prompt text, so one prompt cannot switch zones per branch by itself. That is why the lookup returns local_time and open_now. The prompt then says "If open_now is false, tell the caller today's hours and offer a callback", and every branch behaves correctly in its own zone.

Answers that are the same everywhere, such as policies and service descriptions, belong in one Knowledge Base shared by every branch. Prices or services that differ by site belong in the lookup. The same lookup can also return a different voice or language model for one call, for example a regional voice for branches in Quebec.
A brand-wide number: ask for the ZIP code
A toll-free or brand-wide number tells you nothing about the branch, so the agent asks. The prompt has it ask for the caller's ZIP code or town, confirm it, and then call an HTTP API tool that sends it to your branch locator and gets back the nearest open branch.
That tool has a 5-second default timeout and no automatic retry, so an error goes straight back to the agent. Write the prompt to offer a callback if the locator fails. A Custom Message, such as "One moment while I find your nearest branch", can play once before the request runs, so the caller is not left in silence. The branch the agent picks lands in the call's gathered values, ready for the transfer step.
Transfer to the right branch's staff
Transfer Call hands the caller to a person, and its destination can come from the branch data in three ways. A template such as {{initial_context.transfer_destination}} dials the staff line the lookup returned. Context Mapping picks a number from rules on a field such as location_id, top to bottom, with the first match winning. A field without a prefix reads the value gathered during the call first, then the one loaded at the start, so a branch chosen from a ZIP code beats the dialed one.
Add a fallback destination to every Context Mapping, such as a regional desk. With no fallback and no matching rule, the transfer fails before dialing and the agent carries on with the caller. A Dynamic HTTP Resolver goes further and asks your own system which branch is open and free right now. Our post on human handoff routing shows the exact JSON the resolver must return.
Dograh calls the branch's staff line first while the caller listens to hold music, and joins the two once a person picks up. The Transfer Call timeout is 30 seconds by default, settable from 5 to 120. No answer, a busy line or a failed call all come back to the agent as transfer_failed, and the agent stays with the caller, so the prompt should then take a callback request. Transfer Call works on Twilio, Telnyx, Plivo, Cloudonix and Asterisk ARI. For locations on Exotel numbers, a caller who wants a person gets a callback request booked by the agent.
Join the Dograh Community
Dograh is an OSS alternative to Vapi. Join our Slack community for queries, releases, best practices & community interactions.
Test every location before go-live
You can test each branch from the browser before any number is live. In the agent editor's Variables tab, set called_number to a branch's number, start a test call, and the lookup runs as if that branch rang. Change the number and repeat for each time zone you cover. Transfers are the exception, since browser test calls cannot transfer, so test those with a real phone call to each branch's number.
All 50 branches on one agent share one concurrency limit. Out of the box, a self-hosted install takes 10 simultaneous calls and you can lift that, while pay as you go on Cloud includes 10, with more on request. Self-hosted Dograh has no per-minute platform fee, but servers, model keys and phone minutes still cost money. Dograh Cloud charges a 1¢-a-minute platform fee.
Once real calls flow, each call's trace shows what the agent heard and said, which makes a wrong branch answer easy to trace back to the lookup. Set the built-in QA step to sample a share of calls and read each branch's low scores first. Adding the branch numbers themselves is covered in local phone numbers for every franchise location.
Glossary
- Called number
- The number the caller dialed, saved as called_number and sent to Pre-Call Data Fetch as to_number. With one number per branch, it identifies the branch.
- Initial context
- The values a call starts with, including those returned by Pre-Call Data Fetch, such as a branch's hours and staff line. Prompts read them as variables.
- Context Mapping
- A Transfer Call destination source that picks a number from ordered rules on a call value, such as location_id. The first match wins, and an optional fallback covers no match.
- Brand-wide number
- One number for the whole network, such as a toll-free line on the website. It carries no branch, so the agent asks for a ZIP code or town.

