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.

Twilio

This recipe keeps the number in the customer's Twilio account. Your server answers the Voice webhook, owns the bidirectional Media Stream, creates one Inkibra session per call, and translates between Twilio mu-law and Inkibra PCM16.
Read Server media first, then initialize the server application:
bash
npx @inkibra/voice-cli init

Prerequisites

  • A voice-capable Twilio number
  • Its Account SID and Auth Token for request validation
  • A restricted API key as well if your server creates outbound calls
  • INKIBRA_API_KEY stored on your server
  • Public https:// and wss:// routes

1. Configure the number

In Phone Numbers → Manage → Active numbers, open the number. Under A call comes in, choose Webhook, set POST, and enter https://voice.example.com/twilio/answer. Configure a call-status callback at https://voice.example.com/twilio/status.
Validate X-Twilio-Signature before processing the Voice webhook or status callback. Use Twilio's SDK helper, the exact externally visible URL, and the original form parameters. Validate the WebSocket upgrade with the same Auth Token and exact wss:// URL.

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/twilio/media" statusCallback="https://voice.example.com/twilio/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. A bidirectional <Connect><Stream> delivers the caller's inbound track and accepts audio back over the same socket.

3. Open Inkibra server media

When Twilio sends start, pass the connected 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: "Answer questions about the customer's order.", conversation: { starts: "assistant" }, tools: {}, });
LiveVoice.connect creates, prepares, and starts the Inkibra session. The transport class implements the provider-specific audio mapping in the next step.

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:
ts
twilio.send(JSON.stringify({ event: "clear", streamSid }));
Twilio echoes outstanding marks after clear; classify those generations as cancelled, not completed. Report the number of 24 kHz samples actually played. Honor block-foreground, release, and reaction-versus-foreground lanes exactly as described in Server media.

5. End the session

On Twilio stop, the completed-call callback, or either WebSocket closing:
ts
session.stop(); await session.closed;
Make callbacks idempotent by CallSid. Treat a media disconnect as terminal until your application implements explicit session resumption. If Inkibra sends session.limit, a provider error, or closes the WebSocket, send clear, then end or update the Twilio call.
For outbound calls, create the call with url: "https://voice.example.com/twilio/answer" and a status callback. Use a dedicated subaccount and restricted API key when your application controls the customer's calls. The subaccount Auth Token is still required to validate Twilio signatures.
This recipe connects a customer-owned Twilio number. Twilio BYOC is a separate SIP product for bringing an external carrier into Twilio.

Smoke test

  1. Call the Twilio number and confirm exactly one POST /v1/sessions request.
  2. Speak for five seconds; verify that each 160-byte mu-law/20 ms input chunk becomes one 480-sample Inkibra frame.
  3. Confirm the assistant opening is audible and that buffered audio is not played before release.
  4. Interrupt the assistant; verify clear, a cancelled receipt, and no stale audio after the echoed marks.
  5. Hang up; verify session.stop, both sockets closed, and the nonce/call record removed.
  6. Send a request with a changed body or bad signature and reuse the nonce; both must be rejected.