Headers

Every Cova-* header you can send, and every one Cova sends back.

The CoreValue gateway uses Cova-* headers for request-side configuration and returns Cova-* headers on every response. Header matching is case-insensitive (the gateway lowercases header keys before comparison), but the canonical names below are recommended.

All examples below use the placeholder key sk-cova-XXXXXXXXXXXXXXXX and the gateway base URL https://gateway.corevalue.dev. Authentication is always Authorization: Bearer <COVA_API_KEY> — see Authentication.

Quick Reference

curl https://gateway.corevalue.dev/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-cova-XXXXXXXXXXXXXXXX" \
  -H "Cova-User-Id: user_abc123" \
  -H "Cova-Actor: agent" \
  -H "Cova-Prompt-Id: prompt_42" \
  -H "Cova-Property-Region: us-east-1" \
  -H "Cova-Cache-Enabled: true" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'
from openai import OpenAI

client = OpenAI(
    base_url="https://gateway.corevalue.dev/v1",
    api_key="sk-cova-XXXXXXXXXXXXXXXX",
)

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello!"}],
    extra_headers={
        "Cova-User-Id": "user_abc123",
        "Cova-Actor": "agent",
        "Cova-Prompt-Id": "prompt_42",
        "Cova-Property-Region": "us-east-1",
        "Cova-Cache-Enabled": "true",
    },
)
import { OpenAI } from "openai";

const client = new OpenAI({
  baseURL: "https://gateway.corevalue.dev/v1",
  apiKey: "sk-cova-XXXXXXXXXXXXXXXX",
});

const response = await client.chat.completions.create(
  {
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: "Hello!" }],
  },
  {
    headers: {
      "Cova-User-Id": "user_abc123",
      "Cova-Actor": "agent",
      "Cova-Prompt-Id": "prompt_42",
      "Cova-Property-Region": "us-east-1",
      "Cova-Cache-Enabled": "true",
    },
  },
);

Request Headers

All request headers use the Cova- prefix. They are read before the request is sent to the provider.

Cova-User-Id

DirectionRequest
PurposeEnd-user identifier for analytics, per-user rate-limit segments, and logging.
Validation[a-zA-Z0-9_\-:.], max 256 chars. Invalid values are treated as null (empty string in analytics).
ExampleCova-User-Id: user_abc123

Cova-Prompt-Id

DirectionRequest
PurposePrompt identifier for prompt-expansion and logging. Can also be set via body.prompt_id (the body value overrides the header).
Validation[a-zA-Z0-9_\-:.], max 256 chars. Same sanitizer as Cova-User-Id.
ExampleCova-Prompt-Id: prompt_42

Cova-Customer-Id

DirectionRequest
PurposeWhich of your customers a request was made on behalf of, for per-customer unit economics. Promoted to an indexed customer_id column, so cost can be grouped by customer without scanning the properties map.
ValidationTruncated at 256 characters. Whitespace-only values are ignored.
ExampleCova-Customer-Id: acme-corp

Cova-Feature-Id

DirectionRequest
PurposeWhich feature or surface produced the request — checkout, support-bot, nightly-eval. Answers "what is this spend for" independently of who it was for.
ValidationTruncated at 256 characters. Whitespace-only values are ignored.
ExampleCova-Feature-Id: checkout

Cova-Team-Id

DirectionRequest
PurposeWhich of your teams the spend belongs to. Cost attribution only — this header never affects what anyone can see. Access is decided from the credential, so tagging a request with another team's id changes the invoice split, not the audience.
ValidationTruncated at 256 characters. Whitespace-only values are ignored.
ExampleCova-Team-Id: platform

These three are self-reported: the gateway records what you send. A key scoped to customers checks Cova-Customer-Id against its list, and a key scoped to one customer fills it in. Team budgets and chargeback use the key's team, never Cova-Team-Id.

They cannot be forged by a caller supplying a matching Cova-Property-* header. Property names have their hyphens replaced with underscores, so Cova-Property-Cova-Customer-Id is stored as cova_customer_id and can never collide with the literal Cova-Customer-Id these are read from.

Cova-Actor

DirectionRequest
PurposeWhether a person or a program produced this turn. Cost per model is a commodity; cost per actor is what makes an agent fleet governable, and you cannot budget for something you cannot name.
ValidationOne of human, agent, unknown. Sanitized as an identifier, then matched exactly. An unrecognised value is not coerced to unknown: the request falls through to the heuristic below, since a misspelled header is no reason to discard evidence the payload still carries.
ExampleCova-Actor: agent

When the header is absent, the gateway infers the answer from the payload. The signal is not that tools exist — people chat with search and code interpreters enabled all day, and a capability is not an actor. It is that the turn is a continuation: the last thing in the conversation is a tool result being fed back, which no person typed.

