Read a Lobstack receipt
Read the receipt Lobstack returns on every model call — cost_usd, savings_usd, baseline_reason — and report cost from it instead of estimating.
Version 1.0.0 · 180 lines
What the file covers.
Use when a response carries x_lobstack or an x-lobstack-* header, when asked what a call or a run cost, or when a cost renders as $0.00.
- 01
Where the number is
- 02
Three rules
- 03
Reading it
- 04
Also worth keeping
- 05
Do not
The whole SKILL.md, and where it goes.
In Lobstack, open Skills › Add a skill and paste it. In Claude Code, make the folder and paste it in, then start a new session.
mkdir -p .claude/skills/lobstack-receipts
# paste SKILL.md into .claude/skills/lobstack-receipts/SKILL.md---name: lobstack-receiptsdescription: "Read the receipt Lobstack returns on every model call — cost_usd, savings_usd, baseline_reason — and report cost from it instead of estimating. Use when a response carries x_lobstack or an x-lobstack-* header, when asked what a call or a run cost, or when a cost renders as $0.00."license: MITmetadata: title: "Read a Lobstack receipt" version: 1.0.0 author: lobstack category: cost tags: [cost, receipts, metering, gateway]--- # Read a Lobstack receipt Every response from the Lobstack Gateway carries what the call cost. Read it.Never multiply token counts by a rate card you are holding in your head — thatis how a bundled price table drifts out of date and reports $0.00 next to areal charge, which is a mistake this product shipped for three months. ## Where the number is There are two paths and they carry the receipt differently. This is not adetail; a client that only handles one of them under-reports half its traffic. Buffered (stream: false) — HTTP response headers: ```x-lobstack-request-id: 8f2b1e0c-4d5a-4b91-9c3e-6a7f0d2b5c41x-lobstack-model: gemini-3.8-flash # what actually served itx-lobstack-tier: standardx-lobstack-complexity: 47x-lobstack-routed: truex-lobstack-mode: managed # managed | byok | directx-lobstack-cost-usd: 0.000017 # empty string when unpricedx-lobstack-savings-usd: 0.000352x-lobstack-priced: truex-lobstack-metered: truex-lobstack-baseline-reason: namedx-lobstack-baseline-model: claude-opus-5x-lobstack-baseline-usd: 0.000369``` Streamed (stream: true) — the final SSE frame, under x_lobstack: ```json{ "id": "chatcmpl-…", "object": "chat.completion.chunk", "choices": [], "usage": { "prompt_tokens": 14, "completion_tokens": 9, "total_tokens": 23 }, "x_lobstack": { "request_id": "2f1c…", "served_model": "gpt-5.6-luna", "requested_model": "claude-opus-5", "routed": true, "cost_usd": 0.000017, "savings_usd": 0.000352, "priced": true, "baseline_model": "claude-opus-5", "baseline_reason": "named", "baseline_cost_usd": 0.000369 }}``` There is no x-lobstack-cost-usd header on a streamed response, and therecannot be: the headers are written before the provider has counted a singletoken. If you are streaming, the receipt is on the last frame or you do not haveit. usage keeps the exact OpenAI shape so a foreign SDK parses it unchanged;the money hangs off x_lobstack, which those SDKs ignore. ## Three rules ### 1. null is not zero cost_usd is null — never 0 — when the Gateway could not price thecall, and priced is then false. A model key outside the registry gets acompletion and no price. Render null as unknown. Never as $0.00, never as free, and neversummed into a total that you then present as exact. Writing off a real charge aszero is the single most expensive way to be wrong about money, and a UI willhappily do it for you if you let a null fall through a ?? 0. ```ts// wrong: turns "we do not know" into "it was free"total += receipt.cost_usd ?? 0; // right: a total, and a count of what it could not includeif (receipt.cost_usd === null) unpriced += 1;else total += receipt.cost_usd;// report both: "$4.21 across 1,190 calls; 3 calls unpriced"``` ### 2. savings_usd is meaningless without baseline_reason savings_usd is a subtraction, and a subtraction is only honest if the readercan see where the first operand came from. Two reasons exist: - `named` — the caller asked for baseline_model and the router served something else. savings_usd is a measurement against a model somebody actually chose. Report it as a saving.- `plan_ceiling` — the caller said "auto" and named nothing. baseline_model is the most expensive model their plan would have allowed: what they *would* have paid had they asked for the best one. Nobody requested it. It is a real number and also the most flattering one available. Print the reason next to the figure, or do not print the figure. Label the secondcase as "vs plan ceiling", never as "saved" — presenting a plan-ceilingcomparison as a saving publishes a number the customer never chose to bemeasured against. baseline_reason is null, with savings_usd also null, when thereis nothing to compare: the requested model is the served model, or the modelcannot be priced. That is the correct third answer, not a gap to fill. ### 3. cost_usd is what the customer owes On mode: managed it is the provider's list price inside the plan's includedusage and on Free, and the list price times 1.5 past it(provider price + 50%); rate on the receipt says which, andprovider_cost_usd carries the list price beside it. It is pass-through onbyok and direct — because on BYOK the customer already paid their ownprovider and marking that up would be inventing a charge. So a BYOK receipt showing cost_usd is telling you what the tokenscost at the provider, not what will appear on a Lobstack invoice. ## Reading it ```tsimport OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.LOBSTACK_API_KEY, baseURL: "https://www.lobstack.ai/api/gateway/v1",}); // Buffered: headers, via the raw response.const { data, response } = await client.chat.completions .create({ model: "auto", messages: [{ role: "user", content: "…" }] }) .withResponse(); const costHeader = response.headers.get("x-lobstack-cost-usd");const cost = costHeader ? Number(costHeader) : null; // "" means unpricedconst requestId = response.headers.get("x-lobstack-request-id");``` ```python# Streamed: the last frame with a usage block is the receipt.receipt = Nonefor chunk in client.chat.completions.create(model="auto", messages=msgs, stream=True): raw = chunk.model_dump() if raw.get("x_lobstack"): receipt = raw["x_lobstack"] for c in chunk.choices: print(c.delta.content or "", end="") if receipt and receipt["priced"]: print("\ncost $%.6f on %s" % (receipt["cost_usd"], receipt["served_model"]))elif receipt: print("\ncost unknown - model not priced")``` ## Also worth keeping - request_id is the id of the trace row and is returned even on failures that happen before authentication finishes. Quote it in a support thread and a description becomes a lookup. Log it on every call; it costs nothing.- requested_model vs served_model is the routing decision, in the response. When they differ, routed is true.- x-lobstack-dropped-params: temperature means a sampling parameter you sent was not forwarded, because the target model rejects it with a hard 400. The request succeeded. Nothing is wrong. Do not retry.- x-lobstack-metered: false means the completion happened but the ledger write failed. You were served and the row is missing. Worth an alert. ## Do not - Do not compute cost from token counts and a local price table. Read it.- Do not sum null as zero.- Do not show a saving without its reason.- Do not assume the buffered and streamed shapes are interchangeable.
More SKILL.md files, from Cost first.
Cost on its own shelf, or every developer skill.
- Cost
Pick a model tier, not a flagship
Choose the cheapest model tier that can actually do the task instead of defaulting to a flagship, and verify the choice before spending on it.
SKILL.md · MIT - Cost
Keep an agent inside a budget
Hold an agent, job or run inside a hard dollar budget using the per-call cost the Gateway returns, and handle the 402 when an allowance runs out.
SKILL.md · MIT - Cost
Write a cost-aware system prompt
Write or review a system prompt that does not quietly multiply the bill — bounded output, no re-sent bulk, tool schemas counted as the input tokens they are.
SKILL.md · MIT - Migration
Migrate an SDK call to the Gateway
Move existing OpenAI or Anthropic SDK code onto the Lobstack Gateway by changing the base URL and the key, and verify it actually routed.
SKILL.md · MIT - Observability
Audit a codebase for unmetered model calls
Find model calls that bypass the gateway and therefore appear on no bill anyone reads, and detect the opposite fault of counting one call twice.
SKILL.md · MIT - Migration
Put Cursor or Aider behind the Gateway
Route Cursor, Aider, Continue or any OpenAI-compatible tool through the Lobstack Gateway with a local proxy, and see a cost line per request.
SKILL.md · MIT