Messages API (Anthropic)
The server exposes POST /v1/messages — an Anthropic-compatible endpoint that
applies your Mend guardrail policy before and after calling the upstream Claude
model. Point the Anthropic SDK at the server with a single base_url change.
The endpoint requires the anthropic extra on
the server:
pip install 'mend-guardrails[anthropic]'
Request format
The request body is the standard Anthropic Messages payload with one additional
field: a guardrails object that selects the policy to apply.
{
"model": "claude-3-5-sonnet-latest",
"max_tokens": 1024,
"system": "You are a helpful assistant.",
"messages": [
{"role": "user", "content": "Hello!"}
],
"guardrails": {
"config_id": "strict"
}
}
guardrails object
| Field | Type | Required | Description |
|---|---|---|---|
config_id |
string |
No (in api policy-source mode, always optional; in local mode, required unless MEND_GUARDRAILS_DEFAULT_CONFIG_ID is set) |
Policy ID to apply. |
Standard Anthropic fields forwarded to upstream
| Field | Type | Description |
|---|---|---|
model |
string |
Claude model name (e.g. claude-3-5-sonnet-latest). |
max_tokens |
integer |
Required. Maximum tokens to generate. |
messages |
array |
Conversation history in Anthropic message format. |
system |
string \| array |
Top-level system prompt. |
temperature |
number |
Sampling temperature. |
top_p / top_k |
number / integer |
Sampling parameters. |
stop_sequences |
array |
Custom stop sequences. |
tools / tool_choice |
array / object |
Tool-use definitions. |
metadata |
object |
Opaque metadata forwarded to Anthropic. |
Any other fields are forwarded to the upstream provider unchanged.
Default Anthropic headers
POST /v1/messages always forwards these inbound headers to upstream when
present, even if MEND_GUARDRAILS_FORWARD_HEADERS is unset:
| Header | Why |
|---|---|
x-api-key |
Anthropic API key (including a Docker Sandboxes sentinel). |
anthropic-version |
Required by the Anthropic API. |
anthropic-beta |
Claude Code / 1M-context and other beta features. |
Authorization |
Bearer tokens, when sent. |
Additional names can still be allowlisted with MEND_GUARDRAILS_FORWARD_HEADERS.
Streaming
Set "stream": true to receive an Anthropic Server-Sent Events (SSE) response
with native event: frames (message_start, content_block_delta, …).
Guardrails still run on both input and output. If an output-stage guardrail
fires mid-stream, a terminal event: error frame with type
guardrail_enforcement_triggered is emitted.
Upstream configuration
The /v1/messages route always builds an Anthropic upstream client. When no
models config entry
matches the request's model, the server uses the global environment fallback:
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
API key for the upstream Anthropic provider. |
ANTHROPIC_BASE_URL |
Base URL of the upstream (defaults to Anthropic's API). |
LLM-based guardrails (e.g. Jailbreak) run on an OpenAI-compatible
guardrail_llm, soOPENAI_API_KEYmay also be required on the server when your policy contains such guardrails.
To route a model explicitly, add an entry with "provider": "anthropic" to your
models config:
[
{
"name": "claude-3-5-sonnet-latest",
"provider": "anthropic",
"parameters": {
"base_url": "https://api.anthropic.com",
"api_key": "sk-ant-..."
}
}
]
Response format
On success the response is a standard Anthropic Message object:
{
"id": "msg_abc123",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-latest",
"content": [{"type": "text", "text": "Hello! How can I help you today?"}],
"stop_reason": "end_turn",
"usage": {"input_tokens": 20, "output_tokens": 10}
}
Enforcement responses
When a guardrail blocks a request the server returns HTTP 400:
{
"detail": {
"error": "guardrail_enforcement_triggered",
"message": "Guardrail PromptInjection triggered enforcement",
"guardrail": "PromptInjection"
}
}
The upstream LLM is never called when an input-stage guardrail blocks the request.
HTTP status codes
| Code | Meaning |
|---|---|
200 |
Successful completion; guardrails passed. |
400 |
A guardrail blocked the request or response, or the upstream provider rejected the payload. |
422 |
Invalid request — no config_id and no default configured, the model is routed to a non-Anthropic provider, the anthropic package is not installed, or the SDK rejected a request keyword. Sampling fields (temperature, top_p, top_k) and unknown Claude Code extras are forwarded via extra_body when the SDK signature omits them. |
502 / 504 |
Upstream provider unreachable or timed out. |
500 |
Unexpected internal error. |
Examples
curl
curl -X POST http://localhost:8000/v1/messages \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ANTHROPIC_API_KEY" \
-d '{
"model": "claude-3-5-sonnet-latest",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello!"}],
"guardrails": {"config_id": "default"}
}'
Anthropic Python SDK
Because the server speaks the native Messages API, point the official SDK at it
with a single base_url change:
from anthropic import Anthropic
client = Anthropic(
base_url="http://localhost:8000", # the guardrail server (SDK appends /v1/messages)
api_key="any", # forwarded as-is to the upstream
)
response = client.messages.create(
model="claude-3-5-sonnet-latest",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
extra_body={"guardrails": {"config_id": "default"}},
)
print(response.content[0].text)
For server-side upstream configuration — provider selection, custom headers and query parameters, and model routing — see Configure the upstream provider.