# Messages

> The Messages operations of the Kabu API: notify, ask and steer: one object, one lifecycle per kind.

Notify, ask and steer: one object, one lifecycle per kind.

Generated from the contract, version `2.0.0-draft.2` ([OpenAPI](/openapi.yaml)). Base URL `https://usekabu.com/api`. Errors are problem details, each [code](/docs/errors) with its own page.

## `POST /v2/messages` · Notify, or ask a human (include `ask`)

Commits the message, its deadline and quota before responding. With `wait`, holds until an ask settles or the time passes and returns the current object either way. Retrying with the same Idempotency-Key returns the current state of the same message for its whole life, so `create` in a loop is the complete crash-safe integration. Once the message was deleted with its project (projects.delete), a retry answers 404 and sends nothing.

- **Auth:** an API key.
- **Scopes:** `messages:write`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `Idempotency-Key` | header | string, at most 255 characters | no | Retry-safe key. For messages, sessions and responses it binds to the created object for its life; elsewhere for 24 h. |
| `wait` | query | integer, from 0 to 50 | no | Hold up to N seconds for the state to settle. 0 or absent returns at once. The kabu CLI and the MCP tools wait 25. |

**Request body**: [MessageCreate](#schema-MessageCreate).

| Status | Means | Body |
|---|---|---|
| `201` | Created, or the current state of the object this Idempotency-Key created. | [Message](#schema-Message) |
| `429` | A rate, concurrency or quota limit was reached | [Problem](/docs/api/discovery#schema-Problem) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/messages" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"title":"…"}'
```

## `GET /v2/messages` · List messages

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `messages:read`.
- **Signed-in humans:** `audience`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `limit` | query | integer, from 1 to 100 | no |  |
| `after` | query | string | no | Opaque cursor from next_cursor. |
| `project` | query | string | no | Only messages of this project: its `prj_…` id, slug, normalised key, alias, or its id with separators folded away (`prj_01AB…` as `prj01ab…`, the key the app uses for a project it knows only by id). |
| `session` | query | string | no |  |
| `agent` | query | string | no | Only messages of this agent: its `agt_…` id, slug or normalised key (case, spaces, `-` and `_` ignored). `script` selects messages that name no agent. With `project`, this lists one thread. |
| `kind` | query | [MessageKindKnown](#schema-MessageKindKnown) | no |  |
| `status` | query | [MessageStatusKnown](#schema-MessageStatusKnown) | no |  |
| `order` | query | string, one of `"desc"`, `"asc"` | no |  |
| `cutover` | query | string | no | Set to `1` or `true` to opt in to the server's hitl cut-over: when the operator sets KABU_HITL_CUTOVER, messages created before that instant are excluded. Equivalent to the Kabu-Client-Capabilities value hitl_cutover_v1. Without either, the full history is returned (older app builds rely on this). |
| `Kabu-Client-Capabilities` | header | string | no | Comma-separated client capabilities. `hitl_cutover_v1` opts in to the hitl cut-over exactly as `cutover=1` does. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `200` | OK | [MessageList](#schema-MessageList) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl "https://usekabu.com/api/v2/messages" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `POST /v2/messages/preview` · Render a message without sending it

Validates, resolves recipients and returns what each surface would show. No id is created and nothing is stored.

- **Auth:** an API key.
- **Scopes:** `messages:write`.

**Request body**: [MessageCreate](#schema-MessageCreate).

| Status | Means | Body |
|---|---|---|
| `200` | OK | [MessagePreview](#schema-MessagePreview) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/messages/preview" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"…"}'
```

## `GET /v2/messages/{message_id}` · Get a message; with wait, hold until an ask settles

Returns immediately for a settled ask, a notify or a steer. For a pending ask, holds until it settles or `wait` seconds pass, then returns the current object. Change-watching belongs to events.

- **Auth:** an API key, or a signed-in human's token (the Kabu apps).
- **Scopes:** `messages:read`.
- **Signed-in humans:** `audience`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `message_id` | path | string | yes | A `msg_…` id. |
| `wait` | query | integer, from 0 to 50 | no | Hold up to N seconds for the state to settle. 0 or absent returns at once. The kabu CLI and the MCP tools wait 25. |
| `Kabu-Org` | header | string | no | Select a live membership (default personal org); non-member is 404. Human tokens are live-only. Caller user id is distinct from billing owner. |

| Status | Means | Body |
|---|---|---|
| `200` | OK | [Message](#schema-Message) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl "https://usekabu.com/api/v2/messages/$MESSAGE_ID" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `DELETE /v2/messages/{message_id}` · Erase content (keeps digest, receipt and audit skeleton)

- **Auth:** an API key.
- **Scopes:** `messages:write`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `message_id` | path | string | yes | A `msg_…` id. |

| Status | Means | Body |
|---|---|---|
| `204` | Done |  |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X DELETE "https://usekabu.com/api/v2/messages/$MESSAGE_ID" \
  -H "Authorization: Bearer $KABU_API_KEY"
```

## `POST /v2/messages/{message_id}/cancel` · Withdraw a pending ask

Returns the message. If it had already settled, returns it unchanged.

- **Auth:** an API key.
- **Scopes:** `messages:write`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `message_id` | path | string | yes | A `msg_…` id. |
| `Idempotency-Key` | header | string, at most 255 characters | no | Retry-safe key. For messages, sessions and responses it binds to the created object for its life; elsewhere for 24 h. |

**Request body** (optional): [MessageCancel](#schema-MessageCancel).

| Status | Means | Body |
|---|---|---|
| `200` | OK | [Message](#schema-Message) |
| `4XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |
| `5XX` | Error | [Problem](/docs/api/discovery#schema-Problem) |

```sh
curl -X POST "https://usekabu.com/api/v2/messages/$MESSAGE_ID/cancel" \
  -H "Authorization: Bearer $KABU_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## Schemas

### MessageCreate

| Property | Type | Required | Description |
|---|---|---|---|
| `title` | string, 1 to 120 characters | yes |  |
| `body` | string, at most 8192 characters | no | Markdown subset: bold, italic, code, links and lists. |
| `message` | string, at most 8192 characters | no | HITL producer message body (Markdown subset). |
| `project` | [ProjectRef](#schema-ProjectRef) | no | A project slug (auto-created on first use) or `prj_…` id. Omit for `default`. A slug that normalises (lowercase, letters and digits only) to an existing project's key names that project, so `My App`, `my-app` and `my_app` are one project. Automatic creation stops at 1,000 live projects per org and mode with `429 quota_exceeded`; existing projects stay usable. Nothing remembers a deleted project (projects.delete): the next message that names its slug creates a fresh one. |
| `session` | string | no | A `ses_…` id. A session that no longer exists (deleted with its project) is ignored: the message is accepted and delivered without one, and its `session` is null. |
| `agent` | string, at most 64 characters | no | Agent slug or `agt_…` id; ignored when the key is bound to an agent. A name that normalises to an existing agent's key in the project names that agent. Automatic creation stops at 1,000 distinct agent names per org and mode with `429 quota_exceeded`; a name already used in another project of the org is never a new name. A backstop of 10,000 agent rows per org and mode applies to every automatic creation. |
| `source` | string, at most 120 characters | no | Optional free-text source such as a machine or server name. |
| `fields` | array of [Field](#schema-Field), at most 12 items | no | Optional list of up to 12 key/value extras. |
| `to` | array of [RecipientRef](#schema-RecipientRef), 1 to 20 items | no | Default: the key's `default_to`. |
| `category` | string, at most 64 characters, matching `^[a-z0-9][a-z0-9._-]*$` | no |  |
| `priority` | [PriorityKnown](#schema-PriorityKnown) | no | Registry-backed priority. `urgent` needs the `messages:urgent` scope. Unknown values render as `normal`. |
| `expires_in` | integer, from 60 to 2592000 | no | One expiry for notify (hide) and ask (settle). Default 3600 for asks. |
| `sensitive` | boolean | no | Hide content on lock screens and previews. |
| `action` | [Action](#schema-Action) | no | The concrete action being authorised. |
| `ask` | [Ask](#schema-Ask) | no | Present ⇒ the message is an ask. `type` is presentation; `resolution.decision` is meaning. |
| `blocks` | array of [Block](#schema-Block), at most 10 items | no |  |
| `attachments` | array of [AttachmentRef](#schema-AttachmentRef), at most 10 items | no |  |
| `links` | array of [Link](#schema-Link), at most 4 items | no |  |
| `voice` | [Voice](#schema-Voice) | no |  |
| `appearance` | [Appearance](#schema-Appearance) | no | Cosmetics only. Resolved org → project → agent → recipient, then this override; recipient restrictions always win. |
| `collapse_key` | string, at most 64 characters | no | A later notify with the same key replaces this one on devices. |
| `metadata` | [Metadata](#schema-Metadata) | no | Your own key/value pairs (≤ 50 keys, values ≤ 500 chars), returned verbatim on every object and event. |
| `correlation` | [Correlation](#schema-Correlation) | no | Runtime correlation ids (LangGraph, OpenAI Agents, MCP Tasks, A2A, AG-UI). Opaque to Kabu. |
| `extensions` | [Extensions](#schema-Extensions) | no | Reverse-domain namespaced extension data (e.g. `com.example.ticket`), ≤ 8 KiB total. Cannot change core semantics. |

Unknown properties are refused.

### ProjectRef

A project slug (auto-created on first use) or `prj_…` id. Omit for `default`. A slug that normalises (lowercase, letters and digits only) to an existing project's key names that project, so `My App`, `my-app` and `my_app` are one project. Automatic creation stops at 1,000 live projects per org and mode with `429 quota_exceeded`; existing projects stay usable. Nothing remembers a deleted project (projects.delete): the next message that names its slug creates a fresh one.

Type: string, at most 64 characters.

### Field

| Property | Type | Required | Description |
|---|---|---|---|
| `label` | string, at most 40 characters | yes |  |
| `value` | string, at most 200 characters | yes |  |

Unknown properties are refused.

### RecipientRef

`rcp_…`, `ext:<your external id>`, or `me` (the key's `default_to`).

Type: string, at most 140 characters.

### PriorityKnown

Registry-backed priority. `urgent` needs the `messages:urgent` scope. Unknown values render as `normal`.

Type: string, one of `"low"`, `"normal"`, `"high"`, `"urgent"`.

### Action

The concrete action being authorised.

| Property | Type | Required | Description |
|---|---|---|---|
| `name` | string, at most 100 characters | yes |  |
| `args` | object | no | The proposed arguments, shown to the human and bound into the digest (≤ 16 KiB). |
| `args_schema` | object | no | Optional JSON Schema (2020-12) for `edited_args`, restricted to an offline profile: local `$ref` only (`#/…`), depth ≤ 8, ≤ 8 KiB, no `$dynamicRef`, `format` assertions or `contentSchema`. Unsupported keywords are rejected with 400 unsupported_schema_keyword. |
| `description` | string, at most 2000 characters | no |  |

Unknown properties are refused.

### Ask

Present ⇒ the message is an ask. `type` is presentation; `resolution.decision` is meaning.

Type: [AskConfirm](#schema-AskConfirm) or [AskChoice](#schema-AskChoice) or [AskText](#schema-AskText) or [AskForm](#schema-AskForm) or [AskMulti](#schema-AskMulti).

Chosen by `type`: `"confirm"`: [AskConfirm](#schema-AskConfirm), `"choice"`: [AskChoice](#schema-AskChoice), `"text"`: [AskText](#schema-AskText), `"form"`: [AskForm](#schema-AskForm), `"multi"`: [AskMulti](#schema-AskMulti).

### AskConfirm

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"confirm"` | yes |  |
| `options` | array of [ConfirmOption](#schema-ConfirmOption), 2 to 4 items | no | Each needs `decision` (approve or reject). Omit for approve / reject. |
| `allow_edit` | boolean | no | Human may return `edit` with `edited_args`. Requires `action`. |
| `allow_comment` | boolean | no |  |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpire](#schema-OnExpire) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

Unknown properties are refused.

### ConfirmOption

A confirm option: `decision` is required and must be approve or reject.

| Property | Type | Required | Description |
|---|---|---|---|
| `id` | string, at most 64 characters, matching `^[A-Za-z0-9_.-]+$` | yes |  |
| `label` | string, at most 40 characters | yes |  |
| `description` | string, at most 140 characters | no |  |
| `style` | string, one of `"default"`, `"primary"`, `"destructive"` | no |  |
| `decision` | string, one of `"approve"`, `"reject"` | yes | Confirm options are only approve or reject. |
| `grant` | string, one of `"once"`, `"session"` | no | Only on an approve option: offers a scoped grant (e.g. Allow for this session). |

Unknown properties are refused.

### OnExpire

Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true.

| Property | Type | Required | Description |
|---|---|---|---|
| `decision` | string, one of `"reject"`, `"respond"`, `"ignore"` | yes |  |
| `option` | string | no |  |
| `options` | array of string, at most 12 items | no |  |
| `text` | string, at most 4000 characters | no |  |
| `values` | [FormValues](#schema-FormValues) | no |  |

Unknown properties are refused.

### FormValues



### FormValue

Type: string, at most 4000 characters or number or boolean or array of (string, at most 100 characters), at most 50 items.

### AskChoice

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"choice"` | yes |  |
| `options` | array of [Option](#schema-Option), 2 to 12 items | yes |  |
| `multiple` | boolean | no |  |
| `min` | integer, ≥ 0 | no |  |
| `max` | integer, from 1 to 12 | no |  |
| `allow_other` | boolean | no | Human may type an answer instead (goes to `text`). |
| `allow_comment` | boolean | no |  |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `quiz` | [QuizCreate](#schema-QuizCreate) | no | Quiz mode on create. `correct` is stored server-side and never sent to a device before the reveal point. |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpire](#schema-OnExpire) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

Unknown properties are refused.

### Option

Option ids are unique within an ask. When confirm `options` is omitted, Kabu uses approve (decision approve) and deny (decision reject).

| Property | Type | Required | Description |
|---|---|---|---|
| `id` | string, at most 64 characters, matching `^[A-Za-z0-9_.-]+$` | yes |  |
| `label` | string, at most 40 characters | yes |  |
| `description` | string, at most 140 characters | no |  |
| `style` | string, one of `"default"`, `"primary"`, `"destructive"` | no |  |
| `decision` | string, one of `"approve"`, `"reject"`, `"ignore"`, `"respond"` | no | What choosing this option means. Required on confirm options; choice options are always respond. |
| `grant` | string, one of `"once"`, `"session"` | no | Only on an approve option: offers a scoped grant (e.g. Allow for this session). |

Unknown properties are refused.

### QuizCreate

Quiz mode on create. `correct` is stored server-side and never sent to a device before the reveal point.

| Property | Type | Required | Description |
|---|---|---|---|
| `correct` | array of string, at least 1 items, unique items | yes |  |
| `reveal` | string, one of `"never"`, `"after_answer"` | no |  |
| `ask_confidence` | boolean | no |  |

Unknown properties are refused.

### AskText

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"text"` | yes |  |
| `placeholder` | string, at most 80 characters | no |  |
| `suggestions` | array of (string, at most 80 characters), at most 6 items | no |  |
| `max_length` | integer, from 1 to 4000 | no |  |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpire](#schema-OnExpire) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

Unknown properties are refused.

### AskForm

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"form"` | yes |  |
| `schema` | [FormSchema](#schema-FormSchema) | yes | A flat form: the MCP elicitation subset, ≤ 10 fields, no $ref. `required` names must be declared properties. |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpire](#schema-OnExpire) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

Unknown properties are refused.

### FormSchema

A flat form: the MCP elicitation subset, ≤ 10 fields, no $ref. `required` names must be declared properties.

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"object"` | yes |  |
| `properties` | object, 1 to 10 keys | yes |  |
| `required` | array of string, unique items | no |  |

Unknown properties are refused.

### FormField

One field of the MCP 2026-07-28 elicitation subset. `array` is only a multi-select of string enums.

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | string, one of `"string"`, `"number"`, `"integer"`, `"boolean"`, `"array"` | yes |  |
| `title` | string, at most 60 characters | no |  |
| `description` | string, at most 200 characters | no |  |
| `format` | string, one of `"email"`, `"uri"`, `"date"`, `"date-time"` | no |  |
| `enum` | array of (string, at most 100 characters), at most 50 items, unique items | no |  |
| `enumNames` | array of (string, at most 100 characters), at most 50 items | no |  |
| `items` | [FormArrayItems](#schema-FormArrayItems) | no |  |
| `minLength` | integer, ≥ 0 | no |  |
| `maxLength` | integer, from 1 to 4000 | no |  |
| `minimum` | number | no |  |
| `maximum` | number | no |  |
| `minItems` | integer, ≥ 0 | no |  |
| `maxItems` | integer, from 1 to 50 | no |  |
| `default` | [FormValue](#schema-FormValue) | no |  |

Unknown properties are refused.

### FormArrayItems

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"string"` | yes |  |
| `enum` | array of (string, at most 100 characters), 1 to 50 items, unique items | yes |  |

Unknown properties are refused.

### AskMulti

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"multi"` | yes |  |
| `options` | array of [Option](#schema-Option), 2 to 12 items | yes |  |
| `min` | integer, ≥ 0 | no |  |
| `max` | integer, from 1 to 12 | no |  |
| `allow_comment` | boolean | no |  |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpire](#schema-OnExpire) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

Unknown properties are refused.

### Block

Type: [CodeBlock](#schema-CodeBlock) or [DiffBlock](#schema-DiffBlock) or [FieldsBlock](#schema-FieldsBlock) or [QuoteBlock](#schema-QuoteBlock).

Chosen by `type`: `"code"`: [CodeBlock](#schema-CodeBlock), `"diff"`: [DiffBlock](#schema-DiffBlock), `"fields"`: [FieldsBlock](#schema-FieldsBlock), `"quote"`: [QuoteBlock](#schema-QuoteBlock).

### CodeBlock

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"code"` | yes |  |
| `language` | string, at most 30 characters | no |  |
| `text` | string, at most 8000 characters | yes |  |

Unknown properties are refused.

### DiffBlock

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"diff"` | yes |  |
| `path` | string, at most 300 characters | no |  |
| `text` | string, at most 16000 characters | yes |  |

Unknown properties are refused.

### FieldsBlock

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"fields"` | yes |  |
| `fields` | array of [Field](#schema-Field), at most 12 items | yes |  |

Unknown properties are refused.

### QuoteBlock

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"quote"` | yes |  |
| `text` | string, at most 2000 characters | yes |  |

Unknown properties are refused.

### AttachmentRef

| Property | Type | Required | Description |
|---|---|---|---|
| `media` | string | yes | A `med_…` id. |
| `caption` | string, at most 200 characters | no |  |

Unknown properties are refused.

### Link

| Property | Type | Required | Description |
|---|---|---|---|
| `label` | string, at most 40 characters | yes |  |
| `url` | string (uri), at most 2048 characters | yes |  |

Unknown properties are refused.

### Voice

| Property | Type | Required | Description |
|---|---|---|---|
| `speak` | boolean | no |  |
| `text` | string, at most 12000 characters | no |  |
| `voice` | string, at most 60 characters | no |  |
| `speed` | number, from 0.5 to 2 | no |  |

Unknown properties are refused.

### Appearance

Cosmetics only. Resolved org → project → agent → recipient, then this override; recipient restrictions always win.

| Property | Type | Required | Description |
|---|---|---|---|
| `accent` | [Accent](#schema-Accent) | no | Open enum. Known values are `AccentKnown`; accept and ignore unknown values. |
| `sound` | string, at most 40 characters | no | A registry name from `GET /v2` (e.g. chime). Recipient settings may override. |
| `icon` | string | no | A `med_…` image with purpose avatar. |

Unknown properties are refused.

### Accent

Open enum. Known values are `AccentKnown`; accept and ignore unknown values.

Type: [AccentKnown](#schema-AccentKnown) or string.

### AccentKnown

A palette token (rendered with each platform's accessible colours), never a raw colour.

Type: string, one of `"neutral"`, `"blue"`, `"green"`, `"amber"`, `"red"`, `"purple"`, `"teal"`, `"pink"`, `"orange"`, `"indigo"`, `"mint"`, `"brown"`, `"slate"`.

### Metadata

Your own key/value pairs (≤ 50 keys, values ≤ 500 chars), returned verbatim on every object and event.

### Correlation

Runtime correlation ids (LangGraph, OpenAI Agents, MCP Tasks, A2A, AG-UI). Opaque to Kabu.

| Property | Type | Required | Description |
|---|---|---|---|
| `framework` | string, at most 40 characters | no |  |
| `thread_id` | string | no |  |
| `run_id` | string | no |  |
| `interrupt_id` | string | no |  |
| `tool_call_id` | string | no |  |
| `task_id` | string | no |  |

### Extensions

Reverse-domain namespaced extension data (e.g. `com.example.ticket`), ≤ 8 KiB total. Cannot change core semantics.

### Message

One object for notify, ask and steer. Never contains answer links. On human routes (user token, answer link, delegate) Kabu returns the recipient projection: `to` lists only the caller, metadata is `{}`, and correlation, extensions and quiz answers before the reveal point are null. For a recipient who did not respond, `resolution` is null (status and settled_at still show the ask is settled), so no recipient learns who else was asked or what they answered; the responding human, the creator and integrators see the full resolution and receipt.

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"message"` | yes |  |
| `id` | string | yes |  |
| `livemode` | boolean | yes |  |
| `project` | string | yes |  |
| `version` | integer | yes | Monotonic per message; increases on every state change. Events carry it. |
| `kind` | [MessageKind](#schema-MessageKind) | yes | Open enum. Known values are `MessageKindKnown`; accept and ignore unknown values. |
| `status` | [MessageStatus](#schema-MessageStatus) | yes | Open enum. Known values are `MessageStatusKnown`; accept and ignore unknown values. |
| `sender` | [Actor](#schema-Actor) | yes |  |
| `to` | array of string | yes |  |
| `session` | string or null | yes |  |
| `agent` | string or null | yes |  |
| `category` | string or null | yes |  |
| `priority` | [Priority](#schema-Priority) | yes | Open enum. Known values are `PriorityKnown`; accept and ignore unknown values. |
| `title` | string or null | yes |  |
| `body` | string or null | yes |  |
| `message` | string | no |  |
| `source` | string | no |  |
| `fields` | array of [FieldView](#schema-FieldView) | no |  |
| `blocks` | array of [BlockView](#schema-BlockView) | yes |  |
| `attachments` | array of [Media](#schema-Media) | yes |  |
| `links` | array of [LinkView](#schema-LinkView) | yes |  |
| `action` | [ActionView](#schema-ActionView) or null | yes |  |
| `ask` | [AskView](#schema-AskView) or null | yes |  |
| `control` | string or null | yes |  |
| `expires_at` | [Timestamp](#schema-Timestamp) or null | yes |  |
| `settled_at` | [Timestamp](#schema-Timestamp) or null | yes |  |
| `resolution` | [Resolution](#schema-Resolution) or null | yes |  |
| `ack` | [SteerAck](#schema-SteerAck) or null | yes |  |
| `delivery` | [Delivery](#schema-Delivery) | yes |  |
| `appearance` | [AppearanceView](#schema-AppearanceView) or null | yes |  |
| `voice` | [VoiceView](#schema-VoiceView) or null | yes |  |
| `sensitive` | boolean | yes |  |
| `digest` | string or null | yes |  |
| `metadata` | [Metadata](#schema-Metadata) | yes | Your own key/value pairs (≤ 50 keys, values ≤ 500 chars), returned verbatim on every object and event. |
| `correlation` | [Correlation](#schema-Correlation) or null | yes |  |
| `extensions` | [Extensions](#schema-Extensions) or null | yes |  |
| `traceparent` | string or null | yes |  |
| `created_at` | [Timestamp](#schema-Timestamp) | yes |  |
| `updated_at` | [Timestamp](#schema-Timestamp) | yes |  |

### MessageKind

Open enum. Known values are `MessageKindKnown`; accept and ignore unknown values.

Type: [MessageKindKnown](#schema-MessageKindKnown) or string.

### MessageKindKnown

What a message is.

Type: string, one of `"notify"`, `"ask"`, `"steer"`.

### MessageStatus

Open enum. Known values are `MessageStatusKnown`; accept and ignore unknown values.

Type: [MessageStatusKnown](#schema-MessageStatusKnown) or string.

### MessageStatusKnown

`sent` for notify; `pending → answered | expired | cancelled` for ask; `queued → leased → received → applied | rejected | uncertain` for steer; `stop` marks still-queued instructions `cancelled`.

Type: string, one of `"sent"`, `"pending"`, `"answered"`, `"expired"`, `"cancelled"`, `"queued"`, `"leased"`, `"received"`, `"applied"`, `"rejected"`, `"uncertain"`.

### Actor

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | string, one of `"agent"`, `"human"`, `"system"` or string | yes |  |
| `id` | string | yes |  |
| `name` | string or null | yes |  |

### Priority

Open enum. Known values are `PriorityKnown`; accept and ignore unknown values.

Type: [PriorityKnown](#schema-PriorityKnown) or string.

### FieldView

| Property | Type | Required | Description |
|---|---|---|---|
| `label` | string, at most 40 characters | yes |  |
| `value` | string, at most 200 characters | yes |  |

### BlockView

 Unknown future variants decode as UnknownVariant.

Type: [BlockViewKnown](#schema-BlockViewKnown) or [UnknownVariant](#schema-UnknownVariant).

### BlockViewKnown

Type: [CodeBlockView](#schema-CodeBlockView) or [DiffBlockView](#schema-DiffBlockView) or [FieldsBlockView](#schema-FieldsBlockView) or [QuoteBlockView](#schema-QuoteBlockView).

Chosen by `type`: `"code"`: [CodeBlockView](#schema-CodeBlockView), `"diff"`: [DiffBlockView](#schema-DiffBlockView), `"fields"`: [FieldsBlockView](#schema-FieldsBlockView), `"quote"`: [QuoteBlockView](#schema-QuoteBlockView).

### CodeBlockView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"code"` | yes |  |
| `language` | string, at most 30 characters | no |  |
| `text` | string, at most 8000 characters | yes |  |

### DiffBlockView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"diff"` | yes |  |
| `path` | string, at most 300 characters | no |  |
| `text` | string, at most 16000 characters | yes |  |

### FieldsBlockView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"fields"` | yes |  |
| `fields` | array of [FieldView](#schema-FieldView), at most 12 items | yes |  |

### QuoteBlockView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"quote"` | yes |  |
| `text` | string, at most 2000 characters | yes |  |

### UnknownVariant

Fallback for future ask, block or other variants: render a neutral 'Open in Kabu', never infer approval.

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | string | yes | A variant this client does not know yet. |

### Media

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"media"` | yes |  |
| `id` | string | yes |  |
| `version` | integer | yes |  |
| `livemode` | boolean | yes |  |
| `project` | string | yes |  |
| `kind` | string | yes | image, audio, video or file (open). |
| `status` | string | yes | uploading, processing, ready or failed (open). |
| `content_type` | string | yes |  |
| `filename` | string or null | yes |  |
| `size` | integer | yes |  |
| `sha256` | string | yes |  |
| `width` | integer or null | yes |  |
| `height` | integer or null | yes |  |
| `duration_ms` | integer or null | yes |  |
| `url` | string (uri) or null | yes |  |
| `url_expires_at` | [Timestamp](#schema-Timestamp) or null | yes |  |
| `thumbnail_url` | string (uri) or null | yes |  |
| `created_at` | [Timestamp](#schema-Timestamp) | yes |  |

### Timestamp

Type: string (date-time).

### LinkView

| Property | Type | Required | Description |
|---|---|---|---|
| `label` | string, at most 40 characters | yes |  |
| `url` | string (uri), at most 2048 characters | yes |  |

### ActionView

The concrete action being authorised.

| Property | Type | Required | Description |
|---|---|---|---|
| `name` | string, at most 100 characters | yes |  |
| `args` | object | no | The proposed arguments, shown to the human and bound into the digest (≤ 16 KiB). |
| `args_schema` | object | no | Optional JSON Schema (2020-12) for `edited_args`, restricted to an offline profile: local `$ref` only (`#/…`), depth ≤ 8, ≤ 8 KiB, no `$dynamicRef`, `format` assertions or `contentSchema`. Unsupported keywords are rejected with 400 unsupported_schema_keyword. |
| `description` | string, at most 2000 characters | no |  |

### AskView

An ask as returned: identical to what was sent, except a quiz is shown as a QuizView. Unknown future variants decode as UnknownVariant.

Type: [AskViewKnown](#schema-AskViewKnown) or [UnknownVariant](#schema-UnknownVariant).

### AskViewKnown

An ask as returned: identical to what was sent, except a quiz is shown as a QuizView.

Type: [AskConfirmView](#schema-AskConfirmView) or [AskChoiceView](#schema-AskChoiceView) or [AskTextView](#schema-AskTextView) or [AskFormView](#schema-AskFormView) or [AskMultiView](#schema-AskMultiView).

Chosen by `type`: `"confirm"`: [AskConfirmView](#schema-AskConfirmView), `"choice"`: [AskChoiceView](#schema-AskChoiceView), `"text"`: [AskTextView](#schema-AskTextView), `"form"`: [AskFormView](#schema-AskFormView), `"multi"`: [AskMultiView](#schema-AskMultiView).

### AskConfirmView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"confirm"` | yes |  |
| `options` | array of [ConfirmOptionView](#schema-ConfirmOptionView), 2 to 4 items | no | Each needs `decision` (approve or reject). Omit for approve / reject. |
| `allow_edit` | boolean | no | Human may return `edit` with `edited_args`. Requires `action`. |
| `allow_comment` | boolean | no |  |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpireView](#schema-OnExpireView) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

### ConfirmOptionView

A confirm option: `decision` is required and must be approve or reject.

| Property | Type | Required | Description |
|---|---|---|---|
| `id` | string, at most 64 characters, matching `^[A-Za-z0-9_.-]+$` | yes |  |
| `label` | string, at most 40 characters | yes |  |
| `description` | string, at most 140 characters | no |  |
| `style` | string, one of `"default"`, `"primary"`, `"destructive"` or string | no |  |
| `decision` | string, one of `"approve"`, `"reject"` or string | yes | Confirm options are only approve or reject. |
| `grant` | string, one of `"once"`, `"session"` or string | no | Only on an approve option: offers a scoped grant (e.g. Allow for this session). |

### OnExpireView

Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true.

| Property | Type | Required | Description |
|---|---|---|---|
| `decision` | string, one of `"reject"`, `"respond"`, `"ignore"` or string | yes |  |
| `option` | string | no |  |
| `options` | array of string, at most 12 items | no |  |
| `text` | string, at most 4000 characters | no |  |
| `values` | [FormValues](#schema-FormValues) | no |  |

### AskChoiceView

Type: object and object.

### OptionView

Option ids are unique within an ask. When confirm `options` is omitted, Kabu uses approve (decision approve) and deny (decision reject).

| Property | Type | Required | Description |
|---|---|---|---|
| `id` | string, at most 64 characters, matching `^[A-Za-z0-9_.-]+$` | yes |  |
| `label` | string, at most 40 characters | yes |  |
| `description` | string, at most 140 characters | no |  |
| `style` | string, one of `"default"`, `"primary"`, `"destructive"` or string | no |  |
| `decision` | string, one of `"approve"`, `"reject"`, `"ignore"`, `"respond"` or string | no | What choosing this option means. Required on confirm options; choice options are always respond. |
| `grant` | string, one of `"once"`, `"session"` or string | no | Only on an approve option: offers a scoped grant (e.g. Allow for this session). |

### QuizView

| Property | Type | Required | Description |
|---|---|---|---|
| `reveal` | string | yes |  |
| `ask_confidence` | boolean | yes |  |
| `correct` | array of string or null | yes |  |

### AskTextView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"text"` | yes |  |
| `placeholder` | string, at most 80 characters | no |  |
| `suggestions` | array of (string, at most 80 characters), at most 6 items | no |  |
| `max_length` | integer, from 1 to 4000 | no |  |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpireView](#schema-OnExpireView) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

### AskFormView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"form"` | yes |  |
| `schema` | [FormSchemaView](#schema-FormSchemaView) | yes | A flat form: the MCP elicitation subset, ≤ 10 fields, no $ref. `required` names must be declared properties. |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpireView](#schema-OnExpireView) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

### FormSchemaView

A flat form: the MCP elicitation subset, ≤ 10 fields, no $ref. `required` names must be declared properties.

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"object"` | yes |  |
| `properties` | object, 1 to 10 keys | yes |  |
| `required` | array of string, unique items | no |  |

### FormFieldView

One field of the MCP 2026-07-28 elicitation subset. `array` is only a multi-select of string enums.

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | string, one of `"string"`, `"number"`, `"integer"`, `"boolean"`, `"array"` or string | yes |  |
| `title` | string, at most 60 characters | no |  |
| `description` | string, at most 200 characters | no |  |
| `format` | string, one of `"email"`, `"uri"`, `"date"`, `"date-time"` or string | no |  |
| `enum` | array of (string, at most 100 characters), at most 50 items, unique items | no |  |
| `enumNames` | array of (string, at most 100 characters), at most 50 items | no |  |
| `items` | [FormArrayItemsView](#schema-FormArrayItemsView) | no |  |
| `minLength` | integer, ≥ 0 | no |  |
| `maxLength` | integer, from 1 to 4000 | no |  |
| `minimum` | number | no |  |
| `maximum` | number | no |  |
| `minItems` | integer, ≥ 0 | no |  |
| `maxItems` | integer, from 1 to 50 | no |  |
| `default` | [FormValue](#schema-FormValue) | no |  |

### FormArrayItemsView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"string"` | yes |  |
| `enum` | array of (string, at most 100 characters), 1 to 50 items, unique items | yes |  |

### AskMultiView

| Property | Type | Required | Description |
|---|---|---|---|
| `type` | `"multi"` | yes |  |
| `options` | array of [OptionView](#schema-OptionView), 2 to 12 items | yes |  |
| `min` | integer, ≥ 0 | no |  |
| `max` | integer, from 1 to 12 | no |  |
| `allow_comment` | boolean | no |  |
| `allow_reply` | boolean | no |  |
| `expires_in` | integer, from 60 to 2592000 | no |  |
| `irreversible` | boolean | no | Requires device authentication; never defaulted on expiry. |
| `on_expire` | [OnExpireView](#schema-OnExpireView) | no | Fail-closed default applied by the server at expiry, attributed to system. Never approve or edit: an asking agent cannot schedule its own authorisation. Rejected when `irreversible` is true. |

### Resolution

| Property | Type | Required | Description |
|---|---|---|---|
| `decision` | [Decision](#schema-Decision) | yes | Open enum. Known values are `DecisionKnown`; accept and ignore unknown values. |
| `option` | string or null | yes |  |
| `options` | array of string or null | yes |  |
| `text` | string or null | yes |  |
| `values` | [FormValues](#schema-FormValues) or null | yes |  |
| `comment` | string or null | yes |  |
| `edited_args` | object or null | yes |  |
| `grant` | [Grant](#schema-Grant) or null | yes |  |
| `correct` | boolean or null | yes |  |
| `confidence` | integer, from 1 to 5 or null | yes |  |
| `source` | string | yes | human or system (open). |
| `via` | [Via](#schema-Via) | yes | Open enum. Known values are `ViaKnown`; accept and ignore unknown values. |
| `assurance` | [Assurance](#schema-Assurance) | yes | Open enum. Known values are `AssuranceKnown`; accept and ignore unknown values. |
| `responded_by` | [Actor](#schema-Actor) | yes |  |
| `asserted_by` | string or null | yes |  |
| `responded_at` | [Timestamp](#schema-Timestamp) | yes |  |
| `digest` | string | yes | sha256 of the RFC 8785 canonical request (see ReceiptClaims.req_digest). |
| `receipt` | string | yes | Compact JWS, ES256, whose payload is ReceiptClaims. Verify with /.well-known/jwks.json. |

### Decision

Open enum. Known values are `DecisionKnown`; accept and ignore unknown values.

Type: [DecisionKnown](#schema-DecisionKnown) or string.

### DecisionKnown

What a resolution means. Never infer approval from an unknown value.

Type: string, one of `"approve"`, `"reject"`, `"edit"`, `"respond"`, `"ignore"`.

### Grant

A scoped grant chosen by the human (once or session), bound to agent, session and action.

| Property | Type | Required | Description |
|---|---|---|---|
| `scope` | string | yes |  |
| `agent` | string or null | yes |  |
| `session` | string or null | yes |  |
| `action_digest` | string or null | yes |  |
| `expires_at` | [Timestamp](#schema-Timestamp) or null | yes |  |

### Via

Open enum. Known values are `ViaKnown`; accept and ignore unknown values.

Type: [ViaKnown](#schema-ViaKnown) or string.

### ViaKnown

The surface a resolution came through.

Type: string, one of `"app"`, `"lock_screen"`, `"watch"`, `"live_activity"`, `"carplay"`, `"web"`, `"web_push"`, `"api"`, `"expiry"`.

### Assurance

Open enum. Known values are `AssuranceKnown`; accept and ignore unknown values.

Type: [AssuranceKnown](#schema-AssuranceKnown) or string.

### AssuranceKnown

How the responding human was established.

Type: string, one of `"app"`, `"biometric"`, `"link"`, `"delegated"`, `"system"`.

### SteerAck

| Property | Type | Required | Description |
|---|---|---|---|
| `status` | string | yes |  |
| `note` | string or null | yes |  |
| `consumer` | string or null | yes |  |
| `at` | [Timestamp](#schema-Timestamp) | yes |  |

### Delivery

| Property | Type | Required | Description |
|---|---|---|---|
| `status` | string | yes | queued, sent, delivered, read, failed, suppressed or digested (open). |
| `channels` | array of [ChannelDelivery](#schema-ChannelDelivery) | yes |  |
| `reason` | string or null | yes |  |

### ChannelDelivery

| Property | Type | Required | Description |
|---|---|---|---|
| `channel` | [Channel](#schema-Channel) | yes | Open enum. Known values are `ChannelKnown`; accept and ignore unknown values. |
| `status` | string | yes |  |
| `at` | [Timestamp](#schema-Timestamp) | no |  |

### Channel

Open enum. Known values are `ChannelKnown`; accept and ignore unknown values.

Type: [ChannelKnown](#schema-ChannelKnown) or string.

### ChannelKnown

A delivery channel.

Type: string, one of `"ios"`, `"android"`, `"watch"`, `"live_activity"`, `"web_push"`, `"web"`.

### AppearanceView

Cosmetics only. Resolved org → project → agent → recipient, then this override; recipient restrictions always win.

| Property | Type | Required | Description |
|---|---|---|---|
| `accent` | [Accent](#schema-Accent) | no | Open enum. Known values are `AccentKnown`; accept and ignore unknown values. |
| `sound` | string, at most 40 characters | no | A registry name from `GET /v2` (e.g. chime). Recipient settings may override. |
| `icon` | string | no | A `med_…` image with purpose avatar. |

### VoiceView

| Property | Type | Required | Description |
|---|---|---|---|
| `speak` | boolean | no |  |
| `text` | string, at most 12000 characters | no |  |
| `voice` | string, at most 60 characters | no |  |
| `speed` | number, from 0.5 to 2 | no |  |

### MessageList

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"list"` | yes |  |
| `data` | array of [Message](#schema-Message) | yes |  |
| `has_more` | boolean | yes |  |
| `next_cursor` | string or null | yes |  |

### MessagePreview

| Property | Type | Required | Description |
|---|---|---|---|
| `object` | `"message_preview"` | yes |  |
| `push` | object | yes |  |
| `push.title` | string | yes |  |
| `push.subtitle` | string or null | yes |  |
| `push.body` | string or null | yes |  |
| `live_activity` | object or null | yes |  |
| `voice_text` | string or null | yes |  |
| `resolved_to` | array of string | yes |  |
| `warnings` | array of string | yes | e.g. a 3-option confirm renders as Review on Live Activity. |

### MessageCancel

| Property | Type | Required | Description |
|---|---|---|---|
| `reason` | string, at most 200 characters | no |  |

Unknown properties are refused.
