Skip to main content
Version: 1.13.0-dev

Govern Claude Code with DVARA

Claude Code sends every model call to Anthropic directly. Your security team sees none of it: no policy, no PII check, no budget, no audit record. From 1.8.5 DVARA serves the Anthropic Messages API at POST /v1/messages, so you can point Claude Code at DVARA. Every model call it makes is then governed and billed as one DVARA workspace.

/v1/messages is available from 1.8.5. On the Enterprise platform, workspace budgets, IP access rules and the Flightdeck views apply to it too.

Point Claude Code at DVARA​

You need three things:

  • A running DVARA Gateway, 1.8.5 or later, that Claude Code can reach. The 2026-10-21 API version needs 1.12.0 or later.
  • An Anthropic provider on that Gateway (ANTHROPIC_API_KEY set on the Gateway), or a route for each model id Claude Code asks for.
  • A DVARA workspace API key. On the Enterprise platform, create it in Flightdeck. In DVARA Open Source, generate it with --generate-key. If the key has scopes, it needs completions:write.

Set these where Claude Code runs. Replace <dvara-workspace-key> with your DVARA key, not an Anthropic key:

export ANTHROPIC_BASE_URL=https://gateway.acme.example.com # the Gateway, no /v1
export ANTHROPIC_API_KEY=<dvara-workspace-key> # sent as x-api-key
# or: export ANTHROPIC_AUTH_TOKEN=<dvara-workspace-key> # sent as Authorization: Bearer
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 # optional: no telemetry or update calls
  • ANTHROPIC_BASE_URL has no /v1. Claude Code adds /v1/messages itself.
  • Either key variable works. DVARA reads the key from Authorization: Bearer or, when that header is absent, from x-api-key. It is the same key either way and has the same rights. With ANTHROPIC_API_KEY, an interactive session asks once to approve the key.
  • The DVARA key wins over a saved login. Set it even if Claude Code is signed in to a Claude subscription. See What DVARA does not govern.

Then start Claude Code as usual and ask it something.

Make sure the model ids route​

Claude Code asks for Anthropic model ids. It also asks for a small background model. Each id must be one the Gateway can route: an id served by its Anthropic provider (any model that starts with claude), or an id with its own route. If not, the call is refused with 400 no_provider.

Pin the ids Claude Code uses to ones your Gateway serves:

export ANTHROPIC_MODEL=<main-model-id>
export ANTHROPIC_DEFAULT_OPUS_MODEL=<opus-model-id>
export ANTHROPIC_DEFAULT_SONNET_MODEL=<sonnet-model-id>
export ANTHROPIC_DEFAULT_HAIKU_MODEL=<haiku-model-id> # the background model
How large a request can be

The context window check uses the model's own window where DVARA knows it: model_limits in gateway.yaml (dvara.llm-gateway.model-limits), or the model catalogue on the Enterprise platform. For a model neither knows, it uses Anthropic's declared window of 200,000 tokens. With the default hard threshold of 90%, a request over 90% of that window is refused with 400 context_window_exceeded, or trimmed if the workspace sets a pruning strategy. In 1.8.5 the check always used 200,000 tokens for Anthropic. See Anthropic in Provider Setup.

What happens to a request on an Anthropic route?​

On a route to Anthropic, DVARA sends the request on as Claude Code sent it: every field, message role, content block and the anthropic-beta header. It changes the request only where governance changes it, such as redacted text or the routed model. Anthropic's reply, plain or streamed, comes back the same way.

  • Extended thinking works. The thinking settings and signed thinking blocks pass through unchanged. Thinking is output, so PII rules and guardrails read it.
  • anthropic-beta passes through. DVARA also writes it to the GATEWAY_RESPONSE audit record as anthropic_beta.
  • Usage and cost come from Anthropic's own usage block, including cache reads, cache writes and reasoning tokens.
  • A content block type DVARA does not read is passed on and counted in gateway_anthropic_opaque_blocks_total.

On a route to another provider, DVARA translates the request. A request with extended thinking is refused with 400 unsupported_capability, and the message names the provider. Fields that only tune Anthropic, such as cache_control or a field DVARA does not model, are left out and counted in gateway_anthropic_fields_dropped_total. If your route sends Claude Code to another provider, turn thinking off with CLAUDE_CODE_DISABLE_THINKING=1. Some of the newest models do not allow that.

On every route, DVARA refuses with 400 unsupported_capability anything the provider would act on outside the Gateway: mcp_servers, container, server tools such as web search or code execution, and metadata keys other than user_id.

Send a test call​

You can call the endpoint yourself to check the setup. The anthropic-version header is required, and so is max_tokens. DVARA serves 2023-06-01 and, from 1.12.0, 2026-10-21, the version the current Anthropic SDKs send. Under 2026-10-21, temperature, top_p and top_k are refused, as Anthropic refuses them:

curl -s https://gateway.acme.example.com/v1/messages \
-H "x-api-key: <dvara-workspace-key>" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "<sonnet-model-id>",
"max_tokens": 200,
"messages": [{"role": "user", "content": "Reply with one word: ready."}]
}'

A successful call returns Anthropic's message shape:

