Skip to main content
Attribution is what turns one opaque AI invoice into a bill you can defend. Every call routed through the collector carries three optional context fields — team, workflow, userId. The collector stamps them onto the call metadata it emits to the control plane, and each field drives one lens in the dashboard. You set attribution once as a default and refine it per call. Nothing about attribution changes what leaves your VPC: the full prompt and response stay in your local Postgres, and only the tags below — hashed according to your sensitivity profile — reach Firedog cloud.

The three context fields

team
string
The desk or cost centre that owns the spend — for example macro-desk, credit-research, quant-pnl. Rolls up into the desk lens in Cost allocation, so finance can attribute and charge back a run to the group that spent it. Usually set once on defaultContext per application or service.
workflow
string
The named process the call belongs to — for example market-research, kyc-review, earnings-summary. Populates the workflow register on Home (the reconciled bill, per workflow) and opens the step-by-step workflow detail view where the re-read tax is visible. Best set per call, since one service often runs several workflows.
userId
string
The end user or service account behind the call — for example u_123. It is always salted-hashed inside your VPC before any metadata leaves it, so the dashboard shows stable per-user cost buckets (user_9f3c…) without ever receiving a raw identity. Pass your own internal identifier; the collector does the hashing.
All three fields are optional. A call with none of them still prices, still writes its full record locally, and still reconciles into the total — it just files under Untagged. See Untagged still reconciles below.

Field-to-lens map

FieldHeaderDashboard lensHow it leaves the VPC
teamx-firedog-teamDesk (Cost allocation)Raw under standard/open; hashed under strict
workflowx-firedog-workflowWorkflow register (Home, Workflow detail)Raw under standard/open; hashed under strict
userIdx-firedog-userSalted per-userAlways salted-hashed, every profile

Setting attribution

There are two ways to attach context, and they produce identical metadata. Use the SDK if your code calls the SDK; set the headers directly if you route through the collector as a drop-in proxy.

With the SDK

defaultContext applies to every call from a client. Per-call context is merged on top, so shared tags live on the client and the variable tag rides with the request.
Keep team on defaultContext and set workflow (and userId where you have one) per call. That mirrors reality: a service belongs to one desk but runs many workflows.

With raw headers (proxy path)

If you point an existing OpenAI-compatible client at the collector’s base URL instead of using the SDK, set the same attribution as x-firedog-* headers. You also mint the correlating x-firedog-call-id yourself — a client-side ULID that ties this request to its full VPC record. (The SDK does both for you.)
The header names are the wire format for both paths — the SDK simply serialises context into x-firedog-team, x-firedog-workflow and x-firedog-user, and mints x-firedog-call-id.

How context becomes metadata

The collector prices the call, writes the full record (messages, any RAG chunks, response) to your local Postgres, and emits only CallMetadata to the control plane. The attribution fields on that metadata are exactly your three tags — with userId already hashed, and team/workflow hashed or raw depending on the profile:
There is no raw userId field in the metadata — the collector emits userHash and nothing else for user attribution. The salt lives in your VPC, so the same person maps to a stable bucket in the dashboard, but the cloud never holds a value you could reverse to an identity.

Hashing and sensitivity

What gets hashed before it leaves the VPC is governed by your sensitivity profile, a configurable default rather than a hard guarantee:

strict

Maximal redaction. team and workflow are hashed to opaque tokens (for example Project-Titanwf_7c2a); userId is hashed; identifying categories may be dropped.

standard

Balanced. Raw team and workflow labels, salted userHash, quality signal retained.

open

Richest tags, for teams whose labels are not sensitive.
Regardless of profile, userId is always salted-hashed — there is no setting that emits a raw user identity. Under strict, a desk whose name is itself sensitive (a codenamed project, a client mandate) still reconciles correctly in the dashboard; it just appears under a stable hash instead of a readable label.

Untagged still reconciles

Attribution is additive, never a gate. A call with no team, no workflow and no userId is still priced, still written in full to your VPC, and still counted in the total — it rolls up under Untagged. On Home, tagged plus untagged spend equals the reconciled bill to the cent, so nothing is dropped for lacking a label and the “Untagged” line tells you exactly how much attribution work is left to do.
A healthy rollout drives the Untagged line toward zero over time by adding defaultContext to more services.

Next steps

Sensitivity profiles

Decide what the collector is allowed to emit for each tag.

Quality & shadow testing

Score answers and prove a cheaper model holds up — without leaking content.

Security & data residency

What stays in your VPC, and what the metadata contains.