Messages
POST/v1/messages
The same pipeline in the Anthropic Messages shape, for Claude Code and the Anthropic SDKs.
POST /v1/messages takes Anthropic's request shape (system, messages of content blocks, a required max_tokens, tools with input_schema) and answers in Anthropic's message object or its named-event stream. Behind it is the same pipeline as every other wire: the same routing, memory, connector tools, agent mode and allowances.
It exists so Claude Code and the Anthropic SDKs can use Memtro as their model by setting ANTHROPIC_BASE_URL to https://platform.memtro.com. Errors come back in Anthropic's shape, with a type an Anthropic client has a case for.
Example
Against https://platform.memtro.com, with your key in MEMTRO_API_KEY.
curl https://platform.memtro.com/v1/messages \
-H "x-api-key: $MEMTRO_API_KEY" \
-H "content-type: application/json" \
-d '{
"model": "auto",
"max_tokens": 1024,
"system": "Be terse.",
"messages": [{"role": "user", "content": "What changed on the Baltic account this week?"}]
}'
from anthropic import Anthropic
client = Anthropic(base_url="https://platform.memtro.com", api_key="sk-memtro-…")
message = client.messages.create(
model="auto",
max_tokens=1024,
system="Be terse.",
messages=[{"role": "user", "content": "What changed on the Baltic account this week?"}],
)
print(message.content[0].text)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ baseURL: "https://platform.memtro.com", apiKey: process.env.MEMTRO_API_KEY });
const message = await client.messages.create({
model: "auto",
max_tokens: 1024,
system: "Be terse.",
messages: [{ role: "user", content: "What changed on the Baltic account this week?" }],
});
console.log(message.content);
A non-streaming reply.
{
"id": "msg-2fJw…",
"type": "message",
"role": "assistant",
"model": "anthropic/claude-opus-5",
"content": [{ "type": "text", "text": "Two renewals and a price change." }],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": { "input_tokens": 1830, "output_tokens": 11, "cache_read_input_tokens": 2048, "cache_creation_input_tokens": 0 },
"memtro": { "mode": "chat", "routing": { "requested": "auto", "model": "anthropic/claude-opus-5", "complexity": 2, "method": "heuristic", "reason": "…", "fallbacks": ["…"], "kind": "general" } }
}
Authentication
An API key, as x-api-key (what the Anthropic SDKs send) or Authorization: Bearer.
- Every reply, errors included, carries
x-request-idandrequest-idheaders with the same id, startingmsg-. Quote it to support. - Browsers may call it from any origin: it answers CORS preflights and allows the
Authorization,x-api-keyand Anthropic version headers.
Request body
JSON, with Content-Type: application/json.
modelstringRequiredA model id or routing id; see Model ids. This wire has no default.
messagesarray of objectsRequiredThe conversation.
2 fields of
messagesrolestringRequireduserorassistant.systemanddeveloperturns are moved into the system prompt, and any other role is read asuser.contentstring or array of objectsRequiredText, or content blocks:
text;image(sourcewithtypebase64andmedia_type+data, ortypeurlandurl);tool_use(id,name,input);tool_result(tool_use_id,content,is_error). A block may carrycache_control. Unknown block types are dropped; an image URL that cannot be used is a 400.
max_tokensintegerRequiredA ceiling on the answer's tokens; a positive integer.
systemstring or array of objectsOptionalThe system prompt, as text or text blocks.
toolsarray of objectsOptionalYour own tools,
{name, description, input_schema}, returned astool_useblocks for you to run. Anthropic's server tools (any othertypethan none orcustom) are left out without an error.tool_choiceobjectOptional{"type": "auto" | "any" | "tool" | "none"}, withnamefortool.streambooleanOptionalAnswer as Anthropic's named-event stream. See Streaming.
temperaturenumberOptionalPassed to the provider.
top_pnumberOptionalPassed to the provider.
top_knumberOptionalAccepted and not forwarded.
stop_sequencesarray of stringsOptionalWhere to stop generating.
metadataobjectOptionaluser_idis accepted and ignored.thinkingobjectOptionalAnthropic's thinking request (
type,budget_tokens,display): asks for the thinking back and how hard to think.output_configobjectOptionaleffort, as current Claude Code sends it, is read asreasoning_effort.memtroobjectOptionalMemtro's options; see the
memtroobject.{"memtro": {"mode": "agent"}}gets a multi-step run.
Response
200application/json
An Anthropic message.
idstringOptionalmsg-…, the same id as therequest-idheader.type"message"OptionalAlways
message.role"assistant"OptionalAlways
assistant.modelstringOptionalThe model that was picked.
contentarray of objectsOptionalthinking(when asked for),text, and atool_useblock for each call to your tools. Memtro's own tools ran on the server; their results are in the answer.stop_reasonstringOptionalWhy it ended.
stop_sequencenullOptionalAlways null.
usageobjectOptionalinput_tokens,output_tokens,cache_read_input_tokens,cache_creation_input_tokens.memtroobjectOptionalAs on chat completions, without
answered_by; see What comes back. On a stream it rides onmessage_delta.
200text/event-stream
With stream: true: Anthropic's named events. See Streaming.
Streaming
Anthropic's event stream: message_start, then for each block a content_block_start, its content_block_delta events and a content_block_stop, then message_delta with the stop_reason and message_stop. Blocks are thinking (when asked for), text, and tool_use for calls to your tools. If the run fails after the stream has started, an error event with type api_error is sent instead and no message_stop follows.
event: message_start
data: {"type":"message_start","message":{"id":"msg-2fJw…","type":"message","role":"assistant","model":"anthropic/claude-opus-5","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"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":"Two renewals and a price change."}}
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},…}
event: message_stop
data: {"type":"message_stop"}
Errors
In Anthropic's envelope, {"type": "error", "error": {"type", "message"}}, with no code: the type is chosen from the status. See Errors on the overview.
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | The body failed validation, or another 400 from the error catalogue, such as a refused OpenAI-compatible endpoint; the message says which. |
| 401 | authentication_error | The key is missing, malformed, revoked or expired. |
| 402 | subscription_inactive | The organisation has no active subscription. |
| 404 | not_found_error | Unknown model id. |
| 429 | rate_limit_error | The month's allowance for the request's level is spent. |
| 500 and above | api_error | The provider or Memtro failed; |
Notes
Anthropic's error body has no code: the type is chosen from the status, as in the table under Errors. A 429 on this wire is an allowance, not a rate limit, whatever the type says.