Skip to main content
This page covers how to configure and run assistant-api with each supported telephony provider. Choose your provider below.
For Twilio, Vonage, and Exotel you need a public HTTPS hostname — see the ngrok guide for local development. For Asterisk and SIP, your server connects out to Rapida, so no public URL is required on your end.

Required Environment Variable

All telephony providers (except SIP) require one shared env var:
# docker/assistant-api/.assistant.env
PUBLIC_ASSISTANT_HOST=your-public-hostname.com   # No https:// prefix
This is the hostname that Twilio, Vonage, and Exotel will call back and stream audio to. For local dev, replace it with your ngrok domain.

Twilio

1

Store vault credentials

In the Rapida dashboard go to Credentials → Create Credential, select provider type Twilio, and enter:
KeyDescription
account_sidYour Twilio Account SID (ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)
account_tokenYour Twilio Auth Token
Credentials are encrypted at rest in the vault.
2

Configure the phone number webhook

In the Twilio ConsolePhone Numbers → Manage → Active numbers, select your number and set:
FieldValue
Voice webhook (HTTP POST)https://{PUBLIC_ASSISTANT_HOST}/v1/talk/twilio/call/{assistantId}
Status callback URLhttps://{PUBLIC_ASSISTANT_HOST}/v1/talk/twilio/ctx/{contextId}/event
Replace {assistantId} with the numeric assistant ID from the Rapida dashboard.
3

What Rapida returns

When Twilio hits the webhook, Rapida responds with TwiML that opens a bidirectional Media Stream:
<Response>
  <Connect>
    <Stream url="wss://{PUBLIC_ASSISTANT_HOST}/v1/talk/twilio/ctx/{contextId}"
            name="{assistantId}__{conversationId}"
            statusCallback="https://{PUBLIC_ASSISTANT_HOST}/v1/talk/twilio/ctx/{contextId}/event"
            statusCallbackEvent="initiated ringing answered completed">
      <Parameter name="assistant_id" value="{assistantId}"/>
      <Parameter name="client_number" value="{callerNumber}"/>
    </Stream>
  </Connect>
</Response>
Audio is μ-law 8kHz, base64-encoded per the Twilio Media Streams spec.
4

Verify

# Confirm assistant-api is reachable and the webhook path responds
curl -s -o /dev/null -w "%{http_code}" \
  "https://{PUBLIC_ASSISTANT_HOST}/v1/talk/twilio/call/{assistantId}"
# Expected: 200 or 422 (not 404/502)
PUBLIC_ASSISTANT_HOST must be HTTPS — Twilio rejects plain HTTP webhooks.

Vonage

1

Store vault credentials

In Credentials → Create Credential, select provider type Vonage:
KeyDescription
api_keyVonage API key
api_secretVonage API secret
application_idVonage Application ID (JWT auth)
private_keyVonage Application private key (PEM, JWT auth)
2

Configure the Vonage application answer URL

In the Vonage DashboardApplications, open your voice application and set:
FieldValue
Answer URL (POST)https://{PUBLIC_ASSISTANT_HOST}/v1/talk/vonage/call/{assistantId}
Event URL (POST)https://{PUBLIC_ASSISTANT_HOST}/v1/talk/vonage/ctx/{contextId}/event
3

What Rapida returns

Rapida responds with an NCCO that connects a WebSocket:
[{
  "action": "connect",
  "endpoint": [{
    "type": "websocket",
    "uri": "wss://{PUBLIC_ASSISTANT_HOST}/v1/talk/vonage/ctx/{contextId}",
    "content-type": "audio/l16;rate=16000"
  }]
}]
Audio is Linear PCM 16kHz — higher quality than Twilio’s μ-law.

Exotel

1

Store vault credentials

In Credentials → Create Credential, select provider type Exotel:
KeyDescription
api_keyExotel API key
api_secretExotel API secret
account_sidExotel Account SID
2

Configure the ExoPhone app

In the Exotel DashboardExoPhones, assign your ExoPhone an App and set the passthru URL to:
https://{PUBLIC_ASSISTANT_HOST}/v1/talk/exotel/call/{assistantId}
Rapida responds with Exotel passthru XML that opens a WebSocket stream.
3

What Rapida returns

Rapida returns passthru XML connecting a WebSocket at:
wss://{PUBLIC_ASSISTANT_HOST}/v1/talk/exotel/ctx/{contextId}
Audio is μ-law 8kHz, same format as Twilio.
Exotel operates in India and South-East Asia. Make sure PUBLIC_ASSISTANT_HOST is reachable from Exotel’s servers (public HTTPS, not localhost).

