Request IDs and metadata

Read the X-Request-Id header and each call's request_id, the Cruxy-Version header and the cruxy block, and quote a request when you report a problem.

Every response the gateway sends carries an X-Request-Id header, and a call that succeeds also returns its own per-call request_id in the body. On an error, the two are the same value. On a success they differ, and the body's id is the one request logs and feedback take. Every response also names the API version that served it, in the Cruxy-Version header.

# -i prints the response headers above the body. X-Request-Id and
# Cruxy-Version are headers; this call's own request_id is in the body, at
# cruxy.request_id.
curl -i https://api.cruxy.ai/api/v1/chat \
  -H "Authorization: Bearer $CRUXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mira",
    "messages": [
      { "role": "user", "content": "Hello" }
    ]
  }'

The X-Request-Id header

X-Request-Id is a correlation id, sent on every response of every route, whether the call succeeded or failed.

  • You can set it. Send X-Request-Id on the request, up to 128 characters of [A-Za-z0-9_-], and the gateway reuses your value. Any other value, or none, gets a new UUID.
  • On an error, it equals the body's request_id. The JSON error envelope (code, error, request_id) carries the same value as the header.
  • On a success, it is not the call's id. A success returns a separate per-call id, cruxy.request_id, or request_id on POST /api/v2/chat. Feedback and request logs take that per-call id, not the header.
  • On POST /v1/chat/completions and GET /v1/models, OpenAI's response and error shapes carry no request_id, so the header is the only request id a response there carries.

Quote it when you report a problem; see Report a problem.

# Send your own correlation id. -D - prints the response headers, where
# X-Request-Id echoes it; -o /dev/null drops the body.
curl -s -o /dev/null -D - https://api.cruxy.ai/api/v1/models \
  -H "Authorization: Bearer $CRUXY_API_KEY" \
  -H "X-Request-Id: order-1234-attempt-1"

Where the request ID appears

A success returns its per-call id in the body. Where it sits depends on the endpoint:

ResponseWhere the per-call id is
POST /api/v1/chat, stream: falsecruxy.request_id.
POST /api/v1/chat, stream: truecruxy.request_id on the first frame, the role primer.
POST /api/v2/chatA top-level request_id in the body, and on the stream's start and error frames.
POST /api/v1/embeddings and POST /api/v1/rerankcruxy.request_id.
POST /api/v1/moderationsThe top-level id, formatted modr-<request_id>. Strip the modr- prefix for the same id the other endpoints return.
An error carries request_id in the JSON error envelope, next to code and
error, and its value equals the response's X-Request-Id header. Device
login and native sign-in return the same three fields while pending or refused.

POST /v1/chat/completions and GET /v1/models, the plain OpenAI wire, have no cruxy block, and their OpenAI error shape has no request_id field, so X-Request-Id is the only id a response there carries. When you need a per-call id that request logs and feedback accept, use POST /api/v1/chat.

Log the X-Request-Id of every response and the per-call request_id of every success. The per-call id is the one that ties a line in your logs to the gateway's record of the call.

The Cruxy-Version header

The API is versioned by date, and Cruxy-Version carries the date both ways:

  • On the request, Cruxy-Version: YYYY-MM-DD pins a version. Omit it to get the server's current default.
  • On the response, Cruxy-Version names the version that served the call. Every response carries it, errors included.

A value that is not a YYYY-MM-DD date is 400 invalid_version. A well-formed date this server does not support is 400 unknown_version, and its message lists the supported dates. Both are answered before authentication and before anything is billed.

A few request fields work only under a minimum version, and the Changelog lists each one with its date. Sent without that pin, file_ids is refused with 400 invalid_request, and the error names the version you need in required_version.

A breaking change ships under a new dated version. Ordinary additive fields, new cruxy.* fields among them, ship without one, so ignore JSON fields you do not recognize.

Other response headers

HeaderSent onWhat it carries
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-ResetMetered responsesThe per-minute request window; see Rate limits and budgets.
Retry-AfterEvery 429, and a 503 limits_unavailableThe seconds to wait before you retry.
Cache-ControlEvery JSON responseno-store: no cache, shared or private, may store the response.

The cruxy block

