Gateway
Routing data
What the Gateway records about a request, what it cannot record, and the capture that is off unless you turn it on.
Every request through the Gateway leaves a receipt. This page is the complete account of what that receipt contains, what it deliberately leaves out, and what changes if the routing-events capture described below is switched on.
What the receipt holds
The trace written for every request records what the request cost and how it behaved: which model was asked for, which one answered, the provider, the status code and error class, latency, time to first token, token counts, and the price. It is what Logs and Usage & cost read.
It holds nothing about what the request was. There is no prompt, no completion, no message content and no hash of any of it. That is not a setting; there is no column for it.
The scorer computes a breakdown of 7 signals on every request and uses it to pick a tier. None of that reaches the trace either — only the final score does, on the x-lobstack-complexity header. If you want to see the working for a specific request, use route-preview, which returns the full breakdown without serving anything.
The routing-events capture
A second record can be written alongside the trace, holding the shape of the request and how the routing decision turned out. It exists so the routing decision can eventually be learned from real traffic rather than guessed from public prompts.
It is off unless two environment variables are set, and with them unset nothing is read, built or written — the message is not even carried past the scorer that already read it.
Every field it records
The list below is generated from the writer itself, so it is the whole set and cannot drift from what is actually stored.
| Field | What it is | |
|---|---|---|
| Which request | request_id | The trace id also returned to you as x-lobstack-request-id. |
org_id | Your organization. | |
api_key_id | Which key was used, not the key itself. | |
| The shape of the input | prompt_hash | A keyed hash of the last user message. See below — this is a pseudonym, not anonymity. |
hash_key_id | Which secret produced the hash, so a rotation is visible instead of silent. | |
prompt_chars | How many characters the message ran to. Not the characters. | |
conversation_len | How many messages were already in the conversation. | |
| The decision | complexity | The published 0–100 score. |
complexity_raw | The sum before the cap, so a 145 and a 100 stay distinguishable. | |
complexity_capped | Whether the cap bound. | |
scorer_version | The scorer that produced it, currently ti-1.0.0. | |
signals | Which of the signals fired and what each contributed. | |
requested_model | What you asked for — a model name, or auto. | |
served_model | What answered. | |
tier | The tier chosen. | |
ceiling_tier | The highest tier the request could have reached, so a capped decision is not mistaken for a free one. | |
routed | Whether Lobstack chose, or you pinned. | |
price_aware | Which ordering produced the choice, so a setting change is not read as a behaviour change. | |
plan_tier | The plan in force. | |
mode | managed, byok or direct. | |
| The outcome | status_code | What the caller got back. |
error_class | One of the six error classes, when it failed. | |
latency_ms | End to end. | |
ttft_ms | Time to first token, streaming only. | |
prompt_tokens | Counted by the provider. | |
completion_tokens | Counted by the provider. | |
cost_usd | Null when the call could not be priced — never zero, which would say it was free. |
What it never records
No prompt text. No completion text. No system prompt, tool arguments, file names, or any substring of them. Not truncated, not redacted, not “just the first fifty characters”.
This is enforced by the shape of the table rather than by a rule someone has to remember: there is no column that could hold it. A check in the build additionally refuses any row containing a span of the prompt, and that check is itself tested by planting a leak and requiring it to be caught.
The hash is a pseudonym, not anonymity
prompt_hash exists for one job: recognising that two requests carried the same input, so that asking a cheap tier and then re-asking a dearer model can be identified as a pair.
It is HMAC-SHA256 under a server-side secret rather than a bare digest, and the difference matters. A plain SHA-256 of a short prompt — hi, summarise this — is recoverable by anyone with a word list, which is plaintext wearing a hex coat. A keyed hash is useless to anyone without the secret.
It is still data derived from your input, and this page does not call it anonymisation. It is retained on a clock and it is covered by the same handling as the rest of your Gateway data. If that is not acceptable for your traffic, the capture stays off.
Retention
Every routing event carries its own expiry, set when the row is written and defaulting to 90 days. Deleting a request’s trace deletes its routing event with it.
Why it exists, and what it is not yet
The routing decision today is 7 hand-written signals and a threshold table. That is a reasonable first approximation and it is legible, which is why the whole of it is documented on Token Intelligence. What it is not is learned from anything.
Improving it requires knowing when a routing decision was wrong, and that is the one thing a receipt cannot tell you: a request that was routed to a small model and answered badly looks exactly like one that was routed well. Capturing the shape and the outcome is the precondition for ever measuring it.
There is no learned router in the request path. Model selection is the documented heuristic and nothing else, on every request, for every customer. The capture described here is a prerequisite for training one, not evidence that one exists — and if a learned router is ever put in front of traffic, it will be documented here before it is, not after.