New Sign up free, 10 calls on us. Up to $1, no card needed.

Request Headers

Three optional headers the gateway understands: two carry your own trace context through to our logs, one keeps a conversation on the same upstream so prompt caching keeps hitting. Send none of them and nothing changes.

Header What the gateway does Use it for
X-Trace-Id Echoed back verbatim on the response; written to the access log. Tying a call back to your own tracing system.
X-Span-Id Echoed back verbatim on the response; written to the access log. Identifying one span inside that trace.
X-Session-Id Echoed back. Consecutive requests carrying the same value are pinned to the same channel and the same upstream key. Keeping prompt caching warm across a conversation.
X-Request-ID Not taken from you. Your value comes back as X-Client-Request-ID; the X-Request-ID on the response is always the gateway's own UUIDv7. The gateway's identity for the call - quote it in a support ticket.

Sending them

curl https://synthorai.io/v1/chat/completions \
  -H "Authorization: Bearer $SYNTHORAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Session-Id: conv-8f3a2b" \
  -H "X-Trace-Id: 7c1d9e40f2b84a17" \
  -H "X-Span-Id: 2f9b41c8" \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Session affinity

A cache entry lives on one API key at the provider. A channel can hold several keys, and without a hint the gateway spreads calls across them - so the second turn of a conversation can land on a key that has never seen your prefix and pays the write cost again.

Send the same value on every call of one conversation, and a different value for a different conversation. Nothing is stored server-side and nothing expires: the value is hashed to pick the channel and the key, so the same value always resolves the same way.

On the two OpenAI-compatible endpoints - /v1/chat/completions and /v1/responses - you can skip the header entirely: if the request body carries prompt_cache_key, the gateway uses that as the affinity key. On /v1/messages and the Gemini endpoints, send the header.

Affinity is best effort: it only decides which channel is picked among equally preferred ones. Failover, rate-limit cooldown, retries and an explicit routing preference all still win over it. A model's list price is the same on every channel we may pick.

Limits

  • 128 bytes each. Longer values are truncated. A value containing control characters or anything outside printable ASCII is dropped entirely - not echoed, not logged. These values reach a response header and a log line, so this keeps header and log injection out.
  • They stop at the gateway. None of the three is forwarded to the model provider - the outbound request carries only authentication, the content type and provider-specific headers.
  • None of them identifies a call for billing. Billing, deduplication and reconciliation all key on the gateway's own X-Request-ID, never on a value you control.

When something goes wrong

  • Quote the X-Trace-Id you sent, or the X-Request-ID from the response - either one lets us find the exact call.
  • Your trace ID also shows on the request in Usage Analytics, so you can find the call yourself before opening a ticket.
  • To confirm affinity is working, repeat the same long prefix a few times with one X-Session-Id and watch the Cache R and Cache W columns in Usage Analytics: the first call writes to the cache, the later ones should read from it. Change the value and the write should come back.

See also: Prompt Caching · Usage Analytics