Skip to main content
Version: 3.5.0-rc.3

Messages API

Use POST /v1/messages to call a chat model with the Anthropic Messages request and response shapes. The endpoint works with direct Anthropic models and with models served through the OpenAI Responses protocol. The model you choose decides the backend protocol; the endpoint you call decides the shape of the request and response.

This version supports text conversations, as one response or as a stream. Tools, images and documents, and most optional settings are not available yet. A request that uses one of them is rejected before it reaches the model.

Before you begin​

You need:

  • the API URL shown in the platform portal
  • an API key with invoke access to a chat model
  • the model ID from GET /v1/models

Authenticate with your platform API key as a bearer token: Authorization: Bearer <key>. The x-api-key header is not supported. For an Anthropic model, the gateway sends the connector's Anthropic key; your platform key never reaches Anthropic.

curl -sS "$API_BASE_URL/v1/messages" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "YOUR_MODEL_ID",
"max_tokens": 1024,
"system": "Answer in one sentence.",
"messages": [{"role": "user", "content": "What is the capital of France?"}]
}'

With the Anthropic Python SDK, set the base URL to the API URL without /v1 and pass the key as an auth token:

import anthropic

client = anthropic.Anthropic(base_url="https://api.<your-domain>", auth_token="cm_api_…")
message = client.messages.create(
model="YOUR_MODEL_ID",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(message.content[0].text)

Supported request​

MemberSupport
modelRequired. A model or model alias ID. Preset agents are not supported on this endpoint.
max_tokensRequired integer. null is rejected. 0 is sent to the model as given.
messagesRequired and not empty. Roles user and assistant. A final assistant message (a prefill) returns 400 unless the model is an Anthropic model.
systemA string, or a list of text blocks joined with a blank line.
temperature, top_pSent to the model when set.
streamtrue or false. See Streaming.

Message content is a string or a list of blocks. User messages accept text blocks. Assistant messages accept text, thinking and redacted_thinking blocks, so you can send back earlier answers, including their thinking.

A request that ends with an assistant message asks the model to continue that message. Models served through the Responses protocol cannot continue a message, so the gateway rejects the request instead of returning a new answer. For an Anthropic model the gateway sends the request as given, and the model decides; current Claude models reject a prefilled final assistant message.

Any other request member, including tools, tool_choice, thinking, top_k, stop_sequences, metadata and output_config, returns 400. So does any other block type, such as image, document, tool_use or tool_result, and any cache_control or citations on a block. The error message names what is not supported.

Response​

The response is a Messages message object:

{
"id": "msg_…",
"type": "message",
"role": "assistant",
"model": "YOUR_MODEL_ID",
"content": [{"type": "text", "text": "Paris is the capital of France."}],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 21,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 9
}
}

model is the model ID you sent. stop_reason is one of:

Stop reasonMeaning
end_turnThe model finished its answer.
max_tokensThe answer reached max_tokens. content holds the partial answer.
refusalThe model refused. content holds any text the model wrote before it refused, then the refusal explanation as a text block when the model gives one.

Any other ending, such as a model failure or a stop reason this API cannot represent, returns an error instead of a message.

usage.input_tokens counts input that was not read from or written to the prompt cache. Cache writes and reads are reported separately; your usage accounting counts all three as input.

Streaming​

Set "stream": true to receive the message as server-sent events, in the Messages streaming format:

  1. message_start with an empty message. Its usage counts are placeholders, all 0.
  2. For each content block, content_block_start, its content_block_delta events and content_block_stop. index counts the blocks of the response. Text arrives as text_delta, thinking as thinking_delta followed by one signature_delta. A redacted_thinking block arrives whole in its content_block_start.
  3. message_delta with stop_reason and the final usage, totaled over the whole request and equal to the unary response's, then message_stop.

With the Anthropic Python SDK, client.messages.stream() sends the request with stream set and accumulates the events:

with client.messages.stream(
model="YOUR_MODEL_ID",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
) as stream:
for text in stream.text_stream:
print(text, end="")
message = stream.get_final_message()

The gateway sends ping events while the model is silent. The blocks, stop reason and usage you accumulate from the stream equal the unary response for the same answer, with one exception. When a Responses model sends a reasoning item only in its finished response, after answer text has already streamed, the stream delivers that thinking block after the text, while the unary response keeps the model's order. The block's thinking and signature are the same in both. The gateway has not confirmed that a model accepts the reasoning when it is sent back in this changed order.

With an Anthropic model, the gateway learns that a block is complete only when the model's message ends. Block starts and deltas arrive as the model produces them, but every block's signature_delta and content_block_stop arrive after the last block has started, just before message_delta. Deltas for an earlier block can therefore follow the start of a later one. Accumulate deltas by index, as the Anthropic SDKs do. When an Anthropic model refuses, the stream and the unary response both hold the text written before the refusal. Thinking already streamed also stays in the stream, without a signature, while the unary response leaves it out. The TypeScript SDK's per-event callbacks, such as signature and contentBlock, can name the wrong block in this order, but finalMessage() and the accumulated message are correct.

Errors found before the stream starts, such as an invalid request or a denied model, return an ordinary JSON error with its HTTP status. An error after the stream has started ends it with one error event carrying the same error object, and no message_stop. The stream starts before the gateway calls the model, so when the model service rejects the request, for example with 429, the stream ends with an error event: the HTTP status is already 200, and no Retry-After header is sent. If the client disconnects, the gateway stops the model request.

Conversation history​

The endpoint is stateless. It stores no conversation and creates no conversation items. To continue a conversation, send the earlier messages again, including assistant content exactly as you received it.

Assistant thinking and redacted_thinking blocks must be sent back unchanged.

Thinking from Responses models​

Messages has no field for the reasoning items that OpenAI Responses models return. The gateway returns each one as a thinking block: its thinking is the reasoning summary followed by the reasoning text, separated by blank lines, and its signature starts with cmgw. and carries the reasoning item. Send the block back unchanged; the gateway restores the original reasoning item for the model. A cmgw. signature is not an Anthropic signature: sending it to an Anthropic model returns 400. A malformed cmgw. signature also returns 400.

Errors​

Errors use the Messages error shape:

{"type": "error", "error": {"type": "invalid_request_error", "message": "max_tokens is required"}}
Statuserror.type
400invalid_request_error
401authentication_error
403permission_error
404not_found_error
413request_too_large
429rate_limit_error
529overloaded_error
Otherapi_error

A missing or invalid credential is rejected before the request reaches the gateway and does not use the Messages error shape: a missing or unrecognized credential returns 403, and a recognized API key that is invalid returns 401.

When the model service rejects a request that does not stream, the gateway returns its status with the public message described in Model backend errors, and forwards a Retry-After header. Provider messages are not returned.

When the model service fails after it started answering, the gateway returns the same public message. An invalid request, including one too long for the model, returns 400; an authentication failure 401; a permission failure 403; and a rate limit 429, without a Retry-After header. Any other failure returns 502 with api_error.