DialectTail that reads as an agent continuation
OpenAI messageslast message role is tool or function
Anthropic / Bedrock messageslast message content contains type: "tool_result"
Google contentslast content parts contain functionResponse
Responses inputlast item type is function_call_output

The heuristic under-counts agents, by roughly one turn per run. The first turn of an agent run is identical to a person asking a question of a tool-enabled assistant, because at that moment it is the same request. A ten-turn loop is recorded as one unknown and nine agent; a one-shot agent call is recorded as unknown outright.

This is the argument for sending the header. The heuristic gives coverage with no integration work; the header gives correctness.

unknown is a real answer and never collapses into human. Treating "no evidence of an agent" as "a person" is true for most workloads today and will not stay true, and a default of human would silently reclassify the future as the past.

Cova-Actor-Source

DirectionWritten by the gateway. Not read from your request.
PurposeHow the actor was determined: header if you declared it, inferred if the payload tail matched, none if there was no evidence. Recorded beside the value so the number is interpretable.
ValidationSending this header has no effect. It is ignored rather than rejected.
ExampleAppears in logs and filters as cova_actor_source, never as input

Accepting this inbound would let a caller assert its own provenance, declaring header while sending none, which destroys the only thing the field is for. "45% agent" means nothing if you cannot tell whether that is declared or guessed, and the two carry very different error bars.

cova_actor and cova_actor_source are reserved property names. A Cova-Property-cova_actor header, or Cova-Property-cova-actor which sanitises to the same key, is dropped, so a caller cannot overwrite the classification.

Both appear on the Requests page as the Actor column, and as the Actor and Actor source filters.

Cova-Session-Id, Cova-Session-Name, Cova-Session-Path

DirectionRequest
PurposeGroup related requests into one run, shown on the console's Runs page. Cova-Session-Id identifies the run, Cova-Session-Name labels it, and Cova-Session-Path places a request in the run's tree, for example /plan/search. See Runs.
ValidationTrimmed, then truncated at 256 characters. Blank values are ignored. A Cova-Property-* header cannot set or overwrite them.
ExampleCova-Session-Id: run_7f3a

A session cap limits what each session on a key may spend. The id is whatever your application sends, so a session cap bounds an honest or buggy agent only: a caller that sends a new id gets a fresh cap.

Cova-Property-{Name}

DirectionRequest
PurposeArbitrary key-value metadata attached to the request for filtering, segmentation, and analytics. Multiple Cova-Property-* headers are allowed per request.
ValidationName: lowercased, hyphens converted to underscores, then [a-z0-9_], max 64 chars, so Cova-Property-Region is stored as region. Value: max 256 chars (truncated). Invalid names are silently dropped.
ExampleCova-Property-Region: us-east-1
curl https://gateway.corevalue.dev/v1/chat/completions \
  -H "Authorization: Bearer sk-cova-XXXXXXXXXXXXXXXX" \
  -H "Cova-Property-Region: us-east-1" \
  -H "Cova-Property-Env: production" \
  -H "Cova-Property-App: mobile" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hi"}]}'
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hi"}],
    extra_headers={
        "Cova-Property-Region": "us-east-1",
        "Cova-Property-Env": "production",
        "Cova-Property-App": "mobile",
    },
)
const response = await client.chat.completions.create(
  { model: "gpt-4o-mini", messages: [{ role: "user", content: "Hi" }] },
  {
    headers: {
      "Cova-Property-Region": "us-east-1",
      "Cova-Property-Env": "production",
      "Cova-Property-App": "mobile",
    },
  },
);

Cova-RateLimit-Policy

DirectionRequest
PurposeHeader-based bucket rate-limit policy. Overrides the DB-configured policy when present. Parsed into capacity / window / unit / segment.
ValidationMust follow the format above. An invalid value is ignored, and the rate limits configured in the console apply.
Format<quota>;w=<window seconds>;u=<request|cents>;s=<segment>. Only w=, u= and s= are read: a part spelled any other way, such as window=60, is ignored, and the window falls back to one hour.
ExampleCova-RateLimit-Policy: 100;w=60;u=request;s=user

Cova-Fallbacks

DirectionRequest
PurposeJSON array of model names to try in order if the primary model fails with a retryable status code.
ValidationValid JSON array, non-empty, max 5 elements, each a non-empty string. Invalid or missing → null (no fallback chain).
ExampleCova-Fallbacks: ["groq/llama-3.3-70b-versatile","together/meta-llama/Llama-3.3-70B-Instruct-Turbo"]

Cova-Cache-Enabled