Cruxy-native fields are additive and namespaced under a cruxy key, so an OpenAI client parses the rest of the response unchanged. What the block holds depends on the endpoint.

On chat

On POST /api/v1/chat, the block carries request_id, routed_tier, and reasoning_effort for the turn. The rest appear only when they apply:

FieldWhat it tells you
request_idThe call's id.
routed_tierThe tier that served the call. With model: "auto", this is the tier the router picked.
reasoning_effortThe effort the call ran at, after the tier's default is applied.
memory_recallStored memories were recalled into the turn: the scope and key of each, never the stored value. See Memory.
memory_captureThe turn stored memory: how many memories, and the scope and key of each. See Memory.
rules_appliedStanding rules were injected: how many, and from which rulesets. See Standing rules.
files_omittedFiles attached to earlier messages were left out of this turn, each with the reason.
undeclared_tool_replayReplayed tool exchanges called tools your tools array does not declare, so the model read them as prose. See Handle tool calls.

A stream spreads the block across frames. The role primer carries request_id, routed_tier, and reasoning_effort, plus memory_recall, rules_applied, and files_omitted when they apply. The usage frame carries memory_capture, since what a turn stored is known only once the answer is complete. Streaming shows the frames in order.

Token counts and cost are not in the chat block: they are in usage, which Usage and cost covers.

On other endpoints

  • Embeddings and rerank: cruxy carries request_id and cost.
  • Moderations: no top-level block. A flagged result carries its own results[].cruxy.hazard_codes, the guard taxonomy codes behind the flag, and an unflagged result has no cruxy key; see Moderations.
  • POST /api/v2/chat: no cruxy wrapper. The same signals are top-level fields: request_id, routing (the tier, how it was routed, and the effort), memory_recall, memory_capture, rules_applied, files_omitted, and undeclared_tool_replay.
  • POST /v1/chat/completions: none. That wire carries no Cruxy-native fields.

New cruxy.* fields ship without a new API version. Ignore a field you do not recognize rather than failing on it.

Look up a request

An organization owner or admin can read a call's metadata by its per-call id, the cruxy.request_id a success returned, with GET /api/v1/logs/{request_id}.

# REQUEST_ID holds the cruxy.request_id of an earlier call.
curl https://api.cruxy.ai/api/v1/logs/$REQUEST_ID \
  -H "Authorization: Bearer $CRUXY_API_KEY"
{
  "object": "request_log",
  "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "success",
  "model": "kavi",
  "input_tokens": 1024,
  "output_tokens": 256,
  "latency_ms": 842,
  "key_id": "6f9c1e2a-1b2c-4d5e-8f90-1a2b3c4d5e6f",
  "workspace_id": "2b7c9e4a-3d5f-4a6b-8c1d-9e0f1a2b3c4d",
  "created_at": "2026-07-18T12:34:56.000Z"
}
  • Metadata only. The model, token counts, latency, the lifecycle status (success, error, or partial), the key and workspace that made the call, and when. Prompt and completion content are never stored or returned.
  • Owner or admin only. Anyone else gets 403 logs_forbidden.
  • One answer for a missing id. An id from another organization, or one that names nothing, is 404 log_not_found, so the response never confirms that an id exists.
  • The most recent row wins. One id can map to more than one record, such as a reused Idempotency-Key or an error followed by a retried success; the most recent is returned.

GET /api/v1/logs lists the same metadata for every key in your organization, newest first.

Report a problem

When you contact Cruxy support about a call, send enough to find it without a round of questions:

  • The ids, whole and exactly as the response returned them: the X-Request-Id header, and for a success its per-call request_id as well. If you retried, send the ids of every attempt you have.
  • The Cruxy-Version the response named.
  • The method and path, such as POST /api/v1/chat, and whether stream was on.
  • The HTTP status and the code. The error message alone is not enough; its wording can change.
  • When it happened, in UTC.
  • For a stream, whether it reached data: [DONE], or a finish frame on POST /api/v2/chat.
  • The key_id, if the problem is with a key. Never send the key itself.

To tell Cruxy how a call turned out, rather than report a fault, send Feedback joined to the same request_id.

Next steps

Was this page helpful?