> ## Documentation Index
> Fetch the complete documentation index at: https://docs.telzino.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Events and errors

> Call events and error codes in the Voice SDK

## Events

Subscribe with `call.on(event, fn)`. It returns an unsubscribe function.

```ts theme={null}
const off = call.on('agentState', (state) => console.log('agent is', state));
off();
```

| Event | Payload | Fires when |
| - | - | - |
| `status` | `CallStatus` | The connection status changes. |
| `agentState` | `AgentState` | The agent starts listening, thinking or speaking. |
| `transcript` | `TranscriptItem` | A transcript segment is added or updated. |
| `message` | `ChatMessage` | A chat message is sent or received. |
| `audioLevel` | `{ local: number; agent: number }` | About every 100 ms, only while a listener is attached. |
| `muted` | `boolean` | `setMuted` changes the mute state. |
| `needsUserGesture` | `boolean` | Audio playback is blocked or unblocked. |
| `error` | `TelzinoError` | A non-fatal or terminal error occurred mid-call. |
| `ended` | `{ reason: 'user' \| 'agent' \| 'timeout' \| 'error' }` | The call is over. |

### Types

```ts theme={null}
type CallStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'ended';
type AgentState = 'initializing' | 'listening' | 'thinking' | 'speaking' | 'unknown';
type TranscriptItem = {
  id: string;
  role: 'user' | 'agent';
  text: string;
  final: boolean;
  timestamp: number;
};
type ChatMessage = { id: string; role: 'user' | 'agent'; text: string; timestamp: number };
```

### End reasons

| Reason | Meaning |
| - | - |
| `user` | You called `end()`. |
| `agent` | The agent left the call. |
| `timeout` | The room was closed or deleted, for example when the session ended. |
| `error` | The connection dropped unexpectedly. An `error` event is emitted first. |

`ended` fires once per call.

## Errors

`start()` and the other methods reject with `TelzinoError`, which has `code`, `message`, `status?` and `recoverable`. Errors during a call arrive on the `error` event. In React they land in `error` from `useTelzino()`.

| Code | Meaning |
| - | - |
| `invalid_key` | The publishable key is missing, wrong or revoked. |
| `origin_not_allowed` | This page's origin is not in the key's allowed origins. |
| `embed_disabled` | The Voice SDK is turned off for the account. |
| `agent_not_found` | No such agent for this key. |
| `rate_limited` | Too many sessions; retry later. |
| `invalid_request` | Bad request, a call is already active, or an unexpected server response. |
| `livekit_unavailable` | The media server could not create the session. |
| `network` | Telzino could not be reached. |
| `permission_denied` | Microphone access was denied. |
| `livekit` | A media connection error. |

`recoverable` is the SDK's hint that the same request may succeed if retried. It is true for `rate_limited`, `livekit_unavailable`, `network` and `livekit`. For `network`, retry once. If it keeps failing from a browser, the usual cause is an origin that is not on the key's allowed list; fix that in the dashboard. The other codes (`invalid_key`, `origin_not_allowed`, `embed_disabled`, `agent_not_found`, `invalid_request` and `permission_denied`) are not recoverable and need a change on your side, such as an allowed origin, a different agent id, or microphone permission.

```ts theme={null}
try {
  const call = await voice.start({ agentId: '3f1c2a4e-8b7d-4c3a-9e2f-1a2b3c4d5e6f' });
} catch (err) {
  if (err instanceof TelzinoError && err.recoverable) {
    // retry later
  }
}
```

Import `TelzinoError` from `@telzino/voice`.

The server codes map to the HTTP errors documented in [Voice Sessions](https://docs.telzino.com/api-reference/voice-sessions.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.