> This page is for Voice AI Platform.

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

# API keys

> Get a secret key for your servers or a public key for your pages, and restrict what each may do.

An API key belongs to one entity and comes in two kinds. A **secret key** (`sk_…`) stands in for a
server: it carries a role and can be pinned to an IP allowlist. A **public key** (`pk_…`) stands in
for a web page: it only reaches the browser endpoints under `/v1/` and is restricted by domain,
scope and, optionally, reCAPTCHA.

Keys are created, changed and revoked in the dashboard by an administrator of the entity. The API
never creates keys: a key cannot mint other keys.

## Create a secret key

In the dashboard, create a key of type **Secret**:

| Field       | Notes                                                                                                                     |
| ----------- | ------------------------------------------------------------------------------------------------------------------------- |
| Name        | 1–100 characters. Name the system that will use the key.                                                                  |
| Role        | `READER` (default) or `WRITER`. Writers may create, update and delete.                                                    |
| Allowed IPs | Optional. Single addresses or CIDR ranges, such as `203.0.113.10` or `198.51.100.0/24`. Leave empty to allow any address. |

> **Warning**
>
> The dashboard shows the full key once, when it is created. The platform stores only a hash of it;
> afterwards the key list shows a prefix such as `sk_3f9a…` so you can tell keys apart, never the
> key. A lost key is replaced, not recovered.

Then send the key in place of any other credential:

```bash
curl https://api.hams.ai/outbound-batch/batches \
  -H "X-Secret-Key: $HAMS_SECRET_KEY"
```

## Create a public key

In the dashboard, create a key of type **Public**:

| Field                     | Notes                                                                                                            |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Name                      | 1–100 characters                                                                                                 |
| Allowed domains           | Required. Hostnames such as `www.example.com`, matched against the request's `Origin` or `Referer`.              |
| Allowed scopes            | Optional. The operations the key may call; see the table below. Leave empty to allow all of them.                |
| reCAPTCHA secret          | Optional. Your Google reCAPTCHA secret; stored encrypted. When set, starting a chat requires a `g-token` header. |
| reCAPTCHA score threshold | 0.0–1.0, default `0.5`. Tokens scoring below it are rejected.                                                    |
| Fraud protection          | Off by default.                                                                                                  |

### Scopes

| Scope                | Operation                                            |
| -------------------- | ---------------------------------------------------- |
| `chat:init`          | `POST /v1/ai-agents/{agent_id}/chat/init`            |
| `chat:stream`        | `POST /v1/ai-agents/{agent_id}/chat/stream`          |
| `chat:end`           | `POST /v1/ai-agents/{agent_id}/chat/{call_id}/end`   |
| `chat:rate`          | `POST /v1/ai-agents/{agent_id}/chat/{call_id}/rate`  |
| `chat:status`        | `GET /v1/ai-agents/{agent_id}/chat/{call_id}/status` |
| `widget-config:read` | `GET /v1/ai-agents/{agent_id}/widget-config`         |

A scoped key calling anything outside its list gets `403` with `KEY_SCOPE_NOT_ALLOWED`. Scopes are
append-only: new operations get new scopes, and existing lists keep working.

## Rotate and revoke

Changing a key's restrictions in the dashboard never changes the key itself. Revoking takes effect
immediately: the next request with that key gets `401` with `INVALID_API_KEY`. To rotate a key,
create the new one, deploy it, then revoke the old one.

> **Note**
>
> Rate limits for secret keys are counted per entity, and for public keys per key and per entity. See
> [API conventions](/platform/conventions#rate-limits).