Messages
Anthropic native messages endpoint, used by Claude models. Compared to OpenAI's Chat Completions, the differences are: system is a top-level field, content supports a block array, and tool calls flow through tool_use / tool_result.
Protocol boundary
This is the Anthropic request/response format. Claude and Deepseek groups are the primary use case; some OpenAI groups may also expose /v1/messages through gateway translation, but support depends on group configuration.
Endpoint
POST https://api.sakrylle.com/v1/messagesHeaders
| Name | Required | Description |
|---|---|---|
Authorization | Yes | Bearer sk-xxxxxxxxxxxxxxxx |
Content-Type | Yes | application/json |
anthropic-version | No | Matches Anthropic's official header, e.g. 2023-06-01. Passed through to upstream. |
Accept | No | Set text/event-stream for streaming |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Claude model ID, e.g. claude-haiku-4-5-20251001 |
messages | array | Yes | Conversation history; each item has role (user or assistant) and content (string or block array) |
max_tokens | integer | Yes | Maximum output tokens |
system | string | array | No | System prompt at the top level (do not put it inside messages) |
stream | boolean | No | Defaults to false; true returns SSE |
temperature | number | No | 0–1 |
top_p | number | No | Nucleus sampling probability |
top_k | integer | No | Top-k sampling |
stop_sequences | array | No | Stop sequences |
tools | array | No | Tool definitions, each with name, description, input_schema |
tool_choice | object | No | {"type":"auto"} / {"type":"any"} / {"type":"tool","name":"..."} |
metadata | object | No | e.g. {"user_id":"..."} |
The gateway only validates that
modelis present; all other fields pass through to upstream.
Example request
{
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"system": "You are a concise assistant.",
"messages": [
{ "role": "user", "content": "Hi, please introduce yourself." }
]
}Example response
{
"id": "msg_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "message",
"role": "assistant",
"model": "claude-haiku-4-5-20251001",
"content": [
{ "type": "text", "text": "Hi! I'm Claude, an assistant trained by Anthropic." }
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 24,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 18
}
}Tool calls
Declare tools in the request:
{
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "Look up the current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}
],
"messages": [
{ "role": "user", "content": "What's the weather in Shanghai right now?" }
]
}When the model decides to call a tool, the response content includes a tool_use block:
{
"id": "msg_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "message",
"role": "assistant",
"model": "claude-haiku-4-5-20251001",
"content": [
{
"type": "tool_use",
"id": "toolu_xxxxxxxxxxxxxxxx",
"name": "get_weather",
"input": { "city": "Shanghai" }
}
],
"stop_reason": "tool_use",
"usage": { "input_tokens": 312, "output_tokens": 47 }
}Continue the conversation by writing the tool result back as a tool_result block:
{
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "What's the weather in Shanghai right now?" },
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_xxxxxxxxxxxxxxxx",
"name": "get_weather",
"input": { "city": "Shanghai" }
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_xxxxxxxxxxxxxxxx",
"content": "Sunny, 26°C"
}
]
}
]
}Streaming response
With stream: true, the server returns SSE events. The minimum skeleton:
event: message_start
data: {"type":"message_start","message":{"id":"msg_xxx","type":"message","role":"assistant","model":"claude-haiku-4-5-20251001","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":24,"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":"Hi"}}
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":24,"output_tokens":18}}
event: message_stop
data: {"type":"message_stop"}Errors
400 invalid_request_error— missingmodel, body is not JSON, or parse failure401 authentication_error— invalid API key403 billing_error— balance or subscription quota exhausted403 permission_error— group is restricted to Claude Code clients and rejects others429 rate_limit_error— concurrency or rate limit hit502 upstream_error— upstream repeatedly failed, please retry later
For the full table, see Errors.
Code samples
curl https://api.sakrylle.com/v1/messages \
-H "Authorization: Bearer sk-xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Hello"}
]
}'from anthropic import Anthropic
client = Anthropic(
api_key="sk-xxxxxxxxxxxxxxxx",
base_url="https://api.sakrylle.com",
)
msg = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=1024,
system="You are a concise assistant.",
messages=[{"role": "user", "content": "Hello"}],
)
print(msg.content[0].text)import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: "sk-xxxxxxxxxxxxxxxx",
baseURL: "https://api.sakrylle.com",
});
const msg = await client.messages.create({
model: "claude-haiku-4-5-20251001",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
});
console.log(msg.content[0].text);package main
import (
"context"
"fmt"
"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/option"
)
func main() {
client := anthropic.NewClient(
option.WithAPIKey("sk-xxxxxxxxxxxxxxxx"),
option.WithBaseURL("https://api.sakrylle.com/"),
)
msg, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
Model: anthropic.F(anthropic.Model("claude-haiku-4-5-20251001")),
MaxTokens: anthropic.F(int64(1024)),
Messages: anthropic.F([]anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Hello")),
}),
})
if err != nil {
panic(err)
}
fmt.Println(msg.Content[0].Text)
}use reqwest::Client;
use serde_json::json;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = Client::new();
let resp: serde_json::Value = client
.post("https://api.sakrylle.com/v1/messages")
.bearer_auth("sk-xxxxxxxxxxxxxxxx")
.header("anthropic-version", "2023-06-01")
.json(&json!({
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello"}]
}))
.send()
.await?
.json()
.await?;
println!("{resp:#}");
Ok(())
}import java.net.URI;
import java.net.http.*;
public class Messages {
public static void main(String[] args) throws Exception {
String body = """
{"model":"claude-haiku-4-5-20251001","max_tokens":1024,
"messages":[{"role":"user","content":"Hello"}]}
""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.sakrylle.com/v1/messages"))
.header("Authorization", "Bearer sk-xxxxxxxxxxxxxxxx")
.header("Content-Type", "application/json")
.header("anthropic-version", "2023-06-01")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> resp = HttpClient.newHttpClient()
.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());
}
}using System.Net.Http.Headers;
using System.Text;
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "sk-xxxxxxxxxxxxxxxx");
http.DefaultRequestHeaders.Add("anthropic-version", "2023-06-01");
var payload = """
{"model":"claude-haiku-4-5-20251001","max_tokens":1024,
"messages":[{"role":"user","content":"Hello"}]}
""";
var resp = await http.PostAsync(
"https://api.sakrylle.com/v1/messages",
new StringContent(payload, Encoding.UTF8, "application/json"));
Console.WriteLine(await resp.Content.ReadAsStringAsync());