Skip to content

Formats & event schema

A trace is the record Axium emits for every request that reaches a proxy app runtime: successful, failed, denied by a policy or served from cache. This page is the contract for anyone consuming traces, whether from the PostgreSQL table or from an HTTP batch.

Schema version 1 (header x-axium-trace-version on HTTP; telemetry.schema_version in PostgreSQL)
Cardinality Exactly one trace per request
Fields emitted 42, always present, always in the order below
Computed in PostgreSQL only latency_ms, tokens_total, cost
Unknown values null. Token counts are never defaulted to 0.
Timestamps ISO-8601 in UTC with milliseconds (2026-10-19T10:15:30.120Z)

Fields

Identity

Field Type Description
trace_id uuid Minted when the request enters Axium and returned to the client in the x-axium-trace-id response header. Lets a client report "this request" and you find it.
span_id uuid One span per trace in this version.

Timing

Field Type Description
started_at timestamp When the request entered Axium.
ended_at timestamp When the response finished. For streams, when the last chunk was sent.
latency_ms integer · computed ended_at - started_at.

Origin

Field Type Description
source_module proxy Reserved for other modules in future versions.
proxy_app_id uuid · null The proxy app in the URL. null only when the app does not exist.
api_key_id uuid · null The key that authenticated.
client_ip string · null req.ip. The real client only with TRUST_PROXY=true behind a proxy. Personal data.
client_country null Reserved. No geolocation in this version.
client_user_id string · null Value of the x-axium-user-id request header, if the client sent one.

Provider and model

Field Type Description
provider_id uuid · null
provider_type openai · azure-openai · google-aistudio · google-enterprise · null
provider_region string · null Provider's configured region, global by default.
model_id uuid · null Axium's model record.
model_used string · null Deployed model name sent upstream. On Vertex includes the publisher: google/gemini-2.5-flash, anthropic/claude-….
model_requested string · null What the client asked for. null when the app's default model was used.
dialect openai · gemini Wire protocol of the gateway.
surface see below Which endpoint family was called.
upstream_path string · null Path sent to the provider, e.g. /v1/chat/completions. null if the request never left Axium (denied, cache hit).

surface values: openai.chat, openai.responses, openai.models, openai.files, gemini.generate, gemini.interactions, gemini.models, gemini.files, vertex.generate.

Tokens

All integer · null. null means the provider did not report it.

Field Source
tokens_in Prompt tokens.
tokens_out Completion tokens.
tokens_cache_read Prompt tokens served from the provider's cache (cached_tokens).
tokens_cache_write Reserved, always null.
tokens_reasoning Reasoning tokens (reasoning_tokens).
tokens_saved Reserved, always null.
tokens_total computed: tokens_in + tokens_out.

Cost

All number · null, six decimals, in the currency of the prices configured on the model. null when the model has no price.

Field Definition
cost_in tokens_in × cost_input_1m / 1e6. Cached tokens are billed at full input price.
cost_out tokens_out × cost_output_1m / 1e6.
cost_saved Reserved, always null.
cost computed: cost_in + cost_out.

Request shape

Field Type Description
stream boolean Whether the client asked for streaming.
max_tokens integer · null max_completion_tokens or max_tokens (chat), max_output_tokens (responses), generationConfig.maxOutputTokens (Gemini/Vertex).

Result

Field Type Description
status success · error · denied · timeout · cancelled denied = Axium refused before calling the provider. cancelled = the client closed a stream early.
http_status integer What the client received.
error_origin axium · provider · null Who produced the error.
error_category see below · null Normalised category.
error_message string · null The provider's error.message, or Axium's own message.
cache_hit boolean · null true/false only when the cache policy ran; null otherwise.
fallback_used null Reserved.

error_category values: auth_error, permission_denied, not_found, rate_limit, quota_exceeded, content_filter, invalid_request, server_error, timeout, connection_error, unknown. For non-streamed provider errors it is derived from the status: 401 → auth_error, 403 → permission_denied, 404 → not_found, 429 → rate_limit, 5xx → server_error, other 4xx → invalid_request.

Policies

Field Type Description
policies_applied string[] Policies that ran, whether or not they changed anything: firewall.inbound, dlp_prompt, cache.
denial_reason string · null Only with status = denied. See the table below.
denial_reason HTTP Meaning
app_not_found 404 No proxy app with that id.
app_not_ready 403 App is draft or inactive.
budget_exhausted 429 The app's budget for the current period is spent.
model_ambiguous 400 The name matches more than one allowed model.
model_not_allowed 403 Model is not in the app's allow-list.
no_default_model 400 Client sent no model and the app has no default.
default_model_unusable 403 The default model is inactive or not allowed.
provider_inactive 403 The model's provider is deactivated.
provider_type_mismatch 400 E.g. an OpenAI model through the Gemini gateway.
dialect_mismatch 400 The model's provider does not speak this gateway's protocol.
streaming_unsupported 400 Streaming requested on a surface that does not support it.
firewall_denied 403 Client IP blocked by firewall.inbound.
dlp_blocked 403 dlp_prompt found data the policy blocks.
policy_config_invalid 500 The app's policy document could not be applied.