DirectionRequest
PurposeEnables both cache read and write for this request.
Validation"true" (case-insensitive) → enabled. Any other value → disabled.
ExampleCova-Cache-Enabled: true

Cova-Cache-Save

DirectionRequest
PurposeEnables cache write only (store the response for future hits).
Validation"true" (case-insensitive) → enabled.
ExampleCova-Cache-Save: true

Cova-Cache-Read

DirectionRequest
PurposeEnables cache read only (serve from cache if a hit exists).
Validation"true" (case-insensitive) → enabled.
ExampleCova-Cache-Read: true

Cova-Cache-Bucket-Max-Size

DirectionRequest
PurposeMaximum number of cached responses per cache key (bucket size). Enables load-balancing across cached variants.
ValidationInteger, default 1, max 20. If greater than 20, cache settings return null (cache disabled).
ExampleCova-Cache-Bucket-Max-Size: 5

Cova-Cache-Seed

DirectionRequest
PurposeCache isolation seed. Prepended to the cache key hash to namespace cache entries (e.g. for A/B experiments).
ValidationAny string. Included in the SHA-256 cache key computation.
ExampleCova-Cache-Seed: my-experiment-v2

Cova-Cache-Ignore-Keys

DirectionRequest
PurposeJSON array of body keys to exclude from cache key computation (e.g. ignore user_id so different users share a cache entry).
ValidationValid JSON string array. Invalid JSON is ignored (no keys stripped).
ExampleCova-Cache-Ignore-Keys: ["user_id","timestamp"]

Cache-Control (the standard HTTP header) also sets how long a cached response is kept (s-maxage or max-age, max 365 days, default 7 days). It is not a Cova-* header, but the cache reads it.

curl https://gateway.corevalue.dev/v1/chat/completions \
  -H "Authorization: Bearer sk-cova-XXXXXXXXXXXXXXXX" \
  -H "Cova-Cache-Enabled: true" \
  -H "Cova-Cache-Bucket-Max-Size: 5" \
  -H "Cova-Cache-Seed: my-experiment-v2" \
  -H "Cova-Cache-Ignore-Keys: [\"user_id\",\"timestamp\"]" \
  -H "Cache-Control: max-age=3600" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hi"}]}'
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hi"}],
    extra_headers={
        "Cova-Cache-Enabled": "true",
        "Cova-Cache-Bucket-Max-Size": "5",
        "Cova-Cache-Seed": "my-experiment-v2",
        "Cova-Cache-Ignore-Keys": '["user_id","timestamp"]',
        "Cache-Control": "max-age=3600",
    },
)
const response = await client.chat.completions.create(
  { model: "gpt-4o-mini", messages: [{ role: "user", content: "Hi" }] },
  {
    headers: {
      "Cova-Cache-Enabled": "true",
      "Cova-Cache-Bucket-Max-Size": "5",
      "Cova-Cache-Seed": "my-experiment-v2",
      "Cova-Cache-Ignore-Keys": '["user_id","timestamp"]',
      "Cache-Control": "max-age=3600",
    },
  },
);

Response Headers

The gateway sets these on the response your SDK receives. Cost headers are added once the provider has reported usage.

Cova-Id

DirectionResponse
PurposeGateway-generated request ID (UUID). Used for log correlation and /meta endpoint lookups.
When SetOn every response the gateway forwards from a provider (streaming and non-streaming). Also set on the forwarded upstream request. Not set on cache hits or on requests the gateway refuses.
ExampleCova-Id: 550e8400-e29b-41d4-a716-446655440000

Cova-Provider

DirectionResponse
PurposeThe provider that actually served the request (after fallback resolution). Reflects the key that was used, not the requested provider.
When SetOn every response the gateway forwards from a provider.
ExampleCova-Provider: groq

Cova-Model

DirectionResponse
PurposeThe model that actually served the request (after fallback or budget-gate downgrade). May differ from the requested model.
When SetOn every response the gateway forwards from a provider.
ExampleCova-Model: groq/llama-3.3-70b-versatile

Cova-Status

DirectionResponse
PurposeGateway-level status: "success" or "error". Derived from upstream HTTP status (200 → success, else → error).
When SetOn every response the gateway forwards from a provider.
ExampleCova-Status: success

Cova-Request-Cost

DirectionResponse
PurposeActual cost in USD for this request, computed from extracted usage.
When SetNon-streaming: always set (even when 0) after usage extraction. Streaming: set in the final SSE cova frame (not as a header).
ExampleCova-Request-Cost: 0.0015000000

Cova-Request-Cost-Estimate

DirectionResponse
PurposeAn upfront worst-case cost estimate for requests paid with CoreValue credits. A hint, not the final cost.
When SetNot sent today.
ExampleCova-Request-Cost-Estimate: 0.0230000000

