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.

Plivo

This recipe connects a Plivo number to Inkibra through a server you operate. Your server owns the Plivo webhooks and media WebSocket, creates one Inkibra session per call, and translates between Plivo PCMU and Inkibra PCM16.
Read Server media first, then initialize the server application:
bash
npx @inkibra/voice-cli init

Prerequisites

  • A voice-capable Plivo number and Application
  • PLIVO_AUTH_ID and PLIVO_AUTH_TOKEN stored on your server
  • INKIBRA_API_KEY stored on your server
  • Public https:// and wss:// routes

1. Configure the Plivo Application

Set the Application's Answer URL to POST https://voice.example.com/plivo/answer and its Hangup URL to POST https://voice.example.com/plivo/status. Assign the number to that Application.
Validate each HTTP callback with Plivo's V3 SDK helper before doing any work. Validation uses the exact public URL, request parameters, X-Plivo-Signature-V3, and X-Plivo-Signature-V3-Nonce. Also handle X-Plivo-Signature-Ma-V3 when the account has multiple auth tokens.

2. Return the stream XML

Create a random, single-use nonce, store it with the expected CallUUID, and return:
xml
<?xml version="1.0" encoding="UTF-8"?> <Response> <Stream keepCallAlive="true" bidirectional="true" contentType="audio/x-mulaw;rate=8000" statusCallbackUrl="https://voice.example.com/plivo/status"> wss://voice.example.com/plivo/media/NONCE </Stream> </Response>
Claim the nonce once when Plivo opens /plivo/media/:nonce, then confirm that the WebSocket start event contains the expected call identity.

3. Open Inkibra server media

When Plivo sends start, pass the connected media socket to your Plivo 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 PlivoAudioTransport(plivoSocket), 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 Plivo media event:
  1. Base64-decode media.payload as PCMU at 8 kHz.
  2. Decode mu-law to signed PCM16.
  3. Resample 8 kHz to 24 kHz with a stateful resampler.
  4. 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, then send:
ts
plivo.send(JSON.stringify({ event: "playAudio", media: { contentType: "audio/x-mulaw", sampleRate: 8000, payload: pcmuAudio.toString("base64"), }, }));
Track queued audio by Inkibra generationId. After the SDK's done command, call handlers.receipt with event: "started" when playout begins and event: "completed" when your 24 kHz playout clock reaches the end. On cancel, discard unsent chunks, send Plivo clearAudio, and return a cancelled receipt with 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 Plivo stop, the Hangup callback, or either WebSocket closing:
ts
session.stop(); await session.closed;
Make callbacks idempotent by CallUUID. 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, clear queued Plivo audio and end the call.
For outbound calling, create the Plivo call with the same Answer URL. Use a customer-specific Plivo subaccount when your application controls calls or numbers.

Smoke test

  1. Call the Plivo number and confirm exactly one POST /v1/sessions request.
  2. Speak for five seconds; verify that each 160-byte PCMU/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 clearAudio, a cancelled receipt, and no stale audio after the interruption.
  5. Hang up; verify session.stop, both sockets closed, and the nonce/call record removed.
  6. Replay a signed callback and reuse the nonce; both must be rejected or handled without creating another session.