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 backendToken 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.