API

Anthropic Messages API

Pendra translates Anthropic's Messages format to OpenAI Chat Completions under the hood, so you can point any Anthropic SDK — or Claude Code — at Pendra and get the same Anthropic-shaped responses back, served by a Pendra-managed or self-hosted worker.

Endpoint

POST https://api.pendra.ai/v1/messages

Authentication

Send your Pendra API key on the x-api-key header (Anthropic convention). The Authorization: Bearer header also works for OpenAI-style clients.

Request

curl
curl https://api.pendra.ai/v1/messages \
  -H "x-api-key: pdr_sk_..." \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3.6:27b",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

All standard Anthropic Messages fields are accepted: model, max_tokens, messages, system, temperature, top_p, top_k, stop_sequences, tools, tool_choice, and stream.

Response

Pendra returns the Anthropic message envelope. content is an array of content blocks (text, tool_use, etc.). stop_reason is "end_turn" on a natural finish or "max_tokens" when capped. usage uses Anthropic naming (input_tokens / output_tokens).

{
  "id": "msg_01N3Xc8...",
  "type": "message",
  "role": "assistant",
  "model": "qwen3.6:27b",
  "content": [
    {
      "type": "text",
      "text": "Hello! How can I help you today?"
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 9,
    "output_tokens": 12
  }
}

Anthropic SDK

messages.ts
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  apiKey: "pdr_sk_...",
  baseURL: "https://api.pendra.ai",
});

const msg = await client.messages.create({
  model: "qwen3.6:27b",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello!" }],
});

Streaming

Set "stream": true to receive Anthropic-format SSE events (message_start, content_block_delta, message_delta, message_stop). The X-Request-Id response header is also set on streaming responses for support and reconciliation.

event: message_start
data: {"type":"message_start","message":{"id":"msg_01N3Xc8","type":"message","role":"assistant","content":[],"model":"qwen3.6:27b","stop_reason":null,"usage":{"input_tokens":9,"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":"Hello"}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"!"}}

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":{"output_tokens":2}}

event: message_stop
data: {"type":"message_stop"}

Use with Claude Code

Point Claude Code at Pendra by setting two environment variables — see the full recipe in Integrations → Claude Code.