Asterisk — AudioSocket

AudioSocket uses a native raw TCP protocol. Asterisk connects to Rapida over TCP port 4573 using the contextId as the session identifier. When to use: Asterisk 16+, private/LAN network, lowest latency.
1

Confirm required env vars

# docker/assistant-api/.assistant.env
PUBLIC_ASSISTANT_HOST=your-public-hostname.com
AUDIOSOCKET__HOST=0.0.0.0
AUDIOSOCKET__PORT=4573
The assistant-api binds a TCP AudioSocket server on AUDIOSOCKET__PORT at startup.
2

Verify the Asterisk module is loaded

asterisk -rx "module show like audiosocket"
# res_audiosocket.so — requires Asterisk 16+
If not loaded, add to /etc/asterisk/modules.conf:
[modules]
load = res_audiosocket.so
3

Configure the dialplan

# /etc/asterisk/extensions.conf

[globals]
RAPIDA_API_KEY = rpd-xxx-your-api-key
RAPIDA_HOST    = {PUBLIC_ASSISTANT_HOST}   ; HTTP/webhook host
RAPIDA_AS_HOST = {PUBLIC_ASSISTANT_HOST}   ; AudioSocket TCP host (same server, different protocol)
RAPIDA_PORT    = 4573
ASSISTANT_ID   = 123456789