Context

Field Type Description
meta_tag string · null Value of the x-axium-meta-tag request header.
tags object (string → string) Every other x-axium-<name> request header, keyed by <name>. Repeated headers are dropped.
attributes object Structured findings from policies. Today only dlp_prompt.

attributes.dlp_prompt:

{
  "action": "none | keep | mask | block | error",
  "entities": [ { "entity": "IBAN", "kind": "financial", "country": "ES", "confirmed_by": "checksum" } ],
  "error": "optional message when action = error"
}

Entity names and kinds only. The matched values are never recorded.

Sending context from your application

Three request headers feed the trace and are never forwarded to the provider:

x-axium-user-id: user-42            → client_user_id
x-axium-meta-tag: release-2026.10   → meta_tag
x-axium-team: sales                 → tags.team
x-axium-feature: summarise          → tags.feature

Most SDKs accept default headers:

new OpenAI({ apiKey, baseURL, defaultHeaders: { 'x-axium-user-id': user.id, 'x-axium-team': 'sales' } });

Examples

Successful chat completion (envelope)
{
  "version": 1,
  "data": [
    {
      "trace_id": "3f0d7a0e-8a56-4f4a-9b7b-6d1a7c2b9d10",
      "span_id": "c2a1f0b4-1c3e-4a9d-8e1f-0a2b3c4d5e6f",
      "started_at": "2026-10-19T10:15:30.120Z",
      "ended_at": "2026-10-19T10:15:31.480Z",
      "source_module": "proxy",
      "proxy_app_id": "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
      "api_key_id": "7e6d5c4b-3a2f-4e1d-9c8b-7a6f5e4d3c2b",
      "client_ip": "203.0.113.10",
      "client_country": null,
      "client_user_id": "user-42",
      "provider_id": "11111111-2222-4333-8444-555555555555",
      "provider_type": "openai",
      "provider_region": "global",
      "model_id": "22222222-3333-4444-8555-666666666666",
      "model_used": "gpt-4o-mini",
      "model_requested": "gpt-4o-mini",
      "dialect": "openai",
      "surface": "openai.chat",
      "upstream_path": "/v1/chat/completions",
      "tokens_in": 120,
      "tokens_out": 35,
      "tokens_cache_read": 0,
      "tokens_cache_write": null,
      "tokens_reasoning": 0,
      "tokens_saved": null,
      "cost_in": 0.000018,
      "cost_out": 0.000021,
      "cost_saved": null,
      "stream": false,
      "max_tokens": 256,
      "error_origin": null,
      "status": "success",
      "http_status": 200,
      "error_category": null,
      "error_message": null,
      "cache_hit": false,
      "fallback_used": null,
      "policies_applied": ["firewall.inbound", "dlp_prompt", "cache"],
      "denial_reason": null,
      "meta_tag": "release-42",
      "tags": { "department": "sales" },
      "attributes": { "dlp_prompt": { "action": "none", "entities": [] } }
    }
  ]
}
Denied by the model allow-list (one NDJSON line)
{"trace_id":"5c1e…","span_id":"a9f3…","started_at":"2026-10-19T10:20:00.000Z","ended_at":"2026-10-19T10:20:00.004Z","source_module":"proxy","proxy_app_id":"9a1b…","api_key_id":"7e6d…","client_ip":"203.0.113.10","client_country":null,"client_user_id":null,"provider_id":null,"provider_type":null,"provider_region":null,"model_id":null,"model_used":null,"model_requested":"gpt-5","dialect":"openai","surface":"openai.chat","upstream_path":null,"tokens_in":null,"tokens_out":null,"tokens_cache_read":null,"tokens_cache_write":null,"tokens_reasoning":null,"tokens_saved":null,"cost_in":null,"cost_out":null,"cost_saved":null,"stream":false,"max_tokens":null,"error_origin":"axium","status":"denied","http_status":403,"error_category":"permission_denied","error_message":"Model \"gpt-5\" is not allowed for this proxy app","cache_hit":null,"fallback_used":null,"policies_applied":["firewall.inbound"],"denial_reason":"model_not_allowed","meta_tag":null,"tags":{},"attributes":{}}
Cache hit

status = success, http_status = 200, cache_hit = true, all token and cost fields null, upstream_path = null. The client also receives the x-axium-cache: hit response header.

Stream cancelled by the client

status = cancelled, stream = true, ended_at at the moment the connection dropped, token fields filled with whatever the provider had reported before the cut, usually null.

Versioning

The schema version increments when a field changes meaning or is removed. Adding a field does not bump it. Consumers should ignore unknown fields and check x-axium-trace-version (HTTP) or telemetry.schema_version (PostgreSQL).