# Messages

> The Anthropic Messages API, so an Anthropic client works against this base URL unchanged.

Section: Core API. Canonical page: https://routehook.ai/docs/messages

`POST /v1/messages` accepts Anthropic's request and answers in Anthropic's response shape. Construct an Anthropic SDK client with a new base URL and your key, change the model to a slug from `GET /v1/models`, and the rest of the integration is untouched, including the streaming event names, which is the part an Anthropic client is strictest about.

## An Anthropic client

**JavaScript**

```javascript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://api.routehook.ai",
  apiKey: process.env.ROUTEHOOK_API_KEY,
});

const message = await client.messages.create({
  model: "anthropic/claude-fable-5",
  max_tokens: 512,
  system: "Answer in one sentence.",
  messages: [{ role: "user", content: "Describe harbour fog." }],
});

console.log(message.content[0].text);
```

**Python**

```python
import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.routehook.ai",
    api_key=os.environ["ROUTEHOOK_API_KEY"],
)

message = client.messages.create(
    model="anthropic/claude-fable-5",
    max_tokens=512,
    system="Answer in one sentence.",
    messages=[{"role": "user", "content": "Describe harbour fog."}],
)

print(message.content[0].text)
```

**cURL**

```bash
curl https://api.routehook.ai/v1/messages \
  -H "x-api-key: $ROUTEHOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-fable-5",
    "max_tokens": 512,
    "system": "Answer in one sentence.",
    "messages": [{ "role": "user", "content": "Describe harbour fog." }]
  }'
```

> **Warning: The base URL has no version prefix**
>
> An Anthropic client appends `/v1/messages` to whatever base URL you give it, so it wants https://api.routehook.ai. Pass it https://api.routehook.ai/v1 and the request goes to /v1/v1/messages and 404s. An OpenAI client is the opposite. It wants the prefix. Two clients, two strings; this is the mistake to check first when a working key returns a 404.

## POST /v1/messages

Anthropic's Messages API, so an Anthropic client works against this base URL unchanged.

Status: Available. Authentication: any live API key on the account.

## Request parameters

| PARAMETER | TYPE | REQUIRED | DESCRIPTION |
| --- | --- | --- | --- |
| model | string | required | A slug from GET /v1/models. Anthropic/claude-sonnet-4, not a vendor date-stamped id. |
| messages | array | required | Anthropic's turns: role user or assistant, content a string or an array of blocks. |
| max_tokens | integer | required | Required here as it is in Anthropic's own API. There is no default to fall back on. |
| system | string | optional | The system prompt, sent apart from the turns rather than as one of them. |
| stream | boolean | optional | Emit Anthropic's event framing. Default false. |
| temperature | number | optional | 0 to 1: Anthropic's range, not the 0 to 2 chat completions accepts. Above 1 is 400 invalid_request. |
| top_p | number | optional | 0 to 1. Forwarded unchanged. |
| top_k | integer | optional | Forwarded. Models that do not offer it ignore it. |
| stop_sequences | string[] | optional | Up to 8 strings that end generation when produced. |
| tools | array | optional | Tool definitions in Anthropic's schema. Read the limits below before relying on these. |
| tool_choice | object | optional | Anthropic's tool selection, forwarded with the tools. Bear the tool_use limit below in mind. |
| metadata | object | optional | Accepted and carried with the request. |

## max_tokens is required

Unlike `POST /v1/chat/completions`, this route will not run without `max_tokens`. That is Anthropic's rule and it is kept, because a client that relies on the requirement would otherwise start issuing unbounded generations the moment it pointed here. Omitting it is `400 invalid_request`, before any upstream call is made and before any credit is reserved. It is also the ceiling the pre-flight reservation is sized on, so an honest number costs you less headroom than a large round one.

## The translation

The request is translated into the gateway's internal chat request, routed through exactly the same chain as `POST /v1/chat/completions` (the same routing, the same failover, the same reservation and settlement), and translated back on the way out. Nothing about routing or billing is different; only the shapes at either end are.

| ANTHROPIC FIELD | WHAT HAPPENS TO IT |
| --- | --- |
| system | Becomes a system turn at the head of the message list, rather than staying beside it. |
| messages | Roles and content carry over one for one. A string content becomes the turn's text; a block array is flattened into it. |
| max_tokens | The output ceiling, and the size of the reservation. |
| stop_sequences | The stop list. |
| temperature, top_p, top_k | Forwarded under the same names to whatever serves the call. |
| metadata | Carried with the request; it does not change routing or price. |

