Sessions
Group related requests into a session to keep prompt caches warm and to observe a conversation end-to-end in the activity log.
A session ties together the requests that belong to the same conversation or workflow. By attaching a stable session identifier to your requests, Halfbill can treat them as a unit — keeping upstream prompt caches warm across turns and letting you trace and filter the whole conversation in the dashboard.
Setting the session id
For chat completions, the session key is resolved in priority order — the first present value wins:
- The
x-session-idheader - The
x-session-affinityheader (sent automatically by coding agents such as opencode) - The
prompt_cache_keybody field (OpenAI-compatible) - The
userbody field (OpenAI-compatible)
curl -X POST "https://api.halfbill.uk/v1/chat/completions" \
-H "Authorization: Bearer $HALFBILL_API_KEY" \
-H "Content-Type: application/json" \
-H "x-session-id: conversation-9f8e7d6c" \
-d '{
"model": "claude-sonnet-5",
"messages": [{"role": "user", "content": "Hello!"}]
}'Reuse the same session id for every request in a conversation. If you don't set any of the values above, the request simply has no session and behaves exactly as before.
Anthropic Messages endpoint
For the Anthropic Messages endpoint (/v1/messages), the session key is derived automatically from metadata.user_id. Coding agents such as Claude Code send a JSON object there (e.g. {"session_id":"<uuid>",…}); Halfbill uses its session_id field. An explicit x-session-id header still takes precedence.
Upstream prompt-cache routing
OpenAI uses an OpenAI-style prompt_cache_key to route requests to the cache shard that already holds your prompt prefix — without it, repeat requests can land on different backends and miss the cache under load.
When a request has a session id and you didn't send a prompt_cache_key yourself, Halfbill forwards a keyed hash (HMAC-SHA256 with a Halfbill-side secret) of the session id as the prompt_cache_key to OpenAI. Hashing means your raw session ids are never exposed upstream; the hash is stable per session, which is all cache routing needs. A prompt_cache_key you set explicitly takes precedence and is forwarded as-is.
Anthropic uses a different caching mechanism (cache_control breakpoints), so the prompt_cache_key doesn't apply to Claude models. See Provider Cache Control.
Observing sessions in the activity log
Every request is logged with its resolved session id. In the dashboard Activity view you can:
- See the Session ID on each request's metadata, alongside the request and trace IDs.
- Filter by session id using the search field next to the custom-metadata search, to pull up every request that belongs to a conversation in one place.
This makes it easy to follow a full conversation end-to-end — inspecting what each turn cost and how many tokens were served from cache.
The session id is distinct from freeform metadata. Use metadata custom headers for arbitrary tags (user, tenant, app version); use the session id for the one value that should keep a conversation traceable.
How is this guide?