Skip to main content
Asterisk is the world’s most popular open-source telephony platform. Rapida integrates with Asterisk using two transport methods — AudioSocket (native TCP protocol) and WebSocket (chan_websocket) — enabling real-time bidirectional audio streaming for AI-powered voice conversations.
Both transports follow the same two-phase flow: an HTTP webhook registers the call, then a media connection (TCP or WebSocket) carries the audio. Choose the transport that fits your Asterisk version and network topology.

Transport Comparison

FeatureAudioSocket (Native)WebSocket
Asterisk Moduleres_audiosocketchan_websocket
ProtocolRaw TCPWebSocket (wss://)
Port4573443 (standard HTTPS)
Audio CodecSLIN 16-bit 8kHzμ-law 8kHz
FirewallRequires TCP 4573 openUses standard HTTPS port
TLSNot built-inNative TLS via wss://
Asterisk Version16+20+
Best ForLAN / private networkCloud / NAT traversal

Architecture Overview

Both transports share the same backend pipeline. The only difference is the Streamer layer that bridges the media connection to the AI pipeline.

Two-Phase Call Setup

Every Asterisk call (inbound or outbound, AudioSocket or WebSocket) follows this pattern:
  1. Phase 1 — Webhook: An HTTP request registers the call and gets a contextId
  2. Phase 2 — Media: A transport connection (TCP or WebSocket) streams audio using that contextId
This design decouples call setup from media transport, allowing the same webhook to serve both AudioSocket and WebSocket.

Prerequisites

Asterisk Server

Asterisk 16+ (AudioSocket) or 20+ (WebSocket) with the required module loaded

Rapida Account

Active Rapida account with an API key (rpd-xxx)

Voice Assistant

A configured Rapida voice assistant with phone deployment enabled

Network Access

TCP 4573 (AudioSocket) or HTTPS 443 (WebSocket) to Rapida
For outbound calls, you also need Asterisk ARI enabled with credentials stored in a Rapida vault credential.

Quick Start — Inbound Calls

Choose your transport and follow the matching setup:

1. Verify Module

asterisk -rx "module show like audiosocket"
If not loaded:
# /etc/asterisk/modules.conf
[modules]
load = res_audiosocket.so

2. Configure Dialplan

# /etc/asterisk/extensions.conf

[globals]
RAPIDA_API_KEY = rpd-xxx-your-api-key
RAPIDA_HOST    = api.rapida.ai
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: Call the webhook to get 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: Connect AudioSocket with contextId as UUID
 same => n,AudioSocket(${CTX},${RAPIDA_HOST}:${RAPIDA_PORT})
 same => n,Hangup()
 same => n(error),Playback(an-error-has-occurred)
 same => n,Hangup()

3. Reload

asterisk -rx "dialplan reload"

Inbound Call Flow — Detailed

Sequence Diagram

What Happens at Each Step

StepComponentAction
1Asterisk dialplanAnswers call, sets SLIN audio format
2CURL() functionHTTP GET to Rapida webhook — creates conversation, saves CallContext to Redis
3Webhook responseReturns contextId as plain text (UUID)
4aAudioSocket() appOpens TCP to Rapida:4573, sends contextId as UUID frame
4bWebSocket() appOpens WSS to wss://host/v1/talk/asterisk/ctx/{contextId}
5Rapida transportAtomically reads and deletes CallContext from Redis (prevents replay)
6AI pipelineSTT → LLM → TTS loop until hangup

Outbound Call Flow — Detailed

Outbound calls are initiated via the Rapida SDK (gRPC CreatePhoneCall RPC) or REST API. Rapida uses Asterisk’s ARI (Asterisk REST Interface) to originate the call.

Prerequisites for Outbound

1

Enable ARI on Asterisk

# /etc/asterisk/ari.conf
[general]
enabled = yes
pretty = yes

[rapida]
type = user
read_only = no
password = your-ari-password
# /etc/asterisk/http.conf
[general]
enabled = yes
bindaddr = 0.0.0.0
bindport = 8088
Reload:
asterisk -rx "module reload res_ari.so"
asterisk -rx "module reload res_http_websocket.so"
2

Create Vault Credential in Rapida

In the Rapida dashboard, go to Credentials → Create Credential and select Asterisk. Provide:
FieldRequiredExampleDescription
ari_urlYeshttp://asterisk.local:8088Base URL of Asterisk ARI
ari_userYesrapidaARI username
ari_passwordYesyour-ari-passwordARI password
endpoint_technologyNoPJSIPSIP technology (default: PJSIP)
trunkNomy-sip-trunkSIP trunk name for routing
3

Configure Outbound Dialplan

ARI can originate calls in two modes. Choose one:
4

Assign Credential to Phone Deployment

In the assistant’s Phone Deployment, select the Asterisk vault credential and configure:
OptionValueDescription
Telephony ProviderasteriskSelects Asterisk integration
Credential(your vault credential)ARI connection details
Phone Number+15559876543Caller ID for outbound
Context(optional)Dialplan context for outbound
Extension(optional)Dialplan extension (default: s)
App(optional)Stasis app name (default: rapida)

Outbound Sequence Diagram

SDK Code Examples (Outbound)

from rapida import Rapida

client = Rapida(api_key="rpd-xxx-your-key")

call = client.calls.create(
    assistant_id=123456789,
    to_number="+15551234567",
    from_number="+15559876543",  # optional, uses deployment default
    metadata={"campaign": "follow-up"},
)

print(f"Call queued: conversation_id={call.conversation.id}")

AudioSocket Protocol Reference

Frame Format

Each frame has a 3-byte header followed by a variable-length payload:
+----------+------------------+----------------------+
| Type     | Length            | Payload              |
| 1 byte   | 2 bytes (BE)     | 0-65535 bytes        |
+----------+------------------+----------------------+

Frame Types

TypeHexDirectionDescription
Hangup0x00BothCall termination
UUID0x01Asterisk → RapidaContext ID (sent as first frame)
Silence0x02Asterisk → RapidaSilence indicator
Audio0x10BothAudio data (SLIN 16-bit)
Error0xFFBothError condition

Audio Format

PropertyAudioSocket (SLIN)WebSocket (μ-law)Rapida Internal
EncodingSigned Linear 16-bitμ-law 8-bitLinear PCM 16-bit
Sample Rate8000 Hz8000 Hz16000 Hz
ChannelsMonoMonoMono
Rapida handles all resampling and codec conversion transparently.

Status Callbacks

Rapida sends status updates to Asterisk via ARI events. You can also configure a webhook on the assistant to receive call lifecycle events.

Event Callback URL

POST https://{RAPIDA_HOST}/v1/talk/asterisk/ctx/{contextId}/event
This endpoint is automatically configured during outbound call setup. For inbound calls, Asterisk can optionally POST events to this URL.

Event Types

EventTrigger
webhookInitial call registration
channel_createdARI channel created (outbound)
call.connectedMedia connection established
call.endedCall terminated normally
call.failedError during call

Credential Configuration

Vault Credential Fields

When creating an Asterisk credential in the Rapida vault, provide:
{
  "ari_url": "http://your-asterisk-server:8088",
  "ari_user": "rapida",
  "ari_password": "your-ari-password",
  "endpoint_technology": "PJSIP",
  "trunk": "my-sip-trunk"
}
FieldRequiredDefaultDescription
ari_urlYes (outbound only)Base URL of Asterisk ARI HTTP server
ari_userYes (outbound only)ARI username
ari_passwordYes (outbound only)ARI password
endpoint_technologyNoPJSIPChannel technology: PJSIP, SIP, IAX2, DAHDI
trunkNoSIP trunk for routing: builds PJSIP/trunk/number
Vault credentials are encrypted at rest. The ARI password is never exposed in API responses or logs.

Phone Deployment Options

These options are configured on the assistant’s phone deployment:
OptionDefaultDescription
phoneDefault caller ID for outbound calls
context(none)Asterisk dialplan context for outbound
extensionsDialplan extension
apprapidaStasis application name
endpoint_technologyPJSIPOverrides vault credential
trunkOverrides vault credential

Full Configuration Examples

Example 1: Inbound Only (AudioSocket)

Simplest setup — no ARI needed, no vault credential required for inbound-only.
# /etc/asterisk/extensions.conf
[globals]
RAPIDA_API_KEY = rpd-xxx-your-api-key
RAPIDA_HOST    = api.rapida.ai
RAPIDA_PORT    = 4573
ASSISTANT_ID   = 123456789

[from-pstn]
; Route all inbound calls to Rapida AI
exten => _X.,1,Answer()
 same => n,Set(CHANNEL(audioreadformat)=slin)
 same => n,Set(CHANNEL(audiowriteformat)=slin)
 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)
 same => n,AudioSocket(${CTX},${RAPIDA_HOST}:${RAPIDA_PORT})
 same => n,Hangup()
 same => n(error),Playback(an-error-has-occurred)
 same => n,Hangup()

