Anthropic API
POST /v1/messages speaks Anthropic's Messages API, so Claude Code and the Anthropic SDKs work with the Sevren models. Requests use the same keys, prices, limits and billing as chat completions.
Claude Code
Set these variables in your shell, then start Claude Code from the same shell. Use the key from your account; the base URL has no /v1.
export ANTHROPIC_BASE_URL=https://api.example.com
export ANTHROPIC_AUTH_TOKEN=sk-sv-user-… # your Sevren key
export ANTHROPIC_MODEL=sevren
export ANTHROPIC_DEFAULT_SONNET_MODEL=sevren
export ANTHROPIC_DEFAULT_OPUS_MODEL=sevren
export ANTHROPIC_DEFAULT_HAIKU_MODEL=sevren-lite-27b
claude- Model names. Without the model variables Claude Code asks for Claude model names, which Sevren does not serve; those requests answer
404 not_found_error. The variables map every model Claude Code uses, including its background tasks, to a Sevren model. - Expired keys. A session token is valid for 14 days. When Claude Code reports an authentication error, sign in again and set the new token, or use an account key (
sk-sv-acct-…). - Context window. Claude Code sends the whole conversation with every request, so a long session is bounded by the model's context window. When it is full, the request answers
400; start a new session or compact the conversation. Like every400, that request is kept for up to 7 days to debug it (see Errors). - Telemetry. Claude Code's own telemetry and error reporting follow its settings, not Sevren'. Set
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1to turn them off.
Other agents are configured on the Integrations page.
Requests and SDKs
Point the Anthropic SDK at https://api.example.com and pass your Sevren key.
curl https://api.example.com/v1/messages \
-H "x-api-key: $SEVREN_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "sevren-lite-27b",
"max_tokens": 1024,
"system": "You are terse.",
"messages": [{"role": "user", "content": "Name three prime numbers above 100."}]
}'import os
from anthropic import Anthropic
client = Anthropic(base_url="https://api.example.com", api_key=os.environ["SEVREN_API_KEY"])
message = client.messages.create(
model="sevren-lite-27b",
max_tokens=1024,
messages=[{"role": "user", "content": "Name three prime numbers above 100."}],
)
print("".join(block.text for block in message.content if block.type == "text"))import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ baseURL: "https://api.example.com", apiKey: process.env.SEVREN_API_KEY });
const message = await client.messages.create({
model: "sevren-lite-27b",
max_tokens: 1024,
messages: [{ role: "user", content: "Name three prime numbers above 100." }],
});
console.log(message.content.filter(block => block.type === "text").map(block => block.text).join(""));Endpoints
POST/v1/messages
AUTH: API key
Creates a message, as one JSON object or, with "stream": true, as server-sent events. The request runs through the same access check, child-safety screen, hold, metering and billing as POST /v1/chat/completions.
POST/v1/messages/count_tokens
AUTH: API key
Takes the same body (max_tokens optional) and answers {"input_tokens": N}. For some models the count is an estimate. It is never charged.
curl https://api.example.com/v1/messages/count_tokens \
-H "x-api-key: $SEVREN_API_KEY" \
-H "content-type: application/json" \
-d '{"model": "sevren-lite-27b", "messages": [{"role": "user", "content": "Hello"}]}'
{"input_tokens": 9}Authentication
| Header | Rule |
|---|---|
x-api-key: <key> | Your Sevren key: a session token (sk-sv-user-…) or an account key (sk-sv-acct-…), the same keys as the OpenAI API. |
Authorization: Bearer <key> | The same keys. Claude Code sends this when you set ANTHROPIC_AUTH_TOKEN. If both headers are sent, this one is used. |
anthropic-version, anthropic-beta | Accepted and ignored. |
Request fields
| Field | Rule |
|---|---|
model | Required. A Sevren model id from Models. Claude model names answer 404 not_found_error. |
max_tokens | Optional on Sevren. Omit it to use the provider's default. If supplied, it is bounded by the context window and what your free tokens and credit can pay for, as described in Output limits. |
system | A string or an array of text blocks. |
messages | Required, non-empty. Roles user, assistant and system (text only; a system message after the first turn is passed to the model inside the conversation, next to the user turn it follows). Content is a string or an array of text, tool_use and tool_result blocks. |
tools | Client tools with name, description and input_schema. At most 128; names of 1 to 64 letters, digits, _ or -. |
tool_choice | {"type": "auto"}, {"type": "any"}, {"type": "tool", "name": "…"} or {"type": "none"}, with optional disable_parallel_tool_use. |
stop_sequences, temperature, top_p, top_k | Range-checked like their chat completions counterparts. |
stream | Boolean. true returns server-sent events. |
thinking | {"type": "enabled"} returns the model's reasoning as thinking blocks. budget_tokens is ignored. |
These are accepted and ignored: metadata (dropped, never forwarded), cache_control, service_tier and any other field. Thinking blocks sent back in the conversation are dropped; the model does not read its earlier reasoning. Server tools, tools with a type such as web_search_…, are dropped too, because they run on Anthropic's side.
Only text is supported. Image, document and other non-text blocks in a message answer 400 invalid_request_error.
Tool use
Declare tools in tools. When the model calls one, the answer contains a tool_use block and stop_reason is tool_use. Your client runs the tool; Sevren never runs tools.
{
"id": "msg_…",
"type": "message",
"role": "assistant",
"model": "sevren-lite-27b",
"content": [
{"type": "text", "text": "Let me check the weather there."},
{"type": "tool_use", "id": "toolu_…", "name": "get_weather", "input": {"city": "Ljubljana"}}
],
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {"input_tokens": 212, "output_tokens": 388}
}Send the result back in the next user message as a tool_result block with the call's tool_use_id. Its content is a string or text blocks; set is_error: true when the tool failed. An image or other non-text block inside a tool result is replaced by the text [image omitted: this model reads text only], so an agent session can go on.
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_…", "content": "14 °C, light rain"}
]
}Tool definitions, tool calls and tool results count as input tokens and are billed like any input.
Thinking
The paid models reason before they answer. With "thinking": {"type": "enabled"} the reasoning comes back as thinking blocks before the text. Without it the reasoning is not returned, but it is still generated and billed as output tokens, as on the chat completions endpoint. Thinking blocks carry an empty signature.
Response
Without stream, the answer is one message object: content holds thinking, text and tool_use blocks, in that order. stop_reason is end_turn when the model finished, max_tokens when the output cap was reached and tool_use when it called a tool. usage reports input_tokens and output_tokens, thinking included.
Streaming
With "stream": true the response is text/event-stream in Anthropic's event format: message_start; for each block content_block_start, its deltas (text_delta, thinking_delta or input_json_delta) and content_block_stop; then message_delta with the stop reason and usage, and message_stop.
event: message_start
data: {"type":"message_start","message":{"id":"msg_…","type":"message","role":"assistant","model":"sevren-lite-27b","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":31,"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":"101, 103 and 107."}}
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":{"input_tokens":31,"output_tokens":412}}
event: message_stop
data: {"type":"message_stop"}A failure after the stream has started cannot change the HTTP status, which is already 200. It arrives as an error event with the error object below.
Errors
Errors use Anthropic's shape. Messages are short and fixed; they never repeat your request content.
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_…
{
"type": "error",
"error": {
"type": "not_found_error",
"message": "The requested model does not exist."
}
}| Status | error.type | Typical cause |
|---|---|---|
| 400 | invalid_request_error | Malformed request, unsupported content, a missing max_tokens, or a conversation over the context window. |
| 401 | authentication_error | Missing, unknown or expired key. |
| 402 | billing_error | Free tokens and credit cannot pay for the prompt and one output token. |
| 403 | permission_error | The key may not use this model. |
| 404 | not_found_error | Unknown or disabled model id, for example a Claude model name. |
| 413 | request_too_large | The body is over the size limit. |
| 429 | rate_limit_error | A rate limit; Retry-After is set. |
| 503 | overloaded_error | The model or a dependency is unavailable, or maintenance is on. |
| 500 to 504 | api_error | Any other server error. |
The statuses are the same as on chat completions; the Errors page lists every cause. Every response carries an X-Request-Id header. Quote it when you report a problem.
When a request answers 400, Sevren keeps it for up to 7 days to debug the error: the request body (up to 64 KiB), the error answer and, if the model refused the request, the model's answer. No IP address, API key or account is stored with it. Requests that do not get a 400 are never stored. See Privacy.
