Skip to content
docs/messagesCore API

Messages

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

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
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-sonnet-4",
  max_tokens: 512,
  system: "Answer in one sentence.",
  messages: [{ role: "user", content: "Describe harbour fog." }],
});

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

POST/v1/messagesAvailable

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

AUTHENTICATION
Bearer token, Authorization header
REQUIRED SCOPE
api-key

Request parameters

PARAMETERTYPEREQUIREDDESCRIPTION
modelstringrequiredA slug from GET /v1/models. Anthropic/claude-sonnet-4, not a vendor date-stamped id.
messagesarrayrequiredAnthropic's turns: role user or assistant, content a string or an array of blocks.
max_tokensintegerrequiredRequired here as it is in Anthropic's own API. There is no default to fall back on.
systemstringoptionalThe system prompt, sent apart from the turns rather than as one of them.
streambooleanoptionalEmit Anthropic's event framing. Default false.
temperaturenumberoptional0 to 1: Anthropic's range, not the 0 to 2 chat completions accepts. Above 1 is 400 invalid_request.
top_pnumberoptional0 to 1. Forwarded unchanged.
top_kintegeroptionalForwarded. Models that do not offer it ignore it.
stop_sequencesstring[]optionalUp to 8 strings that end generation when produced.
toolsarrayoptionalTool definitions in Anthropic's schema. Read the limits below before relying on these.
tool_choiceobjectoptionalAnthropic's tool selection, forwarded with the tools. Bear the tool_use limit below in mind.
metadataobjectoptionalAccepted 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 FIELDWHAT HAPPENS TO IT
systemBecomes a system turn at the head of the message list, rather than staying beside it.
messagesRoles and content carry over one for one. A string content becomes the turn's text; a block array is flattened into it.
max_tokensThe output ceiling, and the size of the reservation.
stop_sequencesThe stop list.
temperature, top_p, top_kForwarded under the same names to whatever serves the call.
metadataCarried with the request; it does not change routing or price.

How a generation ends

WHY IT STOPPEDstop_reason
The model finished its answerend_turn
It hit your max_tokensmax_tokens
It produced one of your stop_sequencesstop_sequence, with stop_sequence naming the string that matched

The response

JSON200 OK
{
  "id": "msg_5f81c0",
  "type": "message",
  "role": "assistant",
  "model": "anthropic/claude-sonnet-4",
  "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 }
}

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

TEXTtext/event-stream
event: message_start
data: {"type":"message_start","message":{"id":"msg_5f81c0","type":"message","role":"assistant","model":"anthropic/claude-sonnet-4","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. claude-sonnet-4-20250514 is not a model here; anthropic/claude-sonnet-4 is. 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

STATUSCODEMEANING
400invalid_requestMissing max_tokens, an empty message list, or a malformed turn
401invalid_api_keyKey missing from both x-api-key and Authorization
402insufficient_creditsRefused before any upstream call
409model_unavailableThe slug is unknown, or nothing live can serve it
503upstream_unavailableUpstream outage. Safe to retry