Skip to content
This page is a translation of the authoritative Chinese source and may lag behind.View the original

Template variable reference

The gateway supports {{variable}} expressions at several config locations: at request time they're replaced with actual values from the current request context, injecting dynamic info (access key, request ID, session header, system-prompt hash, etc.) into headers or request bodies sent to the upstream, driving scenarios like upstream log correlation, cache keys, and sticky-session bindings.

Variable table

VariableSourceDescription
{{request_access_key_name}}Auth access key nameThe name in the matched AccessKey entry; empty when unauthenticated
{{request_access_key_group}}Access key group nameThe group name the key belongs to (comma-joined for multiple groups); empty when unauthenticated
{{request_id}}z-request-id request headerThe unique request ID in the client's z-request-id header
{{access_key_hash}}Access key SHA-256 hashFirst 16 bytes of the access-key SHA-256 hash, base64url-encoded (22 chars, deterministic)
{{system_prompt_hash}}System prompt SHA-256Lowercase hex digest of the system-prompt content's sha256, protocol-agnostic, compatible with four placements: Anthropic system, OpenAI messages[role=system], DashScope input.messages[role=system], Google system_instruction
{{header:Key}}Client request headerThe value of the client's Key request header, case-insensitive; leading/trailing whitespace treated as missing

Availability (which contexts support which variables)

Not every variable is available at every config location — it depends on whether that location holds the corresponding request context. The table below shows the variables available at each location:

Variableupstream/model headers·user_agentrequest_payload valueload balancer binding_key_template
{{request_access_key_name}}
{{request_access_key_group}}
{{request_id}}
{{access_key_hash}}❌ (always empty)
{{system_prompt_hash}}❌ (always empty)
{{header:Key}}

The two "always empty" cases are context boundaries, not bugs:

  • access_key_hash is always empty in request_payload's value: the body-rewrite phase runs after protocol translation and, for security, doesn't hold the access-key plaintext, so it can't compute the hash. For cache-key fallback use system_prompt_hash.
  • system_prompt_hash is always empty in binding_key_template: binding-key resolution happens at an earlier stage than body availability (route/LB resolution), before the request body is available, so the system prompt can't be extracted. Sticky-session binding keys usually use access_key_hash or a session header.

⚠️ Writing a variable in a context that doesn't support it resolves that variable to an empty string — if it sits at the end of a fallback chain and the rest of the chain is also empty, the whole chain is empty and the header/write is omitted (the binding key falls back to the original API key). Template validation at config load is warn-don't-block and won't lock up startup.

Fallback-chain syntax

{{var1|var2|var3}} separates multiple candidate variables with |, taking the first non-empty one in order: if the previous is unavailable (unauthenticated / header missing / context unsupported), try the next; if all are empty, replace with an empty string. {{header:Name}} can also be a member of a fallback chain. Unknown variables (not in the table, not header:-prefixed) are skipped in the chain rather than erroring.

Empty-value handling differs by config location:

  • header / user_agent: when the resolved value is fully empty, that header is omitted (an empty string is not sent).
  • request_payload's value: when the chain is fully empty, this write is omitted (the field isn't created, and an empty-string constant isn't written).
  • binding_key_template: when the chain is fully empty, it falls back to the original access key as the binding key (guaranteeing per-session uniqueness, avoiding all requests crowding into one dynamic binding session). The same fallback happens when the template contains unresolved {{...}}.

Typical usage

Session-keyed upstream cache key (header, multi-level fallback)

In the console: open the upstream or model → Custom request headers → add header X-Conversation-Id, value {{header:x-claude-code-session-id|access_key_hash|system_prompt_hash}}.

Step-by-step degradation: has a session header → use the header (same session same key, hits the upstream cache); no header but authenticated → use the access key hash (same user shares cache); neither → use the system hash (same system prompt same key). When all three are empty, the header is omitted.

Upstream cache-field injection (request_payload, body level)

In the console: open the model detail (or the upstream's "default request body rules") → Request body rules+ Add rule → mode overwrite → path prompt_cache_key → value {{header:x-claude-code-session-id|system_prompt_hash}}.

Note that access_key_hash is unavailable here (always empty); use system_prompt_hash for cache-key fallback. Rules apply after protocol translation, so the written upstream-private field isn't dropped by translation. See Upstream and model fields — Request body modification.

Sticky-session binding key (binding_key_template)

In the console: open the load balancer → Edit → enable "Session stickiness" → "Binding header" field {{header:X-Session-Id|access_key_hash}}.

Note that system_prompt_hash is unavailable here (always empty); use access_key_hash or a session header for the binding-key fallback. See Enable sticky sessions.