Skip to main content

Messages

POST /v1/messages implements the Anthropic Messages API shape, so Anthropic SDKs and the tools built on them, most notably Claude Code, run against MindsHub with a base-URL and auth-token change. Model names resolve through the same catalog as everywhere else, so you can point an Anthropic client at any MindsHub model: gpt, kimi, gemini-flash, not just Claude.

Endpoint​

POST https://api.mindshub.ai/v1/messages, plus POST /v1/messages/count_tokens.

Anthropic SDKs take the base URL without /v1 (the client appends /v1/messages itself) and must authenticate with auth_token, not api_key. The api_key field sends an x-api-key header, which MindsHub rejects with a 401 before the request lands. Install the official client: pip install anthropic or npm install @anthropic-ai/sdk. Running Claude Code against MindsHub is covered in Coding agents.

Quick example​

import os
import anthropic

client = anthropic.Anthropic(
base_url="https://api.mindshub.ai",
auth_token=os.environ["MINDSHUB_API_KEY"], # auth_token, not api_key
)

message = client.messages.create(
model="mindshub_air",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(message.content[0].text)

The anthropic-version header your SDK sends is accepted and ignored.

Request parameters​

ParameterHandlingNotes
modeladaptedA real Claude model id is family-mapped to the catalog alias serving that family (claude-sonnet-5 -> sonnet); a bare alias resolves as-is. The response names the model that ran; mindshub_air and mindshub_blaze echo the id the caller sent.
messagesconditionalText, image, tool_use, tool_result and thinking blocks all round-trip to a Claude-family target, where is_error and block-level cache_control survive too. Against another provider each block is rebuilt into that transport's own vocabulary, and anything it has no field for — an Anthropic-only block type, a cache_control or citations marker — is dropped and named in X-MindsHub-Dropped-Content rather than forwarded into a 400.
systemconditionalString or content blocks. Blocks pass through verbatim so cache_control markers reach a Claude-family target; against another provider they are reconciled like any other content, and what that transport cannot carry is named in X-MindsHub-Dropped-Content.
streamforwardedProduces the standard Anthropic event sequence. See the module docstring for the two documented divergences (ping is sent once, and input tokens are 0 on message_start for non-Claude targets).
max_tokensconditionalClamped to the resolved model's real output ceiling, and up to its floor where the backend enforces one; either adjustment is named in X-MindsHub-Clamped-Params.
temperatureconditionalDropped on models that reject sampling params — which is every current Claude 5 model — and named in X-MindsHub-Dropped-Params.
top_pconditionalAs temperature. On a Claude model that still takes sampling params, sending it with temperature drops top_p (Anthropic rejects the pair) and names it in X-MindsHub-Dropped-Params; top_p alone is forwarded.
top_kconditionalAs temperature.
stop_sequencesconditionalForwarded where the transport has a field for them (the OpenAI Responses transport does not). A sequence that fires is reported as stop_reason 'stop_sequence' with the matched string, on Anthropic-wire targets.
output_configadaptedBoth halves are honoured and travel separately: 'effort' is clamped onto the resolved model's published ladder, and 'format' becomes a schema constraint each provider renders in its own shape. A model that cannot constrain its output at all is a 400, not prose.
thinkingconditionalForwarded to models that accept a thinking config, budget_tokens included. Reasoning bills as output tokens. Thinking blocks come back on both streamed and non-streamed turns, so a client can replay them.
toolsadaptedClient tools (input_schema) translate to every provider, strict included. Anthropic's hosted web_search*/web_fetch* map onto the platform's own web search where the target supports it. An mcp_toolset (with its mcp_servers entry) maps onto the target's own remote-MCP connector where it has one and is dropped, named in X-MindsHub-Dropped-Params, where it does not; enabled/default_config.enabled are honoured, defer_loading is dropped. Other server tools (computer_, bash_, text_editor_*, code_execution, memory) have no cross-provider meaning and are dropped. A tool schema is rewritten where the target accepts only a subset of JSON Schema; both kinds of change are named in X-MindsHub-Dropped-Content.
tool_choiceadaptedauto/any/none/tool all map. disable_parallel_tool_use is honoured, including on transports that spell it as a positive parallel_tool_calls. A forced choice a model cannot honour is downgraded to auto rather than refused (ENG-1095).
mcp_serversadaptedRemote MCP servers, Anthropic's shape (type url, url, name, authorization_token). Each must be referenced by exactly one mcp_toolset in tools, as on Anthropic; otherwise 400. Runs on the target's native connector (Claude, GPT); the authorization token is forwarded upstream and never stored or traced. This dialect has no approval concept, so tools run as the model calls them on every target.
metadataignoredAccepted and not read.
service_tierignoredOne serving tier; nothing to select.
cache_controlignoredThe top-level auto-caching form is not read. Block-level cache_control on system blocks, text/tool_result blocks and tool definitions IS honoured on Claude-family targets, and cache usage is reported in the response's usage.
containerignoredThe code-execution tool is not offered here.
inference_geoignoredInference geography is a property of the upstream account, not of a request.

This table is generated from PARAM_SUPPORT in minds/requests/anthropic_compat.py, which a unit test holds to the vendor SDK's own parameter list. The cross-API view is on the capability matrix.

Features the table can only summarize have their own guides: Tool calling (tools, tool_choice, disable_parallel_tool_use), Structured output (output_config.format), Reasoning (output_config.effort, thinking), Web search (web_search* and web_fetch* hosted tools), Prompt caching (block-level cache_control), and Images (image blocks). Server tools (computer_*, bash_*, text_editor_*, code_execution, memory) are dropped from the request; mcp_servers is ignored (Claude Code's own MCP support is client-side and unaffected); Files API references ("source": {"type": "file"}) are unsupported; the Message Batches API is not implemented and POST /v1/messages/batches returns a plain 404.

Unknown top-level request fields are accepted and ignored, not a validation error. Unknown nested content, say an unrecognized block type inside messages, can be forwarded to the provider and fail there instead.

The cross-API view of every parameter is on the capability matrix.

Model names​

The model field accepts two forms:

  • A MindsHub alias: sonnet, opus, gpt, kimi, any alias from Models. This is how you run Claude Code on a non-Claude model.
  • A real Claude model name: anything starting with claude that contains a family name (opus, sonnet, haiku, or fable; a substring match, checked in that order) maps to the corresponding alias. claude-sonnet-5, claude-sonnet-4-6-20250929, and claude-opus-5[1m] all work; version and date segments are ignored, and the family decides the alias. This is what lets Claude Code's own model picker work unmodified.

A claude… name with no recognizable family word returns 404 model_not_found; there is no silent guessing.

Request body​

The full request and response schemas, with every field described, are on the generated reference pages: POST /v1/messages and POST /v1/messages/count_tokens.

mcp_servers (Anthropic's beta remote-MCP field, paired with mcp_toolset entries in tools) is accepted on every model and runs on the target's own connector; see Remote MCP servers. The anthropic-beta: mcp-client-2025-11-20 header the Anthropic SDK sends with it is accepted and not required.

Response​

{
"id": "msg_5084f82319564e568c6e82fd8520c125",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-5",
"content": [{"type": "text", "text": "Hello!"}],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 11,
"output_tokens": 4,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
}
}
  • model names the model that served, as every endpoint does, so it can differ from the Claude id you sent. mindshub_air and mindshub_blaze echo exactly the string you sent. See Models.
  • content blocks are text, tool_use, and, on models that think by default (opus, fable), a leading thinking block with a signature. A turn that called a remote MCP server also carries mcp_tool_use / mcp_tool_result pairs ahead of the text, on Claude and GPT targets alike (non-streamed turns only). Don't assume the first block is your text. Thinking blocks come back on streamed and non-streamed turns alike, in Anthropic's required order.
  • stop_reason is one of end_turn, tool_use, max_tokens, stop_sequence, refusal. On a Claude-family target the model's own word is reported, so a stop your stop_sequences triggered comes back as stop_sequence with the matched string in stop_sequence, and a refusal carries stop_details. On other targets the upstream only tells us as much as OpenAI's finish_reason does, so stop_sequence is not distinguishable from end_turn there.
  • Beta-only stop reasons are mapped onto stable ones. model_context_window_exceeded and compaction arrive as max_tokens; treat the response as truncated. pause_turn (a server-tool loop that hit its iteration cap) is continued for you on a non-streamed turn, up to three times; if the turn is still paused after that and the response body carries the mcp_tool_use blocks the resumption needs, it is reported as pause_turn and you re-send the assistant content with the same tools, as on Anthropic. A paused turn whose blocks are not in the body (web search, or any streamed turn) still arrives as end_turn.
  • All four usage fields are always present. As in Anthropic's own API, input_tokens excludes cached tokens: the three input fields partition the prompt.

Streaming events​

"stream": true produces the standard Anthropic event sequence (message_start → ping → content_block_* → message_delta → message_stop), with three differences from Anthropic's native stream: ping is sent once, message_start reports input_tokens: 0 on non-Claude models, and a failure after the body opens arrives as an error event. Details in Streaming.

Lane-specific behaviour​

Counting tokens​

curl https://api.mindshub.ai/v1/messages/count_tokens \
-H "Authorization: Bearer $MINDSHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "sonnet", "messages": [{"role": "user", "content": "Hello"}]}'
{"input_tokens": 8}

Counts come from Anthropic's real counter. Name a concrete Claude model (claude-sonnet-5) and you get that model's own exact count. Send a catalog alias or a non-Claude model and the count runs against the platform's current Claude counting model instead, so read those as close rather than exact: tokenizers differ by roughly 1.3x across Claude generations, and further still off the family. If Anthropic is unreachable, the endpoint still answers from a rough local estimate rather than failing. Budget with these; don't reconcile billing with them. count_tokens calls are free and unmetered.

Model discovery​

GET /v1/models answers in a shape the Anthropic SDK's client.models.list() parses. Each row carries type, display_name, and created_at beside the OpenAI-shaped fields. See Models endpoint.

Errors​

Errors on this endpoint use the Anthropic envelope:

{"type": "error", "error": {"type": "not_found_error", "message": "The model 'foo' does not exist or you do not have access to it."}}

The status codes and their meanings are the same as everywhere else; see Errors. Three quirks specific to this endpoint:

  • A 402 (empty wallet) arrives typed invalid_request_error, and a 503 typed api_error. Read the HTTP status, not just the type string. A 429 is typed rate_limit_error and carries no code field, so a client keying retries on error.code alone will miss it.
  • Denial headers survive the envelope. A 429 carries Retry-After and the X-MindsHub-* headers, so back off on the value we send rather than guessing. The two billing 429s carry x-should-retry: false, and a 403 for a model an administrator restricted carries X-MindsHub-Deny-Detail: model_restricted plus error.deny_detail inside the envelope. The param-adaptation headers travel too, on successful requests and on errors the target model itself returned, except on a stream whose 200 went out early, which carries none of the X-MindsHub-* headers (see Errors).
  • A malformed request body returns 400 (message prefixed Invalid request body:).

The per-parameter contract this page summarizes is maintained in code, as PARAM_SUPPORT in minds/requests/anthropic_compat.py, and held to the Anthropic SDK's own parameter list by a test.