> Raw Markdown twin (generated at build time from the source Markdown). Rendered page: https://docs.gatellm.io/en/reference/template-variables · Doc index: https://docs.gatellm.io/en/llms.txt


# 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

| Variable | Source | Description |
|------|------|------|
| `{{request_access_key_name}}` | Auth access key name | The name in the matched `AccessKey` entry; empty when unauthenticated |
| `{{request_access_key_group}}` | Access key group name | The group name the key belongs to (comma-joined for multiple groups); empty when unauthenticated |
| `{{request_id}}` | `z-request-id` request header | The unique request ID in the client's `z-request-id` header |
| `{{access_key_hash}}` | Access key SHA-256 hash | First 16 bytes of the access-key SHA-256 hash, base64url-encoded (22 chars, deterministic) |
| `{{system_prompt_hash}}` | System prompt SHA-256 | Lowercase 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 header | The 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:

| Variable | upstream/model `headers`·`user_agent` | `request_payload` `value` | load 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 {#fallback-chain}

`{{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](/en/reference/upstreams-models-fields.md).

### 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](/en/howto/enable-sticky-session.md).

## Related sections

- Upstream/model header and `user_agent` templates: [Upstream and model fields](/en/reference/upstreams-models-fields.md)
- `request_payload` value expressions: [Upstream and model fields — Request body modification](/en/reference/upstreams-models-fields.md)
- `binding_key_template`: [Load-balancing fields](/en/reference/load-balancing-fields.md)
- Exact fallback-chain resolution behavior: see the [Fallback-chain syntax](#fallback-chain) section on this page
- All environment variables: [Environment variable reference](/en/reference/configuration.md)
