Elizon Docs
Voice Integration

Security

Protecting API keys, handling tokens, and cleaning up voice sessions safely.

API Key Protection

Never call the Elizon Public API — including the voice endpoints — directly from client-side code. Your X-API-Key would be visible to anyone inspecting network requests in the browser or a decompiled mobile app.

Instead, use a backend proxy pattern: your server holds the API key and calls GET /voice-agents / POST /voice-agents/:agentId/sessions on the client's behalf. Only the resulting LiveKit wsUrl and token — which are scoped to a single session and expire — are sent to the client.

Client  --(your auth)-->  Your backend  --(X-API-Key)-->  Elizon API
Client  <--(wsUrl, token)--  Your backend

Token Handling

The token returned by POST /voice-agents/:agentId/sessions is a LiveKit participant token that expires 60 minutes after issue. It cannot be renewed in place — to keep a call going past expiry, call POST /voice-agents/:agentId/sessions again to start a fresh session and reconnect the client with the new wsUrl/token pair.

Treat the token like any other short-lived credential: pass it to the client once, don't log it, don't store it beyond the lifetime of the call.

HTTPS Only

wsUrl is always a wss:// (secure WebSocket) URL in production. Serve your own frontend over HTTPS too — browsers block microphone access (getUserMedia) on plain HTTP origins other than localhost.

Microphone Permissions

Requesting the microphone can be denied by the user or blocked by browser/OS policy. Handle the rejection instead of letting the call fail silently:

try {
  await room.localParticipant.setMicrophoneEnabled(true)
} catch (err) {
  // Permission denied, or no microphone available — the call can stay
  // connected (the user can still hear the agent) with the mic left off.
}

Session Cleanup

Call room.disconnect() when your voice UI unmounts, not just when the user explicitly ends the call — otherwise a user who navigates away mid-call leaves the microphone stream and the LiveKit connection open indefinitely.

useEffect(() => {
  return () => {
    room.disconnect()
  }
}, [room])

See Session Management for the full disconnect/mute/unmute reference.