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.

Photon Voice

Photon Voice exposes call control and a bidirectional media stream over gRPC. Its TypeScript SDK delivers caller audio and accepts audio frames on the same MediaSession, making it a direct server-side transport for Inkibra Live Voice.

Prerequisites

  • A Photon Voice account, voice-capable number, and LightAuth JWT
  • Node.js or Bun; the Photon media client uses gRPC
  • INKIBRA_API_KEY stored on the same server
  • @photon-ai/voice-ts and @inkibra/voice-sdk
bash
npm install @photon-ai/voice-ts @inkibra/voice-sdk npx @inkibra/voice-cli init
Photon also offers iMessage numbers. Do not assume a Photon Voice number is the same iMessage line unless Photon has enabled that arrangement for the customer's account.

1. Create the Photon client

ts
import { AudioCodec, createClient } from "@photon-ai/voice-ts"; const photon = createClient({ token: () => getPhotonVoiceToken(), keepalive: { timeMs: 30_000, timeoutMs: 10_000, permitWithoutCalls: true, }, });
Photon expects the JWT in gRPC metadata. Keep the token resolver server-side and refresh it before expiry.

2. Start or accept a call

For an outbound call:
ts
const call = await photon.calls.dial("+14155551234", { from: process.env.PHOTON_VOICE_NUMBER!, });
Subscribe once for the process and route events by callControlId. Persist each event cursor; after a control-stream reconnect, call fetchMissed(cursor) before resuming the subscription.
ts
for await (const event of photon.calls.subscribe()) { await routePhotonEvent(event); }
Use the corresponding incoming-call event instead of dial for inbound calls. Create exactly one Inkibra session only after the call is answered.

3. Open bidirectional media

ts
import LiveVoice from "@inkibra/voice-sdk/server"; const media = photon.media.openStream({ callControlId: call.callControlId, codec: AudioCodec.pcmu8000, sendInbound: true, }); const live = await LiveVoice.connect({ apiKey: process.env.INKIBRA_API_KEY!, audio: new PhotonAudioTransport(media), prompt: "Help the caller schedule an appointment.", conversation: { starts: "assistant" }, tools: {}, });
PhotonAudioTransport implements the server media interface:
  • On Photon frame, decode PCMU 8 kHz, resample with persistent state, and call handlers.input with PCM16LE mono at 24 kHz.
  • On Inkibra enqueue, retain PCM by generationId. Do not send mode: buffer audio yet.
  • On release, resample the generation to 8 kHz, encode PCMU, and call media.sendFrame in order.
  • On cancel, discard unsent frames and invoke Photon's documented media-interruption operation for audio already submitted to its playout queue.
  • On done, report completed only after the provider has finished playing the final submitted frame.
Do not manufacture a completed or cancelled receipt from bytes submitted to gRPC. A receipt represents audio the caller actually heard. If the Photon SDK version available to the customer cannot confirm or interrupt provider playout, document that limitation and do not claim sample-accurate interruption.

4. Close both sides

Treat call.hangup, a terminal media event, or the media stream closing as the end of the Inkibra session:
ts
live.stop(); await live.closed; await media[Symbol.asyncDispose]();
If Inkibra closes first, stop Photon output and end the provider call. Make teardown idempotent by callControlId. A control-event subscription may reconnect and catch up; a failed per-call media stream is terminal.

Smoke test

  1. Place a call and verify one Photon call.answered event and one Inkibra session.
  2. Speak for five seconds; verify continuous inbound PCMU frames become 24 kHz Inkibra input.
  3. Confirm reaction audio and foreground audio play in the order released by Inkibra.
  4. Interrupt the assistant; confirm unsent frames disappear and provider playout stops before reporting cancelled.
  5. Restore a dropped control-event subscription and verify cursor catch-up without creating a duplicate session.
  6. Hang up; verify Photon media and Inkibra both close exactly once.