> This page is for Models.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.hams.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.hams.ai/_mcp/server.

# Authentication and limits

> How to present your API key on a WebSocket connection, how many connections you can hold, and what to do on each error.

Both speech APIs authenticate with an API key issued by HAMS. Send it as a bearer token in the
WebSocket upgrade request, before any message:

```http
GET /asr/ws HTTP/1.1
Host: voice.hams.ai
Upgrade: websocket
Connection: Upgrade
Authorization: Bearer YOUR_API_KEY
```

With the Python `websockets` package that is one argument on `connect`:

```python
headers = {"Authorization": f"Bearer {os.environ['HAMS_STT_API_KEY']}"}
async with websockets.connect("wss://voice.hams.ai/asr/ws", additional_headers=headers) as ws:
    ...
```

> **Note**
>
> `websockets` 12 and 13 call the argument `extra_headers`; 14 and later call it
> `additional_headers`. The examples in these docs use the newer name.

Keys are issued per service, so keep separate keys for speech-to-text and text-to-speech. Missing
or invalid credentials are rejected during the handshake with `401`.

## Connection limit

A credential may hold up to **five** simultaneous WebSocket connections. Speech-to-text rejects a
sixth handshake with `429`; text-to-speech completes the handshake and immediately closes the
socket with close code `1013` (`Server at capacity`). Use one connection per active stream or
session, keep it open for the whole stream instead of reconnecting per message, and close it as
soon as the stream ends so the slot is free for the next one.

## Status codes

| Status              | Meaning                                                                 | What to do                                        |
| ------------------- | ----------------------------------------------------------------------- | ------------------------------------------------- |
| HTTP `401`          | The API key is missing or invalid                                       | Fix the key; do not retry                         |
| HTTP `429`          | Speech-to-text: five connections are already active for this credential | Wait until one of them closes, then connect again |
| Close code `1013`   | Text-to-speech: five connections are already active                     | Wait until one of them closes, then connect again |
| HTTP `502` or `503` | The service is temporarily unavailable                                  | Retry with exponential backoff and jitter         |

Reconnect after transient failures with exponential backoff and jitter. When a connection drops
mid-stream, open a new one and resume: for speech-to-text, resend audio from the last final result;
for text-to-speech, resend the prompt that was in flight.

## Keeping the key safe

The key must stay on a trusted server:

* Store it in a secret manager or a protected environment variable.
* Never commit it to source control.
* Never put it in a URL, including the WebSocket URL.
* Never ship it to browsers or mobile applications; if a client-side framework cannot set the
  header, connect from a server-side service instead.
* Keep it out of client-side logs.

Contact HAMS to rotate a lost or exposed key.