Skip to main content

Exotel Integration

Exotel places and receives calls with bidirectional AgentStream media streaming into Dograh agents.

Walkthrough video

The walkthrough covers, in order:
  1. ExoPhone provisioning
  2. Dograh telephony configuration
  3. Outbound call from a workflow
  4. Inbound call landing on a workflow

Prerequisites

  • An Exotel account with AgentStream / Connect Voice AI enabled
  • KYC verification completed — mandatory for all Indian accounts post-signup before ExoPhones can carry live voice traffic. Follow KYC Verification — Indian Accounts (typically < 30 minutes online).
  • Account SID, API Key, and API Token (Dashboard -> Settings -> API Settings)
  • A Dograh instance reachable over HTTPS and WSS
  • At least one ExoPhone assigned to your account (see provisioning below — don’t assume one already exists)

Supported Exotel front-doors

Per AgentStream: what to use when, Exotel exposes five entry points to AgentStream (Connect Voice AI, Connect + Flow, ExoML outbound, Flow-Based inbound, ExoML inbound). Dograh’s Exotel provider supports exactly two of these today:
  • Outbound: Connect Voice AI APIPOST /v1/Accounts/{sid}/Calls/connect.json with StreamUrl in the API body. No App Bazaar flow involved.
  • Inbound: Flow-Based via Voicebot Applet — ExoPhone → App Bazaar flow → Voicebot Applet with an HTTPS dynamic-URL pointing at Dograh.
Together these cover the “answer → bot” use case without requiring a gRPC control plane.

Not currently supported

  • Connect Voice AI with Flow API (outbound + App Bazaar journey). Dograh’s initiate_call has no path for dialing with Url=<app_bazaar_flow_url>, and transfer_call raises NotImplementedError, so the Connect-applet agent-handoff pattern isn’t wired. Operators who need IVR/greeting/DTMF/agent-handoff journeys can trigger Connect + Flow calls directly against Exotel’s API from outside Dograh; the flow’s Voicebot Applet can still terminate into Dograh’s inbound dynamic-URL endpoint.
  • ExoML / Programmable Voice APIs (gRPC leg events + leg actions), for both outbound and inbound. Would require a gRPC endpoint on Dograh’s side and code-owned per-leg control, which is not implemented today.

ExoPhone provisioning and activation

Provision ExoPhones before configuring Dograh. All calls use HTTP Basic Auth with your API Key / API Token.
  1. List available numbers
    • GET /v2_beta/Accounts/{account_sid}/AvailablePhoneNumbers/{country}/{type}
    • {type} is one of Landline, Mobile, or TollFree.
    • Optional filters: IncomingSMS, InRegion, Contains.
    • Response fields include phone_number, capabilities, country, region, rental_price.
    • Docs: Available numbers
  2. Purchase a number
    • POST /v2_beta/Accounts/{account_sid}/IncomingPhoneNumbers
    • Request body: PhoneNumber (required); VoiceUrl, SMSUrl, FriendlyName (all optional).
    • Response fields include sid, phone_number, friendly_name, capabilities, country, rental_price, currency.
    • Docs: Purchase number
  3. Verify a purchased number
    • GET /v2_beta/Accounts/{account_sid}/IncomingPhoneNumbers/{exophone_sid}
    • Returns sid, phone_number, friendly_name, capabilities, country, region, voice_url, sms_url.
    • Docs: Number details
  4. Assign the number to a Flow
    • In the Exotel dashboard, attach the ExoPhone’s Voice URL to an App Bazaar flow (see Inbound below for the Voicebot Applet wiring).
    • Required before the number can carry inbound traffic, and part of the setup the reviewer’s checklist expects.
KYC (mandatory for Indian accounts): Complete KYC in the Exotel dashboard before expecting purchased numbers to become fully active for voice traffic. A purchased ExoPhone can appear in the API but silently fail on real calls until KYC clears. See KYC Verification — Indian Accounts for the three-step flow (PAN confirmation → business details → authorised signatory) and the “help me verify” escape hatch if automated checks fail.

Dograh telephony configuration

  1. Navigate to /telephony-configurations and click Add configuration
  2. Select Exotel
  3. Enter:
    • Account SID
    • API Key / API Token (HTTP Basic auth for Exotel APIs)
    • API Base URL — default https://api.in.exotel.com (India); use https://api.exotel.com for other regions
  4. Save, then add your ExoPhone under Phone numbers in E.164 format (e.g. +9180XXXXXXXX)
  5. Set an Inbound workflow on that phone number in Dograh.

Programmatic configuration

When wiring Exotel via API or environment rather than the UI, the provider config accepts:

Outbound

Dograh dials using Exotel Connect Voice AI (the “simplest outbound” pattern — bot is the whole experience, no App Bazaar flow): POST {api_base}/v1/Accounts/{AccountSid}/Calls/connect.json Request fields:
  • From — the recipient (callee)
  • CallerId — the ExoPhone
  • StreamUrl — Dograh’s tokenized WSS endpoint (fixed per call, minted by ws_auth.build_media_ws_url)
  • StreamType=bidirectional
  • StatusCallback and StatusCallbackEvents[]=terminalonly attached when a workflow_run_id is present
