# Creates a session

> POST /v1/sessions: Creates a session.

Source: https://docs.voice.kheelona.com/reference/generated/api/createsession/

`POST /v1/sessions`

| Property | Value |
| --- | --- |
| Operation | `createSession` |
| Scope | `sessions:write` |

## Parameters

| Name | In | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | string | no | Retry safely: the same key within 10 minutes returns the same session with a new client secret (the previous one stops working). 1–128 characters from `A–Z a–z 0–9 . _ : -`. |  |

## Request body

Content type `application/json`, required.

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `voice_id` | string | yes | A voice from `GET /v1/voices`: published and enabled for your partner. | pattern `^[a-z][a-z0-9-]{2,39}$` |
| `audio` | object | no | WebSocket audio formats. | may be null |
| `audio.input_format` | string | no | Client → server (default `pcm16_16k`). A PCM audio format: 16-bit little-endian mono at the named sample rate. | one of `pcm16_16k`, `pcm16_24k`, `pcm16_48k`; may be null |
| `audio.output_format` | string | no | Server → client (default `pcm16_24k`). A PCM audio format: 16-bit little-endian mono at the named sample rate. | one of `pcm16_16k`, `pcm16_24k`, `pcm16_48k`; may be null |
| `end_user_ref` | string | no | Your opaque user ID (not personal data): ≤ 128 characters from `A–Z a–z 0–9 . _ : -`. Stored in logs. | pattern `^[A-Za-z0-9._:-]{1,128}$`; may be null |
| `instructions` | string | no | Free text for this session (e.g. the child's name), ≤ 4,000 characters after NFC normalisation. Never stored or logged: it reaches the voice only encrypted. | max length 4000; may be null |
| `interruptions` | boolean | no | `false`: the voice is never cut off by speech while it speaks (for content that must play through). Default `true`. | may be null |
| `languages` | array of string | no | 1–11 distinct codes from `GET /v1/languages`; default `["en", "hi"]`. Speech recognition listens only for these; the voice replies in whichever the person speaks. More than one Indian language, or a beta language, is accepted with a warning. | min items 1; max items 11; may be null |
| `max_duration_s` | integer | no | The longest the session may run, ≤ your partner's limit (default: that limit). | format `int32`; min 1; max 3600; may be null |
| `metadata` | object | no | Your tags: ≤ 10 keys, string values ≤ 200 characters, ≤ 2 KB in total. Stored in logs; no personal data. | may be null |
| `transport` | string | no | Which connection URLs to return (default `auto`: both). Which connection URLs to return. | one of `auto`, `webrtc`, `websocket`; may be null |

Example:

```json
{
  "audio": {
    "input_format": "pcm16_16k",
    "output_format": "pcm16_24k"
  },
  "end_user_ref": "u_8f3a2c",
  "instructions": "The child's name is Aarav. He is 6 and loves dinosaurs. Keep answers very short.",
  "interruptions": true,
  "languages": [
    "en",
    "ta"
  ],
  "max_duration_s": 900,
  "metadata": {
    "app_version": "2.3.1",
    "screen": "home"
  },
  "transport": "auto",
  "voice_id": "lumi-dragon"
}
```

## Responses

### 201

Created (or replayed)

| Header | Type | Description |
| --- | --- | --- |
| `RateLimit-Limit` | integer | Requests allowed at once for this key |
| `RateLimit-Remaining` | integer | Requests left right now |
| `RateLimit-Reset` | integer | Seconds until the limit is full again |
| `X-Request-Id` | string | The request's ID (`req_…`); quote it in reports. |

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `session_id` | string | yes | `ses_…` |  |
| `client_secret` | object | yes | The client secret. |  |
| `client_secret.value` | string | yes | `kvcs_…` |  |
| `client_secret.expires_at` | string | yes | When it stops working if unused. | format `date-time` |
| `connect` | object | yes | Where to connect. |  |
| `connect.webrtc_url` | string | no | WebRTC offer endpoint (for browsers). | may be null |
| `connect.websocket_url` | string | no | WebSocket endpoint. | may be null |
| `voice` | object | yes | The pinned voice. |  |
| `voice.id` | string | yes | The voice ID. |  |
| `voice.version` | integer | yes | The pinned version. | format `int32` |
| `voice.name` | string | yes | Display name. |  |
| `languages` | array of string | yes | The declared languages (default `["en", "hi"]`). |  |
| `limits` | object | yes | The session's limits. |  |
| `limits.max_duration_s` | integer | yes | The session ends after this long. | format `int32` |
| `limits.idle_timeout_s` | integer | yes | The session ends after this long without speech. | format `int32` |
| `warnings` | array of Warning | yes | Language advice; empty when none. |  |
| `warnings[].code` | string | yes | `multiple_indian_languages`, `beta_languages` or `alpha_languages`. |  |
| `warnings[].message` | string | yes | What it means (for `beta_languages` and `alpha_languages`, naming the session's languages of that stage). |  |
| `warnings[].languages` | array of string | yes | The languages concerned. |  |

### 400

`invalid_request`: `details.field` names the field

| Header | Type | Description |
| --- | --- | --- |
| `RateLimit-Limit` | integer | Requests allowed at once for this key |
| `RateLimit-Remaining` | integer | Requests left right now |
| `RateLimit-Reset` | integer | Seconds until the limit is full again |
| `X-Request-Id` | string | The request's ID (`req_…`); quote it in reports. |

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `error` | object | yes | The error. |  |
| `error.code` | string | yes |  | one of `invalid_request`, `unauthenticated`, `secret_expired`, `forbidden`, `origin_not_allowed`, `voice_not_found`, `session_not_found`, `capacity_exhausted`, `partner_limit_reached`, `rate_limited`, `internal`, `not_found`, `conflict`, `unavailable` |
| `error.message` | string | yes | A short, fixed description. Never quotes the request. |  |
| `error.request_id` | string | yes | The request's ID (also in the `X-Request-Id` header); quote it when reporting a problem. | pattern `^req_[0-9A-HJKMNP-TV-Z]{26}$` |
| `error.details` | any | no | Structured detail, e.g. `{"field": "languages[1]"}` or `{"reason": "quota_exhausted"}`. |  |

### 401

Missing or invalid API key

| Header | Type | Description |
| --- | --- | --- |
| `RateLimit-Limit` | integer | Requests allowed at once for this key |
| `RateLimit-Remaining` | integer | Requests left right now |
| `RateLimit-Reset` | integer | Seconds until the limit is full again |
| `X-Request-Id` | string | The request's ID (`req_…`); quote it in reports. |

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `error` | object | yes | The error. |  |
| `error.code` | string | yes |  | one of `invalid_request`, `unauthenticated`, `secret_expired`, `forbidden`, `origin_not_allowed`, `voice_not_found`, `session_not_found`, `capacity_exhausted`, `partner_limit_reached`, `rate_limited`, `internal`, `not_found`, `conflict`, `unavailable` |
| `error.message` | string | yes | A short, fixed description. Never quotes the request. |  |
| `error.request_id` | string | yes | The request's ID (also in the `X-Request-Id` header); quote it when reporting a problem. | pattern `^req_[0-9A-HJKMNP-TV-Z]{26}$` |
| `error.details` | any | no | Structured detail, e.g. `{"field": "languages[1]"}` or `{"reason": "quota_exhausted"}`. |  |

### 403

`forbidden`: the key lacks `sessions:write`, the partner is suspended, or the monthly quota is used up (`details.reason`)

| Header | Type | Description |
| --- | --- | --- |
| `RateLimit-Limit` | integer | Requests allowed at once for this key |
| `RateLimit-Remaining` | integer | Requests left right now |
| `RateLimit-Reset` | integer | Seconds until the limit is full again |
| `X-Request-Id` | string | The request's ID (`req_…`); quote it in reports. |

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `error` | object | yes | The error. |  |
| `error.code` | string | yes |  | one of `invalid_request`, `unauthenticated`, `secret_expired`, `forbidden`, `origin_not_allowed`, `voice_not_found`, `session_not_found`, `capacity_exhausted`, `partner_limit_reached`, `rate_limited`, `internal`, `not_found`, `conflict`, `unavailable` |
| `error.message` | string | yes | A short, fixed description. Never quotes the request. |  |
| `error.request_id` | string | yes | The request's ID (also in the `X-Request-Id` header); quote it when reporting a problem. | pattern `^req_[0-9A-HJKMNP-TV-Z]{26}$` |
| `error.details` | any | no | Structured detail, e.g. `{"field": "languages[1]"}` or `{"reason": "quota_exhausted"}`. |  |

### 404

`voice_not_found`

| Header | Type | Description |
| --- | --- | --- |
| `RateLimit-Limit` | integer | Requests allowed at once for this key |
| `RateLimit-Remaining` | integer | Requests left right now |
| `RateLimit-Reset` | integer | Seconds until the limit is full again |
| `X-Request-Id` | string | The request's ID (`req_…`); quote it in reports. |

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `error` | object | yes | The error. |  |
| `error.code` | string | yes |  | one of `invalid_request`, `unauthenticated`, `secret_expired`, `forbidden`, `origin_not_allowed`, `voice_not_found`, `session_not_found`, `capacity_exhausted`, `partner_limit_reached`, `rate_limited`, `internal`, `not_found`, `conflict`, `unavailable` |
| `error.message` | string | yes | A short, fixed description. Never quotes the request. |  |
| `error.request_id` | string | yes | The request's ID (also in the `X-Request-Id` header); quote it when reporting a problem. | pattern `^req_[0-9A-HJKMNP-TV-Z]{26}$` |
| `error.details` | any | no | Structured detail, e.g. `{"field": "languages[1]"}` or `{"reason": "quota_exhausted"}`. |  |

### 409

`invalid_request`: an `Idempotency-Key` replay of a session that is live or finished, of a different request, or of one still in progress (`details.reason`)

| Header | Type | Description |
| --- | --- | --- |
| `RateLimit-Limit` | integer | Requests allowed at once for this key |
| `RateLimit-Remaining` | integer | Requests left right now |
| `RateLimit-Reset` | integer | Seconds until the limit is full again |
| `X-Request-Id` | string | The request's ID (`req_…`); quote it in reports. |

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `error` | object | yes | The error. |  |
| `error.code` | string | yes |  | one of `invalid_request`, `unauthenticated`, `secret_expired`, `forbidden`, `origin_not_allowed`, `voice_not_found`, `session_not_found`, `capacity_exhausted`, `partner_limit_reached`, `rate_limited`, `internal`, `not_found`, `conflict`, `unavailable` |
| `error.message` | string | yes | A short, fixed description. Never quotes the request. |  |
| `error.request_id` | string | yes | The request's ID (also in the `X-Request-Id` header); quote it when reporting a problem. | pattern `^req_[0-9A-HJKMNP-TV-Z]{26}$` |
| `error.details` | any | no | Structured detail, e.g. `{"field": "languages[1]"}` or `{"reason": "quota_exhausted"}`. |  |

### 429

`partner_limit_reached` (at the partner's concurrent sessions) or `rate_limited`

| Header | Type | Description |
| --- | --- | --- |
| `RateLimit-Limit` | integer | Requests allowed at once for this key |
| `RateLimit-Remaining` | integer | Requests left right now |
| `RateLimit-Reset` | integer | Seconds until the limit is full again |
| `Retry-After` | integer | Seconds to wait before retrying |
| `X-Request-Id` | string | The request's ID (`req_…`); quote it in reports. |

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `error` | object | yes | The error. |  |
| `error.code` | string | yes |  | one of `invalid_request`, `unauthenticated`, `secret_expired`, `forbidden`, `origin_not_allowed`, `voice_not_found`, `session_not_found`, `capacity_exhausted`, `partner_limit_reached`, `rate_limited`, `internal`, `not_found`, `conflict`, `unavailable` |
| `error.message` | string | yes | A short, fixed description. Never quotes the request. |  |
| `error.request_id` | string | yes | The request's ID (also in the `X-Request-Id` header); quote it when reporting a problem. | pattern `^req_[0-9A-HJKMNP-TV-Z]{26}$` |
| `error.details` | any | no | Structured detail, e.g. `{"field": "languages[1]"}` or `{"reason": "quota_exhausted"}`. |  |

### 503

`capacity_exhausted`: retry after `Retry-After` seconds. `details.reason` is `voice_service_starting` (retry in 60 s) or `voice_service_asleep` (retry in 300 s) while the voice service is not running; it is absent when all capacity is in use

| Header | Type | Description |
| --- | --- | --- |
| `RateLimit-Limit` | integer | Requests allowed at once for this key |
| `RateLimit-Remaining` | integer | Requests left right now |
| `RateLimit-Reset` | integer | Seconds until the limit is full again |
| `Retry-After` | integer | Seconds to wait before retrying |
| `X-Request-Id` | string | The request's ID (`req_…`); quote it in reports. |

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `error` | object | yes | The error. |  |
| `error.code` | string | yes |  | one of `invalid_request`, `unauthenticated`, `secret_expired`, `forbidden`, `origin_not_allowed`, `voice_not_found`, `session_not_found`, `capacity_exhausted`, `partner_limit_reached`, `rate_limited`, `internal`, `not_found`, `conflict`, `unavailable` |
| `error.message` | string | yes | A short, fixed description. Never quotes the request. |  |
| `error.request_id` | string | yes | The request's ID (also in the `X-Request-Id` header); quote it when reporting a problem. | pattern `^req_[0-9A-HJKMNP-TV-Z]{26}$` |
| `error.details` | any | no | Structured detail, e.g. `{"field": "languages[1]"}` or `{"reason": "quota_exhausted"}`. |  |
