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

# Browser Ephemeral Keys

> Let a web page talk to 60db Realtime with a short-lived ek_ key minted by your server

Never put an `sk_` key in browser code. Instead, your server mints a short-lived **ephemeral key** (`ek_...`) and sends it to the page. The browser connects with that key, and it stops working when it expires.

**1. Mint a client key on your server**

Your server calls [`POST /v1/realtime/client_secrets`](https://docs.60db.ai/realtime/api-reference/ephemeral-keys) with your `sk_` key. Set the session config here. The config is applied when the browser connects.

```javascript theme={null}
// Server (Node 18+). Runs with your sk_ key; never sent to the browser.
app.get('/realtime-token', async (req, res) => {
  const r = await fetch('https://api.60db.ai/v1/realtime/client_secrets', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.SIXTYDB_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      expires_after: { anchor: 'created_at', seconds: 600 },
      session: {
        type: 'realtime',
        model: '60db-realtime-v1-mini',
        output_modalities: ['audio'],
        audio: { output: { voice: 'YOUR_VOICE_ID' } },
        instructions: 'You are a friendly voice assistant.',
      },
    }),
  });
  const { value, expires_at } = await r.json();
  res.json({ value, expires_at }); // only the ek_ value leaves the server
});
```

The response has the key and its expiry:

```json theme={null}
{ "value": "ek_...", "expires_at": 1761130000, "session": { "type": "realtime", "model": "60db-realtime-v1-mini" } }
```

**2. Connect from the browser**

Pass the `ek_` key to the client. With `@openai/agents-realtime`, call `connect` with `apiKey`:

```javascript theme={null}
import { RealtimeAgent, RealtimeSession, OpenAIRealtimeWebSocket } from '@openai/agents-realtime';

const { value } = await fetch('/realtime-token').then((r) => r.json());

const session = new RealtimeSession(
  new RealtimeAgent({ name: 'Assistant', instructions: 'You are a friendly voice assistant.' }),
  {
    transport: new OpenAIRealtimeWebSocket({ url: 'wss://api.60db.ai/v1/realtime' }),
    model: '60db-realtime-v1-mini',
  },
);

await session.connect({ apiKey: value });
```

> [!NOTE]
>
> The SDK sends the key as a WebSocket subprotocol. If you connect by hand, the subprotocols are `realtime`, `openai-insecure-api-key.<ek_...>` and `openai-beta.realtime-v1`.

**3. Refresh before expiry**

Keys live from 10 seconds to 2 hours (default 600). A key can be reused until it expires. Mint a new one before the old one expires for long sessions, and once per page load for short ones.

## Security notes

| Rule | Why |
| - | - |
| Mint keys only on your server | The `sk_` key can create keys and must not leave the server |
| Keep the TTL short | A leaked `ek_` works until it expires. Default is 600 seconds |
| Mint one key per page load, not per request | Limits how many live keys a leaked page can expose |
| Do not use `ek_` on other endpoints | `ek_` keys are accepted only on `/v1/realtime`. REST and `/ws/realtime` reject them |
| Revoke by deleting the parent key | An `ek_` key stops working when the `sk_` key that minted it is deleted or disabled |

The `session` config you send to `client_secrets` is applied when the browser connects. To use different settings, mint a new key.

## Errors

Errors use the OpenAI envelope:

```json theme={null}
{ "error": { "message": "...", "type": "authentication_error", "code": "...", "param": null } }
```

| HTTP status | Type | Meaning |
| - | - | - |
| `400` | `invalid_request_error` | `expires_after` or `session` is invalid |
| `401` | `authentication_error` | Missing or invalid `sk_` key |
| `500` | `server_error` | Retry with backoff |

## Related

* [Create ephemeral key](https://docs.60db.ai/realtime/api-reference/ephemeral-keys.md) reference
* [WebSocket connection](https://docs.60db.ai/realtime/api-reference/websocket.md) reference


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