For Indian ExoPhones, Dograh’s _exotel_dial_number() helper maps the stored E.164 CallerId to Exotel’s 0-prefixed national form (Exotel’s Connect Voice AI example uses CallerId=0XXXXXXXXXX). Example: +91731485233807314852338. The destination From stays in E.164 per the same example (From=+91XXXXXXXXXX). Non-India numbers pass through unchanged. Test from a workflow Call button. Confirm two-way audio and that hangup marks the run completed. Per-call dynamic StreamUrl or dashboard-flow-driven outbound would require Connect + Flow or ExoML respectively — see Supported Exotel front-doors for why those aren’t implemented here.

Inbound

Dograh implements Flow-Based Inbound via the Voicebot Applet’s dynamic-URL contract. Exotel’s Stream and Voicebot Applet reference explicitly documents the applet’s URL parameter as “a WebSocket URL … or an HTTPS endpoint that dynamically returns the WSS URL” — that is the contract Dograh implements.

Wiring

  1. In Exotel, open or create an App Bazaar flow.
  2. Drop a Voicebot Applet into the flow.
  3. Configure the applet:
    • URL = https://<dograh-host>/api/v1/telephony/inbound/run
    • Authentication = HTTP Basic; username = your Exotel API Key, password = your Exotel API Token (Dograh verifies this in verify_inbound_signature).
    • Sample Rate = 8000. Dograh’s Exotel transport is fixed at 8 kHz today; do not append ?sample-rate=… — the applet will negotiate a rate the transport doesn’t decode and audio breaks.
  4. Assign the ExoPhone’s Voice URL to this flow in Exotel.
  5. In Dograh, assign the matching inbound workflow to the number.

Response contract

On invocation, Dograh validates auth and route mapping, creates a workflow run, and returns JSON:
Exotel’s Voicebot Applet then opens the bidirectional WebSocket to that URL.

Voicebot Applet operational limits

Drawn from Exotel’s applet documentation:
  • Audio format: raw/slin — 16-bit PCM little-endian, mono, base64-encoded.
  • Sample rate: 8000 (PSTN). Exotel’s applet also supports 16000 and 24000 via ?sample-rate=…, but Dograh’s Exotel transport is hard-coded to 8 kHz today, so leave the sample rate at the default.
  • Chunk size: min 3.2 KB (100 ms of audio), max 100 KB, must be a multiple of 320 bytes. Smaller chunks risk audio distortion; larger cause timeouts.
  • Custom parameters: up to 3 key-value pairs appended to the URL (?param1=value1&param2=value2, 256 chars total).

Status callbacks

Outbound registers a per-run status callback URL: /api/v1/telephony/exotel/status-callback/{workflow_run_id}?exotel_auth=... The exotel_auth token is an HMAC minted from your API token so Exotel’s POST does not need an Authorization header. The handler also rejects CallSid values that do not match the run’s stored call id. Terminal events update the workflow run status.
Inbound status callbacks: the inbound dynamic-URL path currently returns the WSS URL without registering a per-run StatusCallback on the Exotel side. If you rely on Exotel-driven terminal events to close inbound runs, be aware that this is a known open item on the PR.

Phone ownership validation

Exotel does not publish a purpose-built “verify ownership” endpoint. The closest documented mechanism is Number Details — but it assumes the number is already on the account and only returns its configuration; it does not confirm ownership by itself. Dograh therefore validates ownership as a list + membership check:
  1. Normalize the configured number and generate match-key variants (E.164, national, with/without leading +). For Indian numbers stored as 0-prefixed national forms, an IN country hint is applied.
  2. Query GET /v2_beta/Accounts/{account_sid}/IncomingPhoneNumbers and iterate the returned ExoPhones.
  3. Set-intersect the wanted variants against each entry’s phone_number / PhoneNumber / FriendlyName (also India-normalized).
  4. Raise ProviderPhoneNumberLookupError if no variant matches, with a message pointing the operator back to the Exotel dashboard.
This is the documented workaround given Exotel’s public API surface — there is no dedicated ownership-check endpoint to switch to.

Troubleshooting

  • Connect fails — verify Account SID, API key/token, base URL region (api.in.exotel.com or api.exotel.com only), and AgentStream entitlement.
  • No inbound agent — confirm the ExoPhone has an inbound workflow in Dograh and the Exotel Voicebot Applet points to /api/v1/telephony/inbound/run.
  • Auth failed on inbound — ensure the Voicebot Applet is configured with HTTP Basic (username = API Key, password = API Token) matching the credentials stored in Dograh.
  • ProviderPhoneNumberLookupError at config save — the ExoPhone is not on the Exotel account, the wrong api_base_url region is selected, or the number was purchased but has not cleared KYC yet.
  • One-way audio — confirm public WSS reachability and firewall/IP rules for Exotel egress; also check the sample rate on the applet matches what your bot pipeline expects.
  • Choppy or dropped audio — the outgoing chunk size may be out of range; keep chunks ≥ 3.2 KB and ≤ 100 KB, in multiples of 320 bytes. See Exotel’s WSS errors and handling.
  • Status callback 401 — ensure Dograh minted the StatusCallback URL (includes exotel_auth) and that the query string is preserved by any intervening proxy.

References