Example 2: Inbound Only (WebSocket)

No firewall port needed beyond HTTPS — ideal for cloud-hosted Asterisk.
# /etc/asterisk/extensions.conf
[globals]
RAPIDA_API_KEY = rpd-xxx-your-api-key
RAPIDA_HOST    = api.rapida.ai
ASSISTANT_ID   = 123456789

[from-pstn-ws]
exten => _X.,1,Answer()
 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)
 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()

Example 3: Inbound + Outbound (Full Setup)

Complete setup with ARI for outbound calls and AudioSocket for media.
[globals]
RAPIDA_API_KEY = rpd-xxx-your-api-key
RAPIDA_HOST    = api.rapida.ai
RAPIDA_PORT    = 4573
ASSISTANT_ID   = 123456789

; ---- Inbound: PSTN → Rapida AI ----
[rapida-inbound]
exten => _X.,1,Answer()
 same => n,Set(CHANNEL(audioreadformat)=slin)
 same => n,Set(CHANNEL(audiowriteformat)=slin)
 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)
 same => n,AudioSocket(${CTX},${RAPIDA_HOST}:${RAPIDA_PORT})
 same => n,Hangup()
 same => n(error),Playback(an-error-has-occurred)
 same => n,Hangup()

; ---- Outbound: Rapida ARI → Customer ----
; ARI originates into this context; RAPIDA_CONTEXT_ID is a channel variable
[rapida-outbound]
exten => s,1,Set(CHANNEL(audioreadformat)=slin)
 same => n,Set(CHANNEL(audiowriteformat)=slin)
 same => n,AudioSocket(${RAPIDA_CONTEXT_ID},${RAPIDA_HOST}:${RAPIDA_PORT})
 same => n,Hangup()

