Agent-readable docs index: /docs/llms.txt. Full docs in one file: /docs/llms-full.txt. Download /docs/docs.zip to grep all markdown files locally.

WhatsApp Calling

WhatsApp Business Calling reaches your server through Twilio Programmable Voice. The live-media bridge is the same bidirectional Twilio Media Stream used for telephone calls; only sender activation and outbound permission differ.
Read Server media first, then initialize the server application:
bash
npx @inkibra/voice-cli init

Prerequisites

  • An approved WhatsApp sender in Twilio with WhatsApp messaging enabled
  • Meta Business Verification and the messaging limit required to save Voice Endpoint Configuration
  • The Twilio Account SID and Auth Token for request validation
  • A restricted Twilio API key as well if your server creates outbound calls
  • INKIBRA_API_KEY stored on your server
  • Public https:// and wss:// routes

1. Activate the sender

In Develop → TwiML Apps, create an app whose Voice Request URL is POST https://voice.example.com/whatsapp/answer.
Open the WhatsApp sender, find Voice Endpoint Configuration, select that TwiML Application under Connect to a TwiML Application, and save. In Meta WhatsApp Manager, enable the calling icon and configure business hours if the sender should accept user-initiated calls.
Validate X-Twilio-Signature on the Voice webhook, status callback, and WebSocket upgrade using Twilio's SDK helper, the exact public URL, and the original request parameters.

2. Return TwiML

Create a random, single-use nonce bound to the expected CallSid, then return:
xml
<?xml version="1.0" encoding="UTF-8"?> <Response> <Connect> <Stream url="wss://voice.example.com/whatsapp/media" statusCallback="https://voice.example.com/whatsapp/status"> <Parameter name="nonce" value="NONCE" /> </Stream> </Connect> </Response>
Twilio does not allow query parameters in a Stream URL. Read nonce from start.customParameters, claim it once, and verify the received CallSid.

3. Open Inkibra server media

When Twilio sends start, pass the connected WhatsApp Media Stream to your Twilio audio transport and connect Inkibra:
ts
import LiveVoice from "@inkibra/voice-sdk/server"; const session = await LiveVoice.connect({ apiKey: process.env.INKIBRA_API_KEY!, audio: new TwilioAudioTransport(twilioSocket, streamSid), prompt: "Help the WhatsApp caller with their order.", conversation: { starts: "assistant" }, tools: {}, });
LiveVoice.connect creates, prepares, and starts the Inkibra session. WhatsApp Calling uses the same Twilio transport class as a telephone call.

4. Bridge audio and playback state

For each Twilio media event, base64-decode media.payload as audio/x-mulaw, 8 kHz mono. Decode mu-law to signed PCM16, resample to 24 kHz with persistent state, and send raw little-endian PCM16 to Inkibra in 480-sample, 20 ms binary frames.
For each SDK enqueue command, command.pcm is PCM16LE mono at 24 kHz. Keep mode: "buffer" chunks private until the matching release. Downsample released audio to 8 kHz, mu-law encode, and send audio followed by a mark:
ts
twilio.send(JSON.stringify({ event: "media", streamSid, media: { payload: pcmuAudio.toString("base64") }, })); twilio.send(JSON.stringify({ event: "mark", streamSid, mark: { name: generationId }, }));
When the SDK sends done, send one mark after the generation's final media chunk. Call handlers.receipt with started when playout begins and completed when Twilio echoes that mark. On cancel, discard unsent chunks and send {"event":"clear","streamSid":"..."}. Twilio echoes outstanding marks after clear; classify those generations as cancelled and report the number of 24 kHz samples actually played. Honor block-foreground, release, and both playback lanes as described in Server media.

5. End the session

On Twilio stop, the completed-call callback, or either WebSocket closing, send Inkibra session.stop, close both sockets, and remove the nonce/call record. Make callbacks idempotent by CallSid. If Inkibra sends session.limit, a provider error, or closes the WebSocket, clear queued audio and end the Twilio call.

Outbound WhatsApp calls

First obtain the consumer's WhatsApp call permission and persist its status and expiry. Then create the call with WhatsApp addresses and the same answer route:
bash
curl --request POST \ "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Calls.json" \ --user "$TWILIO_API_KEY:$TWILIO_API_SECRET" \ --data-urlencode "From=whatsapp:+15551230000" \ --data-urlencode "To=whatsapp:+15551239999" \ --data-urlencode "Url=https://voice.example.com/whatsapp/answer"
The From sender must be registered and activated for Voice Calling on that Twilio account. Do not attempt to connect a WhatsApp call to a PSTN endpoint; Twilio rejects that bridge. Check Twilio's current sender-country availability before enabling outbound calling.

Smoke test

  1. From a WhatsApp consumer app, call the activated sender and confirm the TwiML Application receives the Voice webhook.
  2. Confirm exactly one POST /v1/sessions request and continuous 480-sample Inkibra input frames while speaking.
  3. Confirm the assistant opening is audible and buffered audio waits for release.
  4. Interrupt the assistant; verify Twilio clear, a cancelled receipt, and no stale audio after echoed marks.
  5. Hang up; verify session.stop, both sockets closed, and the call record removed.
  6. For outbound, verify denied or expired permission blocks the API call; valid permission reaches the WhatsApp app and uses the same media bridge.
  7. Send a request with a bad Twilio signature and reuse the nonce; both must be rejected.