# Client secrets

A client secret lets a browser open one Live session without your API key. Create it on your server, then pass its value to the browser.

## Create a client secret

- `POST https://api.dotwave.ai/v1/live/client_secrets`: Authenticate with your API key.

```bash
curl https://api.dotwave.ai/v1/live/client_secrets \
  -H "Authorization: Bearer $DOTWAVE_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "expires_after": {"anchor": "created_at", "seconds": 60},
    "session": {
      "type": "live",
      "model": "nemotron-voicechat",
      "audio": {
        "input": {"format": {"type": "audio/pcm", "rate": 24000}},
        "output": {
          "format": {"type": "audio/pcm", "rate": 24000},
          "encoding": "base64"
        }
      }
    }
  }'
```

- **`expires_after.seconds`**: How long the secret stays valid: 60 by default, 600 at most. `expires_after.anchor` is `created_at`.
- **`session.type`**: `live`.
- **`session.model`**: The model the session opens, such as `nemotron-voicechat`.
- **`session.audio`**: The audio formats, 24 kHz PCM16 in and out, and the output encoding, `base64` or `binary`.
- **`metadata`**: Your own string keys, kept with the session’s record: up to 16, and 512 bytes in all.

## The response

```json
{"value": "…", "expires_at": 1790000000,
 "session": {"id": "sess_…", "type": "live", "model": "nemotron-voicechat",
             "live_url": "wss://api.dotwave.ai/v1/live/sessions"}}
```

- **`value`**: The secret. Give only this to the browser.
- **`expires_at`**: When the secret expires, in Unix seconds.
- **`session.id`**: The session the secret opens. Look it up later with `GET /v1/sessions/{session_id}`.
- **`session.live_url`**: The socket to open.

## Connect from a browser

Browsers cannot set headers on a WebSocket, so pass the secret as the `token` query parameter. The browser still sends `session.start` first. A client that can set headers sends `Authorization: Bearer <value>` instead.

```typescript
// `secret` is the `value` and `liveUrl` the `session.live_url`
// your server received.
const ws = new WebSocket(`${liveUrl}?token=${encodeURIComponent(secret)}`);

ws.onopen = () => {
  // session.start must be the first event, or the socket refuses it.
  ws.send(JSON.stringify({
    type: 'session.start',
    session: { model: 'nemotron-voicechat' },
  }));
};

ws.onmessage = ({ data }) => {
  const event = JSON.parse(data);
  if (event.type === 'session.output_audio.delta') play(event.delta);
};
```

A secret opens one session. An unused secret counts toward your concurrency limit until it expires, so create it just before the browser connects.

## Errors

Refusals use the [error envelope](https://dotwave.ai/docs/errors/). A field the model does not support, such as custom instructions or tools, is refused with `400 invalid_parameter`. At zero credit, the call returns `403 insufficient_credit`. Past your project’s limits it returns `429`, and when the model is at capacity `503`, both with `Retry-After`.
