1npx @inkibra/voice-cli init
INKIBRA_API_KEY stored on your serverhttps:// and wss:// routesPOST https://voice.example.com/whatsapp/answer.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.nonce bound to the expected CallSid, then return:12345678910<?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>
nonce from start.customParameters, claim it once, and verify the received CallSid.start, pass the connected WhatsApp Media Stream to your Twilio audio transport and connect Inkibra:123456789import 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.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.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:1234567891011twilio.send(JSON.stringify({ event: "media", streamSid, media: { payload: pcmuAudio.toString("base64") }, })); twilio.send(JSON.stringify({ event: "mark", streamSid, mark: { name: generationId }, }));
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.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.123456curl --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"
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.POST /v1/sessions request and continuous 480-sample Inkibra input frames while speaking.release.clear, a cancelled receipt, and no stale audio after echoed marks.session.stop, both sockets closed, and the call record removed.