# Create a client token

`POST https://api.morevoice.ai/v1/client_tokens`

Mint, on your server, a short-lived token a browser uses to start one call to one assistant with @morevoice/web (`MoreVoiceWeb.start({ token })`). The token works once, until `expires_at`; bind it to your site with `origin`. It carries the call's ID, so you can follow the call (webhooks, GET /v1/calls/{id}/events) before it starts.

Authenticate with a secret API key: `Authorization: Bearer $MOREVOICE_API_KEY`.

Operation ID: `client_tokens_create`.

## Parameters

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `MoreVoice-Version` | `string` | no | The API version to use for this request. Defaults to the version the API key is pinned to. |
| `Idempotency-Key` | `string` | no | A unique key (for example a UUID) that makes this request safe to retry: for 24 hours, a retry with the same key and parameters returns the first response instead of acting twice. |

## Request body

Optional, `application/json`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `agent_user_id` | `string` | no | The agent whose softphone the token opens (the embeddable softphone; `/ws/call` refuses agent tokens). |
| `assistant_id` | `string` | no | The assistant the browser will talk to. |
| `metadata` | `MetadataInput` | no | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `origin` | `string` | no | The only website the token works from (`https://shop.example`): the browser's Origin must match. Strongly recommended. http is accepted for localhost only. |
| `ttl_s` | `integer` | no | How long the token can be used to start the call, in seconds (300–900, default 300). The call itself may run longer. |

Example:

```json
{
  "assistant_id": "asst_8tRPaZp5hLMbrGqdJ9AmNa",
  "metadata": {
    "crm_contact_id": "0031x00000AbCdE"
  },
  "origin": "https://shop.example",
  "ttl_s": 300
}
```

## Responses

### 201 Created

The token. Returns `ClientToken`.

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"client_token"` | — |
| `token` | `string` | The client token (a signed JWT). Give it to the browser; it opens one call, once, before `expires_at`. |
| `expires_at` | `string` | An ISO-8601 timestamp in UTC. |
| `call_id` | `string` | The call the token opens (the same ID the call will have). |
| `assistant_id` | `string \| null` | `asst_…` ID. |
| `agent_user_id` | `string \| null` | `usr_…` ID. |
| `origin` | `string \| null` | The website the token is bound to, or null. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `ws_url` | `string` | Where the browser connects: `<ws_url>?client_token=<token>` (the @morevoice/web SDK does it for you). |

Example:

```json
{
  "agent_user_id": null,
  "assistant_id": "asst_8tRPaZp5hLMbrGqdJ9AmNa",
  "call_id": "call_3slqGaD2htUzxdRsI",
  "expires_at": "2026-11-03T09:19:22.000Z",
  "livemode": true,
  "object": "client_token",
  "origin": "https://shop.example",
  "token": "<client_token: short-lived JWT, pass it through unchanged>",
  "ws_url": "wss://api.morevoice.ai/ws/call"
}
```

### 400 Bad Request

The request is invalid: a parameter is missing, malformed or unknown, or the version header is unknown. Returns `ErrorEnvelope`.

### 401 Unauthorized

No valid API key was sent. Returns `ErrorEnvelope`.

### 403 Forbidden

The key may not do this (a missing scope, a plan limit, or a compliance block). Returns `ErrorEnvelope`.

### 409 Conflict

The request conflicts with the object's state, or the Idempotency-Key was reused with other parameters. Returns `ErrorEnvelope`.

### 429 Too Many Requests

Too many requests, or no call capacity right now. Retry after the Retry-After delay. Returns `ErrorEnvelope`.

### 500 Internal Server Error

Something went wrong on MoreVoice's side. Retry with the same Idempotency-Key. Returns `ErrorEnvelope`.

## Code samples

**TypeScript**

```ts
import MoreVoice from "@morevoice/sdk";

const mv = new MoreVoice(); // MOREVOICE_API_KEY from the environment

const clientToken = await mv.clientTokens.create({
	assistant_id: "asst_8tRPaZp5hLMbrGqdJ9AmNa",
	metadata: {
		crm_contact_id: "0031x00000AbCdE",
	},
	origin: "https://shop.example",
	ttl_s: 300,
});
console.log(clientToken);
```

**Python**

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

client_token = client.client_tokens.create({
    "assistant_id": "asst_8tRPaZp5hLMbrGqdJ9AmNa",
    "metadata": {
        "crm_contact_id": "0031x00000AbCdE",
    },
    "origin": "https://shop.example",
    "ttl_s": 300,
})
print(client_token)
```

**cURL**

```sh
curl -X POST https://api.morevoice.ai/v1/client_tokens \
  -H "Authorization: Bearer $MOREVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "assistant_id": "asst_8tRPaZp5hLMbrGqdJ9AmNa",
  "metadata": {
    "crm_contact_id": "0031x00000AbCdE"
  },
  "origin": "https://shop.example",
  "ttl_s": 300
}'
```
