Skip to Content
Rendez-vous.ai Dashboard 1.1.0 is released 🎉
Telephony & FreeSWITCHDialplans & Routing

Dialplans & Routing

In FreeSWITCH, Dialplans are essentially the routing layer of your telephony engine. If you are familiar with web development, you can think of the dialplan as the routes/ directory in an Express.js application.

When a call arrives, FreeSWITCH parses the XML dialplan to match the incoming number (and other conditions) against a list of regular expressions. Once a match is found, it executes a sequence of actions (applications).

All routing logic in rdv.ai-freeswitch is located within the conf/dialplan/ directory.

Contexts: Public vs. Default

FreeSWITCH separates routing into “contexts” to ensure security and logical separation of traffic.

1. The Public Context (conf/dialplan/public/)

This is the most critical context for Rendez-vous.ai. Any call originating from the outside world (e.g., a customer calling from their cell phone via Twilio or VoIP.ms) enters the public context.

  • 00_inbound_did.xml: This file handles the primary incoming routing. “DID” stands for Direct Inward Dialing (the phone number the user dialed).
  • The Handoff: In a traditional PBX, the dialplan might instruct FreeSWITCH to ring a specific desk phone. In our architecture, the dialplan matches the incoming DID and immediately executes a webhook/socket call to the Core API (rdv.ai-api).

2. The Default Context (conf/dialplan/default/)

This context handles internal routing. It is primarily used for:

  • Routing calls between internal SIP extensions (e.g., 1000 calling 1001).
  • Local testing using softphones (Zoiper, Linphone).
  • Outbound dialing rules (if the AI agent needs to initiate a call to the outside world).

How a Call is Handled (The XML Flow)

While the Core API controls the logic of the conversation, the dialplan dictates how the call physically starts and ends.

A simplified version of our inbound routing logic follows this sequence:

  1. Condition Matching: The dialplan checks the destination_number (the number the caller dialed).
<condition field="destination_number" expression="^(.*)$">
  1. Setting Variables: It exports essential variables that the Core API will need, such as the caller’s ID and the unique UUID of the FreeSWITCH channel.
<action application="set" data="api_url=http://${API_HOST}/api/telephony/incoming-call"/>
  1. Triggering the API: Using a module like mod_curl or mod_httapi, FreeSWITCH sends an HTTP request to the Core API, passing along the call metadata.
  2. Executing API Instructions: The API responds with dynamic XML instructions (or connects via Event Socket), telling FreeSWITCH to answer the call and open a WebSocket (uuid_audio or mod_audio_fork) to stream the raw RTP audio back to the Node.js server.

Dynamic Variables (API_HOST)

To keep our Docker containers environment-agnostic, we heavily utilize dynamic variables within the XML dialplans.

When the rdv.ai-freeswitch container boots up, it reads environment variables (like API_HOST) and injects them into conf/vars.xml. This allows the exact same dialplan files to route webhooks to ngrok during local development and to internal Kubernetes services during production without needing code changes.

Last updated on