Skip to content
PALM SOUQ

FinOps guide · Attribution

Designing per-tenant LLM cost attribution that finance will trust

Identifiers, headers, pricing at the serving model, cache and reasoning tokens, internal traffic and reconciliation: a build guide for per-tenant attribution.

Rohan Malhotra, VP Engineering3 min read
usage_event.json
tenant
cus_8812
model
Claude Sonnet 4.5
input / cached / output
18,000 / 12,000 / 900
priced at
$3.00 · $0.30 · $15.00
cost
$0.03510
One event per request, priced at the model that served it

Procedure · 6 steps

  1. 1Pick the tenant identifierUse the same customer ID your billing system uses as the tenant key, and send it on every request in X-PalmSouq-Tenant.
  2. 2Add tier, feature and user dimensionsSend X-PalmSouq-Tier, X-PalmSouq-Feature and a pseudonymous X-PalmSouq-User so cost can be grouped by plan, feature and seat.
  3. 3Record every token classStore input, output, cache read, cache write and reasoning tokens separately from the provider usage block on each response.
  4. 4Price at the serving model and timePrice each event at the list or contracted price of the model that actually served it, effective at the request time.
  5. 5Separate internal trafficRoute evaluations, experiments and internal tools under reserved tenant IDs so they stay out of cost of revenue.
  6. 6Reconcile monthlyCompare the attributed total with provider invoices and explain the difference before finance closes the month.

Per-tenant attribution sounds like a logging problem. In practice it is an accounting system, and finance will only use it if it reconciles with the provider invoices and survives edge cases such as caching, retries, fallbacks and internal traffic. This guide walks through the design decisions in the order you need to make them.

1. Use your billing customer ID as the tenant key

Attribution exists to connect cost with revenue, so the tenant key should be the identifier your billing system already uses for the paying account: a Stripe customer ID, a Metronome customer ID, or your own account ID that maps one to one. Avoid workspace or project IDs as the primary key unless they are the unit you bill. If you need finer grouping, add it as a separate dimension.

In PALM SOUQ the key travels in the X-PalmSouq-Tenant header. The SDK sets it from your request context, so application code does not need to pass it through every function call.

2. Add the dimensions you will be asked about

Within a quarter of launching attribution, someone will ask for cost by plan, cost by feature and cost by seat. Capture them from day one: X-PalmSouq-Tier for the plan, X-PalmSouq-Feature for the product surface, and X-PalmSouq-User for a pseudonymous user ID. Backfilling dimensions later is impossible because the request context is gone.

3. Record every token class separately

Providers report usage in different shapes, and the classes are priced differently. Store each class as its own counter on every event: uncached input, cache read, cache write, output, and reasoning tokens where the provider reports them. Folding cache reads into input overstates cost; folding reasoning into output is correct for pricing but hides the cause when a model update doubles your reasoning spend.

Here is one request on Claude Sonnet 4.5: 18,000 input tokens, of which 12,000 are cache reads, and 900 output tokens. Priced at $3.00 uncached input, $0.30 cache read and $15.00 output per 1M, the event costs $0.03510. Pricing all input at the uncached rate would give $0.06750, which overstates this tenant's cost by 92%.

{
  "tenant": "cus_8812",
  "tier": "growth",
  "feature": "contract_review",
  "model": "claude-sonnet-4.5",
  "tokens": { "input": 6000, "cache_read": 12000, "cache_write": 0, "output": 900, "reasoning": 0 },
  "cost_usd": 0.03510,
  "fallback_from": null,
  "ts": "2026-02-24T09:14:07Z"
}

4. Price at the serving model, at request time

Two mistakes are common here. The first is pricing at the requested model when a fallback served the request. The second is repricing history when a provider changes prices. Price each event once, at the model that served it and the price effective at the time, and store the cost on the event. If you have negotiated discounts, keep a price table per provider contract and record which one applied.

5. Handle retries, streams and failures

  • Retries: each upstream attempt that the provider billed is its own event, linked to the same logical request ID.
  • Streams cancelled by the user: providers usually bill tokens generated before the cancel, so record the partial usage they report.
  • Errors: a 4xx before generation is usually free, while a timeout mid-generation may not be. Record what the provider reports, not what you assume.
  • Circuit breaker refusals: record a zero-cost event so blocked attempts appear in the tenant's history.

6. Keep internal traffic out of COGS

Evaluation runs, prompt experiments, staging environments and internal tools should carry reserved tenant IDs. They belong in research and development, and mixing them into a tenant's cost destroys trust in the numbers the first time an evaluation sweep lands on a real customer's line.

7. Reconcile before you publish

At month end, compare the attributed total for each provider with the provider invoice. A gap of a fraction of a percent is normal, from rounding and from billing-period boundaries. A larger gap usually means traffic that bypassed the gateway, a missing token class, or a stale price. Explain the gap in writing each month; finance will trust the per-tenant view once they see that the totals tie out.

Rohan Malhotra

VP Engineering, PALM SOUQ

NIT Trichy. Previously led streaming data infrastructure at Flipkart. Responsible for the token accounting pipeline and the 99.999% availability target.

Don't wait for a surprise $10,000 OpenAI invoice.

Route your agent traffic through PALM SOUQ in 2 minutes. Change one base URL, pass a tenant header, and every token is attributed and governed.

14-day free production trial · 100k requests included · no card required