[rapida-inbound]
exten => _X.,1,Answer()
 same => n,Set(CHANNEL(audioreadformat)=slin)
 same => n,Set(CHANNEL(audiowriteformat)=slin)
 ; Phase 1 — register the call and get a contextId
 same => n,Set(CTX=${CURL(https://${RAPIDA_HOST}/v1/talk/asterisk/call/${ASSISTANT_ID}?from=${CALLERID(num)}&x-api-key=${RAPIDA_API_KEY})})
 same => n,GotoIf($["${CTX}" = ""]?error)
 ; Phase 2 — open AudioSocket TCP using the contextId as UUID
 same => n,AudioSocket(${CTX},${RAPIDA_AS_HOST}:${RAPIDA_PORT})
 same => n,Hangup()
 same => n(error),Playback(an-error-has-occurred)
 same => n,Hangup()
CHANNEL(audioreadformat)=slin and CHANNEL(audiowriteformat)=slin are required — AudioSocket expects signed 16-bit linear PCM.
4

Reload dialplan

asterisk -rx "dialplan reload"
5

Test connectivity

# Verify AudioSocket port is reachable
nc -zv {PUBLIC_ASSISTANT_HOST} 4573

# Verify HTTPS webhook is reachable
curl -I https://{PUBLIC_ASSISTANT_HOST}

Outbound calls via ARI (AudioSocket)

For outbound calls, Rapida uses Asterisk’s ARI to originate the call. The dialplan receives the RAPIDA_CONTEXT_ID channel variable set by ARI:
; /etc/asterisk/extensions.conf
[rapida-outbound]
exten => s,1,Set(CHANNEL(audioreadformat)=slin)
 same => n,Set(CHANNEL(audiowriteformat)=slin)
 ; RAPIDA_CONTEXT_ID is injected as a channel variable by Rapida ARI
 same => n,AudioSocket(${RAPIDA_CONTEXT_ID},${RAPIDA_AS_HOST}:${RAPIDA_PORT})
 same => n,Hangup()
ARI vault credential keys (store in Rapida credentials):
KeyRequiredDescription
ari_urlYesARI base URL, e.g. http://asterisk.local:8088
ari_userYesARI username
ari_passwordYesARI password
endpoint_technologyNoChannel technology — default PJSIP
trunkNoSIP trunk name, builds PJSIP/trunk/number

Asterisk — WebSocket

WebSocket uses chan_websocket over standard HTTPS/WSS. No custom TCP port required. When to use: Asterisk 20+, cloud-hosted Asterisk, NAT traversal scenarios.
1

Confirm required env vars

# docker/assistant-api/.assistant.env
PUBLIC_ASSISTANT_HOST=your-public-hostname.com
No AudioSocket port needed — WebSocket uses the standard HTTPS port.
2

Verify the Asterisk module is loaded

asterisk -rx "module show like chan_websocket"
# chan_websocket.so — requires Asterisk 20+
If not loaded, add to /etc/asterisk/modules.conf:
[modules]
load = chan_websocket.so
3

Configure the dialplan

# /etc/asterisk/extensions.conf

[globals]
RAPIDA_API_KEY = rpd-xxx-your-api-key
RAPIDA_HOST    = {PUBLIC_ASSISTANT_HOST}
ASSISTANT_ID   = 123456789

[rapida-inbound-ws]
exten => _X.,1,Answer()
 ; Phase 1 — register the call and get a contextId
 same => n,Set(CTX=${CURL(https://${RAPIDA_HOST}/v1/talk/asterisk/call/${ASSISTANT_ID}?from=${CALLERID(num)}&x-api-key=${RAPIDA_API_KEY})})
 same => n,GotoIf($["${CTX}" = ""]?error)
 ; Phase 2 — open WSS connection using contextId in the URL path
 same => n,WebSocket(wss://${RAPIDA_HOST}/v1/talk/asterisk/ctx/${CTX})
 same => n,Hangup()
 same => n(error),Playback(an-error-has-occurred)
 same => n,Hangup()
4

Reload dialplan

asterisk -rx "dialplan reload"
5

Test connectivity

# Verify HTTPS/WebSocket host is reachable
curl -I https://{PUBLIC_ASSISTANT_HOST}

Outbound calls via ARI (WebSocket)

; /etc/asterisk/extensions.conf
[rapida-outbound-ws]
exten => s,1,NoOp(Outbound WebSocket call)
 ; RAPIDA_CONTEXT_ID is injected by Rapida ARI
 same => n,WebSocket(wss://${RAPIDA_HOST}/v1/talk/asterisk/ctx/${RAPIDA_CONTEXT_ID})
 same => n,Hangup()
Same ARI vault credentials apply — see the AudioSocket section above.

AudioSocket vs WebSocket — Quick Comparison

AudioSocketWebSocket
Asterisk version16+20+
Moduleres_audiosocketchan_websocket
ProtocolRaw TCP port 4573WSS (HTTPS port)
FirewallTCP 4573 must be openStandard HTTPS only
Audio codecSLIN 16-bit 8kHzμ-law 8kHz
LatencyLowestSlightly higher
TLS built-inNoYes (native WSS)

SIP

The assistant-api runs a built-in SIP server. Any SIP client can call it directly — no external provider account needed.
1

Configure env vars

# docker/assistant-api/.assistant.env
SIP__SERVER=0.0.0.0           # Bind address (listen on all interfaces)
SIP__EXTERNAL_IP=203.0.113.10 # Your PUBLIC IP — advertised in SDP
SIP__PORT=5090                 # SIP signalling port (UDP)
SIP__RTP_PORT_RANGE_START=10000
SIP__RTP_PORT_RANGE_END=20000
SIP__EXTERNAL_IP must be your server’s real public IP. If left as 0.0.0.0, SDP will advertise a non-routable address and audio will not work.
2

Open firewall ports

PortProtocolPurpose
5090UDPSIP signalling
10000–20000UDPRTP media
3

Dial from any SIP client

Use this connection string from any SIP phone, softphone, or PBX:
sip:{assistantID}:{apiKey}@{SIP__EXTERNAL_IP}:{SIP__PORT}
FieldValue
assistantIDNumeric assistant ID from the Rapida dashboard
apiKeyRapida API key scoped to the project (rpd-xxx)
SIP__EXTERNAL_IPPublic IP set in env var
SIP__PORT5090 (default)
4

Connect Asterisk as a SIP trunk (optional)

To route calls from an Asterisk PBX through Rapida over SIP, configure pjsip.conf:
; /etc/asterisk/pjsip.conf
[rapida-trunk]
type=endpoint
context=rapida-outbound
disallow=all
allow=ulaw
aors=rapida-trunk-aor
outbound_auth=rapida-trunk-auth

[rapida-trunk-auth]
type=auth
auth_type=userpass
username={assistantID}
password={apiKey}

[rapida-trunk-aor]
type=aor
contact=sip:{SIP__EXTERNAL_IP}:{SIP__PORT}

[rapida-dialer]
type=identify
endpoint=rapida-trunk
match={SIP__EXTERNAL_IP}
; /etc/asterisk/extensions.conf
[rapida-outbound]
exten => _X.,1,NoOp(Route through Rapida SIP)
 same => n,Dial(PJSIP/${EXTEN}@rapida-trunk)
 same => n,Hangup()