## How a generation ends

| WHY IT STOPPED | stop_reason |
| --- | --- |
| The model finished its answer | end_turn |
| It hit your max_tokens | max_tokens |
| It produced one of your stop_sequences | stop_sequence, with stop_sequence naming the string that matched |

## The response

**200 OK**

```json
{
  "id": "msg_5f81c0",
  "type": "message",
  "role": "assistant",
  "model": "anthropic/claude-fable-5",
  "content": [{ "type": "text", "text": "Fog swallowed the cranes one by one." }],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": { "input_tokens": 9, "output_tokens": 12 }
}
```

> **Note: model on the way back is our slug**
>
> The `model` field in the response is the slug you asked for, never the id the upstream knows it by. A model with several targets behind it would otherwise report a different id depending on which was healthy that minute, and your own logging and cost attribution key off that field. Nothing on the response names what actually served the call.

## The key goes in x-api-key

An Anthropic SDK sends the key as `x-api-key`, and this route accepts it there, which is what lets a client be constructed with nothing but a new base URL. It is the same key and the same account as a bearer token; `Authorization: Bearer sk_live_…` works here too if you are calling by hand. Every other route on the platform reads the `Authorization` header only.

## Streaming

`stream: true` emits Anthropic's framing, not OpenAI's: named events, each written as an `event:` line followed by its `data:` line. There is no `[DONE]` sentinel: `message_stop` ends the stream. The events come in one order: `message_start`, then `content_block_start`, a run of `content_block_delta` frames carrying `text_delta`, `content_block_stop`, then `message_delta` with the final `stop_reason` and output token count, then `message_stop`.

## The event stream

**text/event-stream**

```text
event: message_start
data: {"type":"message_start","message":{"id":"msg_5f81c0","type":"message","role":"assistant","model":"anthropic/claude-fable-5","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":9,"output_tokens":0}}}

event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Fog"}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" swallowed the cranes"}}

event: content_block_stop
data: {"type":"content_block_stop","index":0}

event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":12}}

event: message_stop
data: {"type":"message_stop"}
```

## What is not supported

The compatibility is the Messages route and its translation, and nothing wider. Everything below is a real gap, not an omission from this page. Check it against your integration before you switch a base URL.

- **Model ids are ours.** Anthropic's own `claude-sonnet-4-20250514` is not a model here — send a slug this catalogue publishes, such as `anthropic/claude-fable-5`. An id this catalogue does not know is `409 model_unavailable`, which is the one line of a ported integration you will always have to change.
- **Content blocks come back as text.** The response translation produces `text` blocks, and a stream emits `text_delta` and nothing else. `tools` are accepted and forwarded to the model, but a reply that is a tool call is not re-framed as a `tool_use` block, if your integration depends on `tool_use`, use `POST /v1/chat/completions`, where tool calls come back in OpenAI's shape.
- **Errors are the gateway's envelope.** Failures answer `{ error: { code, message, request_id } }` with the eight codes used everywhere else here, not Anthropic's `{ type: "error", error: { type } }`. The HTTP status is right; code that branches on Anthropic's error `type` is not.
- **No other Anthropic route exists.** Token counting, the Message Batches API, the Files API and the admin endpoints have no path here and answer 404. `POST /v1/messages` is the whole of the surface.
- **Prompt caching and extended thinking are not part of the translation.** `cache_control` on a block and `thinking` blocks are not modelled; where cached input is billed differently, the saving shows up in the charge rather than in this response shape.

## Failures

| STATUS | CODE | MEANING |
| --- | --- | --- |
| 400 | invalid_request | Missing max_tokens, an empty message list, or a malformed turn |
| 401 | invalid_api_key | Key missing from both x-api-key and Authorization |
| 402 | insufficient_credits | Refused before any upstream call |
| 409 | model_unavailable | The slug is unknown, or nothing live can serve it |
| 503 | upstream_unavailable | Upstream outage. Safe to retry |

## Related

- [Chat](/docs/chat): The OpenAI-shaped route, with tools and streaming.
- [SDKs](/docs/sdks): Which client reaches which route, and with which base URL.
- [Streaming](/docs/streaming): The other framing, used by chat and completions.
