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

# Realtime WebSocket

> Connect to the OpenAI-compatible Realtime WebSocket, authenticate with sk_ or ek_ keys, and handle close codes

Open a Realtime session over WebSocket. The protocol is the OpenAI Realtime GA protocol. After the handshake, send [client events](https://docs.60db.ai/realtime/api-reference/client-events.md) and receive [server events](https://docs.60db.ai/realtime/api-reference/server-events.md).

## Endpoint

**url** `string`

`wss://api.60db.ai/v1/realtime`

## Query parameters

**model** `string` *required*

Model to run. Use `60db-realtime-v1-mini`. OpenAI model names (`gpt-realtime`, `gpt-realtime-mini`, `gpt-4o-realtime-preview*`) are accepted and run on this model.

```
wss://api.60db.ai/v1/realtime?model=60db-realtime-v1-mini
```

## Authentication

Use one of two methods.

### Server: Bearer header

**Authorization** `string` *required*

`Bearer sk_your_api_key`. Use this on servers, from Node `ws`, Python, or the OpenAI SDKs.

```javascript theme={null}
import WebSocket from 'ws';

const ws = new WebSocket(
  'wss://api.60db.ai/v1/realtime?model=60db-realtime-v1-mini',
  { headers: { Authorization: `Bearer ${process.env.SIXTYDB_API_KEY}` } },
);
```

### Browser: ephemeral key subprotocols

Browsers cannot set headers on a WebSocket. Pass the `ek_` key in the subprotocol list instead. Mint the key with [Create ephemeral key](https://docs.60db.ai/realtime/api-reference/ephemeral-keys.md).

| Subprotocol | Value |
| - | - |
| 1 | `realtime` |
| 2 | `openai-insecure-api-key.<ek_...>` |
| 3 | `openai-beta.realtime-v1` |

```javascript theme={null}
const ws = new WebSocket(
  'wss://api.60db.ai/v1/realtime?model=60db-realtime-v1-mini',
  ['realtime', `openai-insecure-api-key.${ephemeralKey}`, 'openai-beta.realtime-v1'],
);
```

`@openai/agents-realtime` sends these subprotocols for you when you call `session.connect({ apiKey })`.

| Key | Accepted on `/v1/realtime` | Accepted on other 60db endpoints |
| - | - | - |
| `sk_` | Yes | Yes |
| `ek_` | Yes | No |

## Session start

After the socket opens, the server sends `session.created`. Send `session.update` before you stream audio so the voice, audio formats and tools are set. See [Client events](https://docs.60db.ai/realtime/api-reference/client-events.md#session).

## Errors

Failures are reported as an `error` event with this shape:

```json theme={null}
{
  "type": "error",
  "event_id": "event_...",
  "error": { "type": "invalid_request_error", "code": "...", "message": "..." }
}
```

Some failures also close the socket. The close code tells you which kind. See the table below.

## Close codes

| Code | Name | Meaning |
| - | - | - |
| `1000` | Normal | Session ended normally |
| `1008` | Policy | Authentication or policy check failed |
| `1011` | Internal | Unexpected server error |
| `4000` | Idle timeout | No audio or activity for too long |
| `4001` | Max duration | Session reached the maximum length |
| `4002` | Insufficient credits | The workspace balance cannot cover the session |
| `4003` | Concurrency limit | Too many sessions for your plan |
| `4005` | Upstream failure | A speech or model service failed. Retry the session |
| `4006` | Protocol violation | A client event was malformed or out of order |

> [!NOTE]
>
> Read the close reason and the last `error` event before you retry. Retry `4005` with backoff. Do not retry `1008`, `4002` or `4003` until the cause is fixed.

## Related

* [Client events](https://docs.60db.ai/realtime/api-reference/client-events.md)
* [Server events](https://docs.60db.ai/realtime/api-reference/server-events.md)
* [Create ephemeral key](https://docs.60db.ai/realtime/api-reference/ephemeral-keys.md)


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