DVARA headers — request and response reference
DVARA is an AI governance platform, so every /v1/* request is already audited with a server-generated trace ID. The headers below let you authenticate, correlate the audit trail with your own request identifiers, and read the signals DVARA emits back on the response.
Request headers
Authorization: Bearer gw_…
Required on every data-plane request. Create the API key from API Keys in DVARA Flightdeck. Flightdeck applies the signed-in user's role and active workspace. The key starts with gw_, and the full secret is shown exactly once; only a SHA-256 hash is persisted. See Data plane authentication for rotation and revoke flows.
Every /v1 request must carry a valid key. A missing header returns
401 api_key_required; a key absent from the store returns
401 invalid_api_key. Revoked and expired keys return their corresponding
401 codes. The former require-api-key switch was removed in 1.8.0.
X-Trace-ID
Pass X-Trace-ID for distributed tracing correlation. DVARA resolves the trace ID in this order: (1) client-supplied X-Trace-ID / X-Trace-Id header, (2) the OTel trace ID from the current span when OpenTelemetry tracing is active, (3) a random UUID. The chosen value lands on the audit event, the SLF4J MDC, and is echoed back as the X-Trace-ID response header.
A supplied trace ID must contain at most 128 letters, digits, or ._:-. DVARA replaces an invalid value with a new trace ID rather than reflecting it into logs.
# Python (OpenAI SDK)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={"X-Trace-ID": "my-trace-123"},
)
// TypeScript (OpenAI SDK)
const response = await client.chat.completions.create(
{
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
},
{ headers: { "X-Trace-ID": "my-trace-123" } },
);
// Java (LangChain4j)
OpenAiChatModel model = OpenAiChatModel.builder()
.baseUrl("http://localhost:8080/v1")
.apiKey("your-dvara-api-key")
.modelName("gpt-4o")
.customHeaders(Map.of("X-Trace-ID", "my-trace-123"))
.build();
The same trace ID will appear on:
- The
GATEWAY_RESPONSEaudit event (one per request through the data plane), and any other event types fired downstream by policy, PII, guardrail, MCP, or admin paths —trace_idlives on the access log line surrounding the event, so a single grep on the trace ID reconnects every layer - The
X-Trace-IDresponse header DVARA sends back to you - The OTLP span emitted for the request (when tracing is enabled)
X-Session-Id
Pass X-Session-Id for agent session tracking. DVARA groups requests sharing a session ID into a single agentic session, which activates loop detection, approval gates, and session-level audit grouping.
A supplied session ID uses the same 128-character and [A-Za-z0-9._:-] contract. DVARA ignores an invalid session ID, so the request is not joined to that session.
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Continue our conversation"}],
extra_headers={"X-Session-Id": "agent-session-456"},
)
const response = await client.chat.completions.create(
{
model: "gpt-4o",
messages: [{ role: "user", content: "Continue" }],
},
{ headers: { "X-Session-Id": "agent-session-456" } },
);
OpenAiChatModel model = OpenAiChatModel.builder()
.baseUrl("http://localhost:8080/v1")
.apiKey("your-dvara-api-key")
.modelName("gpt-4o")
.customHeaders(Map.of("X-Session-Id", "agent-session-456"))
.build();
Sessions appear in the DVARA Flightdeck under Agents → Sessions, where you can see loop-detection status, kill a runaway session, or drill into every request the session produced.
X-Dvara-End-User and X-Dvara-Tags (MCP and A2A)
Name the customer a tool call or an agent call is for, and tag it, so its activity adds up with that customer's model
calls. This is the MCP and A2A version of the chat request's user field and metadata.tags.
X-Dvara-End-User: cust-42
X-Dvara-Tags: customer=acme,booking=B-1042
You can send the same values inside the protocol message instead. In MCP, put them in params._meta. In A2A, put them
in params.metadata or the message's metadata. The keys are dvarahq.com/end_user (a string) and dvarahq.com/tags
(an object). A header wins when both name the same tag.
- At most 16 tags. A key is up to 64 letters, digits or
_ . : -. An end user or a tag value is up to 128 letters, digits or_ . : @ + / = -. - DVARA refuses a value it cannot accept: MCP answers
-32602(MCP_ATTRIBUTION_INVALID), and A2A answers invalid params. It never drops a value without telling you. - DVARA records the values as tags: your tags, the end user as
user, and the session assession. These are the same tags a model call gets, so one tag value reads the same across model calls, tool calls and agent calls. - These values are what your application says. They do not prove who the caller is. Send an opaque customer id, not a name or an email address.
Response headers
DVARA stamps response headers to signal in-band conditions that don't justify a non-2xx but a well-behaved client should react to. None of these break the OpenAI wire format; they layer on top.
| Header | Distribution | When emitted | What it means |
|---|---|---|---|
X-Trace-ID | Both | Always | The trace ID DVARA resolved for the request — echo it in your application logs to grep across the audit, tracing, and provider-log layers. |
X-Cache: HIT / MISS | Both for exact cache; Enterprise for semantic matching | Cache configured | Whether the response came from a cache rather than the provider. A HIT avoids the upstream call. Semantic similarity matching is Enterprise-only. |
X-Gateway-Strict-Downgraded: true | Both | response_format: json_schema with strict: true against Anthropic or AWS Bedrock | The provider cannot enforce strict JSON Schema; validate on the client if strictness is load-bearing. |
X-Gateway-Failover-Blocked: capability_mismatch | Both | 503 failover_capability_mismatch | The primary failed and no fallback supports the requested capability. |
X-Budget-Remaining-Pct, X-Budget-Remaining-Tokens | Enterprise | A workspace has a budget cap | Remaining percentage and estimated token buying power. |
X-Budget-Warning: true | Both | A policy returned WARN_AGENT | A policy warning, despite the historical header name; it is not a budget signal. |
X-Context-Window-Warning, X-Context-Window-Utilization | Both | Approaching the context window | Estimated input tokens crossed the configured warning threshold. |
X-Gateway-Pii-Unresolved | Enterprise | Response detokenization left placeholders | Count of unresolved request placeholders in the reply. |
Retry-After, X-RateLimit-Retry-After-Seconds | Both | A rate limiter returns 429 | Backoff time in the standard and seconds-only forms. |
X-RateLimit-Tokens-Limit, X-RateLimit-Tokens-Remaining, X-RateLimit-Reset | Both | Token-budget branch returns 429 | Active token limit, remainder, and reset time. |
From 1.8.2 DVARA no longer sends X-License-Warning. Licence expiry never changes a response, so it is reported in DVARA Flightdeck, in LICENSE_* audit events, and on GET /actuator/license instead. See Get and apply a license.
The HTTP error envelope ({"error": {"type": "...", "code": "...", "message": "...", ...}}) is documented separately in Error handling.
Why this matters for governance
Trace IDs and session IDs are the join key between:
- Your application's structured logs
- Your tracing backend (Jaeger, Tempo, Honeycomb)
- DVARA's tamper-evident audit trail
- The upstream provider's request logs (via propagation)
Without them, when a policy fires a denial at 3 a.m. you know what was blocked but not which customer request triggered it. With them, one grep on the trace ID reconnects every layer of the stack.
Next steps
- Back to language-agnostic integration overview.
- Looking for examples by language? See Python, JavaScript / TypeScript, or Java.
- Debugging a specific response code? See Troubleshooting.