{
"id": "msg_01H8kQ3nV2pX7rT4yW9bZ6cD",
"type": "message",
"role": "assistant",
"model": "<sonnet-model-id>",
"content": [{"type": "text", "text": "Ready."}],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {"input_tokens": 14, "output_tokens": 4}
}

The response carries an X-Trace-ID header. Look it up in Flightdeck's audit view: the call has a GATEWAY_RESPONSE record, and its tokens and cost show on Cost Dashboard (platform roles) or Usage and Token Usage (workspace roles) under the key's workspace.

To count tokens without calling a model, send the same body to POST /v1/messages/count_tokens. It answers {"input_tokens": 14}. On an Anthropic route the number is Anthropic's count; otherwise it is DVARA's estimate. A count is not billed, books no usage and is not counted as a call against the unlicensed call band. It still runs workspace status, policy, prompt templates, PII and guardrails, and the rate limit counts it as a request.

What does a refusal look like?​

On /v1/messages and /v1/messages/count_tokens, every refusal comes back in Anthropic's error envelope, so Claude Code can show it. The status and the code are the same as on /v1/chat/completions. A wrong key:

{
"type": "error",
"error": {
"type": "authentication_error",
"message": "Invalid API key.",
"code": "invalid_api_key"
},
"request_id": "a3f1c9e25b7d4e8a9c1f2d6b8e4a7f30"
}

request_id is the trace id, the same value as the X-Trace-ID header.

StatusCodeWhat it meansWhat to do
400unsupported_api_versionThe anthropic-version header is missing, or is neither 2023-06-01 nor (from 1.12.0) 2026-10-21Send one of the two. Claude Code does. Before 1.12.0 only 2023-06-01 is served, so a client on a current Anthropic SDK is refused.
400invalid_requestFrom 1.12.0, under 2026-10-21: the request sets temperature, top_p or top_k, has a document in an assistant message, or citations on a user's textLeave them out, as Anthropic requires for that version
400no_providerNo provider or route serves the model idPin the model variables above, or add a route
400unsupported_capabilityThinking on a non-Anthropic route, or a field DVARA refuses on every routeTurn thinking off, or route the model to Anthropic
400context_window_exceededThe request is over the model's context window, or the provider's when DVARA does not know the model'sShorten the session, or set the model's window in model_limits. See the note above
401invalid_api_keyThe key is not a DVARA key, or a Claude subscription credential reached DVARASet a DVARA workspace key
403api_key_scopeThe key has scopes and lacks completions:writeAdd the scope, or use another key
403session_killedOn the Enterprise platform, someone killed this agent sessionStart a new session

Policy, PII BLOCK, guardrail and budget refusals use the codes in Error handling.

What does DVARA govern for Claude Code?​

DVARA governs the model calls. Each one runs the same controls as /v1/chat/completions: policy, PII rules, guardrails (on the system text too), budgets, rate limits, call caps, cost records and the signed audit chain. On the Enterprise platform, a workspace's IP allowlist or denylist applies to a call that sends its key in x-api-key, as it does for a bearer key.

PII detection fires on every Claude Code call

Claude Code's own prompt carries an email address, so the email detector matches on every request. With the default PII action, LOG, that is an audit entry and nothing else. A workspace whose action is REDACT or TOKENIZE edits Claude Code's own prompt before it reaches the model. BLOCK refuses every call. For a workspace that runs Claude Code, keep the email action at LOG, or expect that edit. See PII detection.

Govern the MCP tools Claude Code calls​

Everything above governs Claude Code's model calls. Its MCP tool calls are separate: Claude Code calls each MCP server you add to it directly. Add the DVARA MCP Gateway as its MCP server instead, and DVARA applies your workspace's policy to every tool call, redacts personal data in results and records each one.

claude mcp add --transport http dvara http://localhost:8080/mcp \
--header "Authorization: Bearer <dvara-workspace-key>"

claude mcp list should show dvara: http://localhost:8080/mcp (HTTP) - ✔ Connected. DVARA lists each server's tools with the server's id in front, so in Claude Code the MCP quickstart's tools appear as mcp__dvara__demo-tools__get_order_status and so on. Asked to look up customer C-42 and then delete them, Claude Code gets:

Customer C-42: Jane Doe, [REDACTED_EMAIL], [REDACTED_PHONE_NUMBER],
card on file [REDACTED_CREDIT_CARD], plan Enterprise.
delete_customer is irreversible and not allowed in this workspace

The delete never reaches the MCP server. Both calls are in Flightdeck under MCP → Tool Calls (platform roles) or Agents → MCP Tool Calls (workspace roles).

What does DVARA not govern?​

  • Claude Code's local actions. File edits, shell commands and git run on the developer's machine and never reach DVARA.
  • MCP servers Claude Code calls directly. Add them to DVARA instead, as in Govern the MCP tools Claude Code calls.
  • A Claude subscription. Claude Code signed in to a Claude subscription, with no DVARA key set, uses its own credential. DVARA cannot govern that use; if that credential reaches the Gateway, it is refused with 401. Set a DVARA key as shown above.
  • Claude Code on Amazon Bedrock or Google Vertex AI. With CLAUDE_CODE_USE_BEDROCK or CLAUDE_CODE_USE_VERTEX, Claude Code does not use /v1/messages, so it cannot be pointed at DVARA.

Where to go next​