Claude proxy

A transparent passthrough proxy to the Anthropic Claude API at https://ai.hep.gg/anthropic/<proxy_token>/..., so Claude Code calls can be metered and analyzed. You supply your own Anthropic key.

Claude proxy

The Claude proxy is a transparent reverse proxy in front of the Anthropic API. You send a normal Anthropic request to https://ai.hep.gg/anthropic/<proxy_token>/... and everything after the proxy token is forwarded verbatim to https://api.anthropic.com. The point is metering and analytics: each call is logged (token usage, stop reason, tool names) against the machine that owns the proxy token. This is what ccas points Claude Code at.

How auth works here

This endpoint uses two credentials at once, and they belong to different parties:

  • The pt_ proxy token sits in the URL path as the first segment. It identifies your machine to the gateway. Get one from POST /sync/machines (or let ccas init do it). It is matched against your registered machines.
  • Your own Anthropic credentials (the x-api-key header, anthropic-version, and so on) ride in the request as usual and are forwarded to Anthropic unchanged. The gateway does not supply an Anthropic key for you.

An unknown proxy token returns 401 in Anthropic's error shape:

{ "type": "error", "error": { "type": "authentication_error", "message": "Unknown proxy token" } }
POSThttps://ai.hep.gg/anthropic/{proxy_token}/v1/messagesAuth required
Transparent passthrough to the Anthropic Messages API.

The proxy accepts any HTTP method. The path shown is the common case (the Messages API), but anything after /{proxy_token}/ is forwarded as-is, so /anthropic/{proxy_token}/v1/messages, /anthropic/{proxy_token}/v1/models, and any other Anthropic path all work. Query strings are preserved. The raw body is forwarded as bytes up to 32 MB with no JSON re-encoding.

Path
proxy_token
stringrequired
Your machine's pt_ proxy token. First path segment.

Headers are forwarded except host, content-length, connection, and accept-encoding. The x-session-id header (or metadata.userID in the body) is captured for analytics grouping. The response is Anthropic's, passed through with its status and headers (minus content-length, connection, transfer-encoding, and content-encoding). Streaming SSE is piped through and parsed for usage (input, output, cache_creation, cache_read tokens), stop_reason, and any tool_use names.

If Anthropic is unreachable, the proxy returns 502 in the Anthropic error shape:

{ "type": "error", "error": { "type": "upstream_error", "message": "Upstream unreachable" } }

Example

Construct the URL by prefixing your normal Anthropic call with https://ai.hep.gg/anthropic/<proxy_token>. Send your Anthropic key exactly as you would to api.anthropic.com.

curl
curl https://ai.hep.gg/anthropic/$PROXY_TOKEN/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet-latest",
    "max_tokens": 256,
    "messages": [{ "role": "user", "content": "Hello, Claude." }]
  }'

Using it with the Anthropic SDK

Point the SDK's base URL at the proxy prefix (including your proxy token) and pass your real Anthropic key. The SDK appends /v1/messages to the base URL.

node
import Anthropic from "@anthropic-ai/sdk";
 
const client = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
  baseURL: `https://ai.hep.gg/anthropic/${process.env.PROXY_TOKEN}`,
});
 
const msg = await client.messages.create({
  model: "claude-3-5-sonnet-latest",
  max_tokens: 256,
  messages: [{ role: "user", content: "Hello, Claude." }],
});
console.log(msg);