1npx @inkibra/voice-cli init
INKIBRA_API_KEY stored on your serverhttps:// and wss:// routesPOST, and enter https://voice.example.com/twilio/answer. Configure a call-status callback at https://voice.example.com/twilio/status.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.nonce bound to the expected CallSid, then return:12345678910<?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>
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.start, pass the connected 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: "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.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:1twilio.send(JSON.stringify({ event: "clear", streamSid }));
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.stop, the completed-call callback, or either WebSocket closing:12session.stop(); await session.closed;
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.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.POST /v1/sessions request.release.clear, a cancelled receipt, and no stale audio after the echoed marks.session.stop, both sockets closed, and the nonce/call record removed.