Cova-Credits-Remaining

DirectionResponse
PurposePost-request credit balance in USD (pre-request balance − actual cost).
When SetSuccessful requests paid with CoreValue credits. Non-streaming: set as a header. Streaming: available from /meta, not in the final SSE cova frame.
ExampleCova-Credits-Remaining: 48.7700000000

Cova-Gateway-Mode

DirectionResponse
PurposeWhether cross-provider translation was applied. "passthrough" (default) or "translated".
When SetOn every response the gateway forwards from a provider. "translated" when the request was converted to another provider's format: an anthropic/, google/ or bedrock/ model on /gateway/v1.
ExampleCova-Gateway-Mode: translated

Cova-Translation-Warning

DirectionResponse
PurposeComma-separated list of request fields dropped during cross-provider translation.
When SetSet only when gatewayMode is "translated" and fields were dropped. Absent otherwise.
ExampleCova-Translation-Warning: logprobs,top_k

Cova-Cache

DirectionResponse
PurposeCache hit indicator. Set to "HIT" when the response is served from cache.
When SetSet only on cache hits (when Cova-Cache-Read or Cova-Cache-Enabled is true and a cached entry exists).
ExampleCova-Cache: HIT

Cova-Cache-Bucket-Idx

DirectionResponse
PurposeThe bucket slot index from which the cached response was served.
When SetSet only on cache hits.
ExampleCova-Cache-Bucket-Idx: 2

Cova-Cache-Latency

DirectionResponse
PurposeOriginal response latency (in ms) of the cached response, recorded when it was first stored.
When SetSet only on cache hits.
ExampleCova-Cache-Latency: 450

X-Cova-Retry-Count

DirectionResponse
PurposeNumber of retry attempts made before the request succeeded. Set by the retry executor for observability.
When SetSet when gateway-configured retries are enabled (env-configured server-side) and at least one retry was attempted. Absent on first-try success or when retries are disabled.
ExampleX-Cova-Retry-Count: 2

Streaming Final SSE Frame

When the gateway processes a streaming request, it appends one final SSE event after the upstream stream closes (including after data: [DONE] for OpenAI-format providers).

Exact Format

data: {"cova":{"requestId":"...","provider":"...","model":"...","status":"success","requestCost":0.0015,"gatewayMode":"translated","translationWarning":"logprobs"}}

The frame is a single SSE data: line terminated by \n\n (two newlines). The JSON payload is nested under a "cova" key so the SDK can detect it by checking "cova" in lastChunk.

CovaGatewayMeta Shape

The fields the CoreValue TypeScript and Python SDKs return:

FieldTypeSource
requestIdstringCova-Id header
providerstringCova-Provider header
modelstringCova-Model header
status"success" | "error"Cova-Status header
requestCostnumber?Cova-Request-Cost (non-streaming) or final SSE event (streaming) or /meta endpoint
estimatedCostnumber?Cova-Request-Cost-Estimate (upfront, all responses)
creditsRemainingnumber?Cova-Credits-Remaining
gatewayMode"passthrough" | "translated"?Cova-Gateway-Mode
translationWarningstring?Cova-Translation-Warning (comma-separated list of dropped fields, only when translated)

In the streaming final SSE frame, requestCost is set only when usage.costUsd > 0. If no usage was extracted (e.g. upstream errored), the field is absent and the SDK should fall back to the /meta endpoint.

Retrieving Metadata After Streaming

For streaming requests, cost and credit fields are not available as response headers. Use the /meta endpoint to retrieve the CovaGatewayMeta after the stream completes:

curl https://gateway.corevalue.dev/gateway/v1/meta/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer sk-cova-XXXXXXXXXXXXXXXX"
import requests

resp = requests.get(
    "https://gateway.corevalue.dev/gateway/v1/meta/550e8400-e29b-41d4-a716-446655440000",
    headers={"Authorization": "Bearer sk-cova-XXXXXXXXXXXXXXXX"},
)
meta = resp.json()  # CovaGatewayMeta
const resp = await fetch(
  "https://gateway.corevalue.dev/gateway/v1/meta/550e8400-e29b-41d4-a716-446655440000",
  { headers: { Authorization: "Bearer sk-cova-XXXXXXXXXXXXXXXX" } },
);
const meta = await resp.json(); // CovaGatewayMeta

The /meta endpoint returns:

  • 200 — metadata found. Returns CovaGatewayMeta JSON.
  • 202 — requestId not found; may be in-flight, expired (TTL 5 min), or never existed. Returns { status: "pending" }.
  • 404 — requestId exists but belongs to a different organization.

Metadata TTL is 300 seconds (5 minutes) after the request completes. The requestId must be a valid UUID v4.

On this page