> Keiro Labs API documentation - Messages
> Page: https://docs.keirolabs.ai/messages.html
> Markdown: https://docs.keirolabs.ai/messages.md
> Agent index: https://docs.keirolabs.ai/llms.txt
> API base URL: https://api.keirolabs.ai/v1
> Auth: send "Authorization: Bearer <api-key>" with a key created in the
> Keiro console (https://console.keirolabs.ai/api-keys). Use credentials saved by
> `keiro setup`, or resolve the key from a secret manager or an owner-only
> secret file and pass it to the client explicitly. Never read another
> provider's variable, and never put raw key material in environment
> variables, code, docs, or logs.

# Messages API

Use `POST /v1/messages` when your application already uses the Messages
request and content-block format. Keiro accepts that wire shape, routes it
through the same public eb1 service as the other completion endpoints, and
returns a Messages-style response.

## Send a message

```bash
printf 'Keiro API key: '
IFS= read -rs KEIRO_BEARER
printf '\n'

curl -sS https://api.keirolabs.ai/v1/messages \
  -H "Content-Type: application/json" \
  -d '{
    "model": "eb1-preview",
    "max_tokens": 256,
    "system": "Answer in one concise sentence.",
    "messages": [
      {
        "role": "user",
        "content": "Why are idempotency keys useful?"
      }
    ]
  }' \
  -H @- <<<"Authorization: Bearer $KEIRO_BEARER"
```

Read generated text from the text blocks in `content`. A buffered response has
this customer-visible shape:

```json
{
  "id": "msg_...",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Idempotency keys let a safe retry reuse the original result instead of running twice."
    }
  ],
  "model": "eb1-preview",
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 18,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "output_tokens": 19
  }
}
```

IDs, text, and token counts vary by request. Depend on the field layout, not
the sample values.

The three input-side fields are disjoint, matching Anthropic's native
Messages usage semantics: `input_tokens` counts the portion of the prompt
served from neither cache, `cache_read_input_tokens` counts prompt tokens
served from a prompt cache, and `cache_creation_input_tokens` counts prompt
tokens written to one. `input_tokens + cache_read_input_tokens +
cache_creation_input_tokens` equals the full prompt, so a client that sums
the three fields — Claude Code's context gauge does — reads the real context
size. All three fields are always present. On a stream, the `message_start`
usage carries zero for both cache fields — cache counts, like
`output_tokens`, are only known at the terminal `message_delta`.

**Deprecated (2026-08-08): additive-subset usage shape.** Messages usage
previously rendered `input_tokens` as the full inclusive prompt count with
the two cache fields as subsets of it. That shape is retired for all
Messages consumers; the disjoint semantics above are the contract and take
effect with the next deployment. If your integration read `input_tokens` as
the inclusive prompt total, sum `input_tokens + cache_read_input_tokens +
cache_creation_input_tokens` to recover the old value. Responses with zero
cache activity are byte-identical under both shapes.

## Supported request fields

The public Messages surface supports these top-level fields:

- `model`, `messages`, and optional top-level `system`
- `max_tokens`, `temperature`, `top_p`, and `stop_sequences`
- `stream`
- `tools`, `tool_choice`, and `parallel_tool_calls`
- `thinking` with `type` `enabled` or `disabled`; enabled thinking requires a
  positive `budget_tokens`
- `metadata`
- `idempotency_key`, with the request-header form preferred for HTTP clients

Unknown fields fail closed. Do not assume that a field from another Messages
implementation is available until it is listed here.

## Content blocks

User and assistant messages accept strings or non-empty content-block arrays.
Supported customer workflows include text, top-level user image blocks, and
the `tool_use` / `tool_result` history used for function calling.

Images are not supported inside `tool_result` content because tool results are
textual on the shared continuation contract. See [Images and vision](images-vision.md)
for supported image placement and [Tool calling](tool-calling.md) for tool
history.

## Stream a response

Set `stream` to `true` to receive Messages-style server-sent events. A normal
text stream follows this lifecycle:

1. `message_start`
2. `content_block_start`
3. one or more `content_block_delta` events
4. `content_block_stop`
5. `message_delta`, including the terminal stop reason and final usage
6. `message_stop`

When extended thinking is enabled, one or more `thinking` content blocks
arrive before the text block: a `content_block_start` with block type
`thinking`, `thinking_delta` deltas carrying the thinking text, then
`content_block_stop`, all closing before the text block opens. These blocks
carry summary text constructed by the API and are unsigned; do not round-trip
them to a provider as signed thinking blocks.

Streams are governed by documented idle and wall-clock time entitlements, not
a flat timeout. Messages requests carry no effort field; `thinking.budget_tokens`
selects the entitlement row — see
[Streaming](streaming.md#stream-lifetime-and-time-entitlements).

A failure after streaming begins arrives as an in-band `error` event with a
sanitized `error.type`, `error.code`, and `error.message`. Retriable failures
also include `error.retry_after_seconds`.

## Related pages

- [API reference](api-reference.md)
- [Streaming](streaming.md)
- [Errors](errors.md)
- [Idempotency](idempotency.md)