Example 4: WebSocket-Only (Inbound + Outbound)

Zero custom ports — everything runs over HTTPS.
[globals]
RAPIDA_API_KEY = rpd-xxx-your-api-key
RAPIDA_HOST    = api.rapida.ai
ASSISTANT_ID   = 123456789

; ---- Inbound ----
[rapida-inbound-ws]
exten => _X.,1,Answer()
 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)
 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()

; ---- Outbound (ARI routes here with RAPIDA_CONTEXT_ID) ----
[rapida-outbound-ws]
exten => s,1,WebSocket(wss://${RAPIDA_HOST}/v1/talk/asterisk/ctx/${RAPIDA_CONTEXT_ID})
 same => n,Hangup()

Call Lifecycle

State Machine

Captured Metrics

Each call automatically records:
MetricDescription
call.durationTotal call time (seconds)
call.talk_durationActive speech time
call.statuscompleted, failed, no_answer
call.disconnected_byuser, assistant, system

Troubleshooting

Symptoms: AudioSocket() returns immediately, no audioChecklist:
  • Verify Rapida host is reachable: nc -zv api.rapida.ai 4573
  • Check firewall allows TCP 4573 outbound
  • Ensure res_audiosocket.so is loaded
  • Verify the contextId (token) is non-empty in the dialplan
Symptoms: WebSocket() fails, no audioChecklist:
  • Verify WSS URL is correct: wss://api.rapida.ai/v1/talk/asterisk/ctx/{contextId}
  • Ensure chan_websocket.so is loaded
  • Check TLS certificate trust on the Asterisk server
  • Verify Asterisk version is 20+ for chan_websocket
Symptoms: Transport connects but immediately drops with “call context not found”Checklist:
  • Context ID expires after 5 minutes — ensure fast connection after webhook
  • Each contextId is single-use (atomically deleted on first connection)
  • Don’t reuse contextIds across calls
  • Verify the webhook returned a valid contextId (not an error message)
Symptoms: AI speaks but doesn’t hear, or vice versaChecklist:
  • Set CHANNEL(audioreadformat)=slin and CHANNEL(audiowriteformat)=slin
  • Verify res_audiosocket module is loaded
  • Check Asterisk timing: asterisk -rx "timing test"
Symptoms: Audio artifacts, long pausesChecklist:
  • Check network latency to Rapida: ping api.rapida.ai (target < 100ms)
  • Verify no packet loss on the path
  • For WebSocket: ensure no proxy is buffering WebSocket frames
  • Reduce network hops if possible
Symptoms: SDK call returns error, no ring on customer phoneChecklist:
  • Verify ARI is enabled: asterisk -rx "http show status"
  • Check ARI credentials in vault: ari_url, ari_user, ari_password
  • Verify endpoint format: PJSIP/trunk/number or PJSIP/number
  • Check ARI URL is reachable from Rapida: must not be behind NAT without port forwarding
  • Review Asterisk CLI: asterisk -rvvv for ARI request logs
Symptoms: Outbound call connects but AudioSocket/WebSocket has no contextChecklist:
  • Verify ARI channel variables are configured: {"variables": {"RAPIDA_CONTEXT_ID": "..."}}
  • Check the dialplan references ${RAPIDA_CONTEXT_ID} (exact case)
  • Verify deployment options include context and extension

Debug Commands

# Check modules
asterisk -rx "module show like audiosocket"
asterisk -rx "module show like chan_websocket"

# Enable verbose debugging
asterisk -rx "core set debug 5"

# Monitor active channels
asterisk -rx "core show channels verbose"

# Check ARI status
asterisk -rx "http show status"

# View dialplan
asterisk -rx "dialplan show rapida-inbound"

# Test network connectivity
nc -zv api.rapida.ai 4573   # AudioSocket
curl -I https://api.rapida.ai  # HTTPS/WebSocket

High Availability

Multi-Node Deployment

Dialplan with failover (AudioSocket):
[rapida-inbound-ha]
exten => _X.,1,Answer()
 same => n,Set(CHANNEL(audioreadformat)=slin)
 same => n,Set(CHANNEL(audiowriteformat)=slin)
 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)
 same => n,AudioSocket(${CTX},rapida-primary:4573)
 same => n,GotoIf($["${AUDIOSOCKET_STATUS}" = "FAILED"]?failover)
 same => n,Hangup()
 same => n(failover),AudioSocket(${CTX},rapida-secondary:4573)
 same => n,Hangup()
 same => n(error),Playback(an-error-has-occurred)
 same => n,Hangup()

API Reference

Inbound Webhook

GET /v1/talk/asterisk/call/{assistantId}
ParameterLocationRequiredDescription
assistantIdPathYesRapida assistant ID
fromQueryYesCaller phone number
x-api-keyQuery or HeaderYesAPI key for authentication
channel_idQueryNoAsterisk channel ID
Response: Plain text contextId (UUID)

WebSocket Media Endpoint

GET /v1/talk/asterisk/ctx/{contextId}
Upgrade: websocket
ParameterLocationRequiredDescription
contextIdPathYesContext ID from webhook response

Status Callback

POST /v1/talk/asterisk/ctx/{contextId}/event
Receives ARI-style JSON events with a type field.