# Client events

> Client events: sent by the client to Kheelona Voice.

Source: https://docs.voice.kheelona.com/reference/generated/events/client/

Client events: sent by the client to Kheelona Voice, as JSON text frames. Every event has a `type` field.

Every client event also carries:

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `event_id` | string | no | Optional ID chosen by the client: 1–64 characters of `A–Z a–z 0–9 _ -`. | pattern `^[A-Za-z0-9_-]{1,64}$`; default `null`; may be null |

| Event | When |
| --- | --- |
| [`input.interrupt`](#inputinterrupt) | Stop the current reply now, as if the person had started speaking. |
| [`input.text`](#inputtext) | Inject a turn as text, skipping speech recognition. It goes through the safety checks, with the same logging as voice turns. Invisible characters are removed from `text` when it is parsed; text that is empty once cleaned is refused as `invalid_request`. |
| [`input.mute`](#inputmute) | The client mic was muted or unmuted. |
| [`playback.position`](#playbackposition) | WebSocket only: how much of a reply the client actually played when interrupted. |
| [`client.capabilities`](#clientcapabilities) | Optional, sent once after `session.ready`: what the client can do. Capabilities this server doesn't know are ignored. |
| [`session.end`](#sessionend) | End the session normally. |
| [`ping`](#ping) | Application heartbeat; the server answers `pong`. |

## `input.interrupt`

Stop the current reply now, as if the person had started speaking.

No fields besides `type`.

## `input.text`

Inject a turn as text, skipping speech recognition. It goes through the safety checks, with the same logging as voice turns. Invisible characters are removed from `text` when it is parsed; text that is empty once cleaned is refused as `invalid_request`.

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `text` | string | yes | The turn's text (at most 2,000 characters; never stored). | max length 2000 |

## `input.mute`

The client mic was muted or unmuted.

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `muted` | boolean | yes | Whether the mic is muted. |  |

## `playback.position`

WebSocket only: how much of a reply the client actually played when interrupted.

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `reply_id` | [ReplyId](#replyid) | yes | The interrupted reply. | pattern `^rep_[0-9A-HJKMNP-TV-Z]{26}$` |
| `played_ms` | integer | yes | Milliseconds of it the client played. | format `uint32`; min 0 |

## `client.capabilities`

Optional, sent once after `session.ready`: what the client can do. Capabilities this server doesn't know are ignored.

| Field | Type | Required | Description | Rules |
| --- | --- | --- | --- | --- |
| `playback_pause` | boolean | no | The client pauses and resumes its playback buffer on `reply.paused` and `reply.resumed`. Without it the server never sends those two events. | default `false` |

## `session.end`

End the session normally.

No fields besides `type`.

## `ping`

Application heartbeat; the server answers `pong`.

No fields besides `type`.

## Types

### ReplyId

string, pattern `^rep_[0-9A-HJKMNP-TV-Z]{26}$`.
