# Messages

> Everything an agent sends you is a message, a notification or an ask. What a message carries, who it goes to, and its lifecycle.

Everything an agent sends you is a **message**, created with one request, `POST /v2/messages`. A message is one of
two kinds:

- a **notification** (`kind: notify`) keeps you in the know: what finished, what failed, a report, a file;
- an **ask** (`kind: ask`) keeps you in the loop: the agent waits for your decision. A message with an `ask` field is
  an ask; see [Asks and answers](/docs/concepts/asks).

The only field a notification needs is `title`.

## What a message carries

| Field | What it is | Limit |
|---|---|---|
| `title` | The line your phone shows first | 120 characters |
| `body` | The rest, in Markdown: bold, italic, code, links and lists | 8,192 characters |
| `project`, `agent` | Who is asking, about what: see [Projects and agents](/docs/concepts/projects-and-agents) | 64 characters each |
| `session` | The run it belongs to: see [Sessions](/docs/concepts/sessions) | a `ses_…` id |
| `priority` | `low`, `normal` (the default), `high`, or `urgent`, which needs the `messages:urgent` scope | |
| `fields` | Key facts, as label and value | 12 |
| `blocks` | Code, a diff or a quote, shown as such | 10 |
| `attachments` | Files you uploaded first: images, audio, video, documents | 10, up to 2 GiB each |
| `links` | Buttons that open a URL: a pull request, a dashboard | 4 |
| `voice` | `{"speak": true}` has the app read the message aloud | |
| `sensitive` | Keeps the content off the Lock Screen until you unlock | |
| `expires_in` | Seconds until it expires: a notification is hidden, an ask settles as expired. Asks default to an hour | 60 to 2,592,000 (30 days) |
| `to` | Who receives it; by default, you (the key's own recipient) | 20 recipients |

The whole request is at most 64 KiB of JSON, and a field the contract does not define is refused (`400
unknown_parameter`), so a typo never goes unnoticed. The [create a message](/docs/api/messages#messages.create)
reference has every field.

## Lifecycle

A notification is `sent` once Kabu has it. An ask starts `pending` and settles exactly once:

| Status | Means |
|---|---|
| `pending` | Waiting for an answer |
| `answered` | Someone answered: read `resolution` |
| `expired` | Its deadline passed first |
| `cancelled` | The agent withdrew it (`POST /v2/messages/{message_id}/cancel`), or its session ended |

Whether a phone has shown it is a separate fact, the message's `delivery`, and never changes its `status`: an ask
stays answerable even when a push fails. Each change raises the message's `version` and emits an
[event](/docs/concepts/events).

## Before sending

`POST /v2/messages/preview` takes the same body, checks it and returns what each surface would show, without creating
anything. `kabu send --dry-run` and `kabu ask --dry-run` call it.
