Appearance
Spend reconciliation
Reconcile the AI bill the same way you reconcile a bank statement. Each row that comes from your provider's billing API lands in an inbox. You confirm the matches Flowstate found, write off the orphans with a note, defer what isn't ready, and move on.
Provider invoices and on-device telemetry never agree to the cent. Trial seats, retainer drawdowns, shared keys, billing lag, currency rounding — every one of them produces a delta. The job of this page is to make the deltas explicit and ledger-able.
How a row gets here
Two pipes feed the queue:
- Provider billing pulls. Configured at Usage providers. Flowstate pulls the daily-or-finer billing snapshot from each provider's admin API.
- Observed sessions. Captured by the Flowstate Agent on developer machines, with per-call token counts.
The reconciler attempts to match each billing row to the observed sessions for the same provider, same employee, same usage date. Confidence is propagated as a status.
The five buckets
Every billing row sits in exactly one of these at any time:
| Status | What it means | How you act |
|---|---|---|
unreconciled | Billed, no matching sessions found yet | Investigate, or MARK it as write-off / defer |
suggested | Auto-matched; awaiting confirm | CONFIRM if the match looks right |
auto_matched | High-confidence auto-match (acts like suggested) | CONFIRM to finalise |
confirmed | Explicit acceptance; counts toward the spend ledger | Locked unless you reopen |
written_off | Billed but won't be matched, note required | Captured in the residual variance line |
deferred | Kicked into the next period | Returns automatically next month |
The page opens filtered to unreconciled only — that's the work queue. The filter chips on the toolbar widen the view.
Actions per row
Each row carries the buttons that make sense for its status:
- Match (suggested / auto-matched) — fires the
CONFIRMaction. - Mark (unreconciled) — opens a dropdown: write off, or defer.
- Write off — opens a note-required dialog. The note is mandatory; it ends up on the audit trail.
- Defer — kicks the row to next month without a note.
The action mutation (CONFIRM / WRITE_OFF / DEFER) is atomic. After the mutation, the row re-renders with its new status, and the period summary at the bottom of the table recomputes.
Period summary
The bottom row of the table is a four-column dense summary for the selected month:
| Column | Meaning |
|---|---|
| Billed | Sum of totalCostUsd across all provider-billing rows for the period |
| Observed | Sum of session costs Flowstate captured for the same period |
| Written off | Sum of rows you've explicitly marked as won't-match |
| Residual | Billed − Observed − Written off — the gap that's still unexplained |
A healthy month has a small residual. A persistent residual above a few percent usually means a provider billing pull is misconfigured or a chunk of seat traffic isn't being captured (check Providers inventory for monitoring-gap rows).
Rows can come from any provider you've connected — your Cursor seats and Claude API line sit alongside your image-gen line (e.g. Midjourney, OpenAI Images), your video-gen line (e.g. Runway, Sora), and your sales-copilot or legal-research line for orgs that use them. Whatever the modality, the bucket model is the same. See Catalog for which providers are wired in today.
Period selector
The toolbar carries a month input — pick any past month or the current one. The default is the current month; rows for completed months can still be reopened by setting unreconciled back via the audit log.
What this isn't
This isn't the chart-of-accounts. It isn't a finance-grade ledger of every transaction. It's a reconciliation surface — the bridge between the provider's invoice and the workforce-attributed spend you actually want to plan against. Confirmed rows feed the official spend ledger downstream in Finance & Compliance.
Empty state
If a period has no billing rows in the buckets you've filtered to, the page shows a tick-icon empty state. Either you've actually cleared the queue (good) or the billing pull hasn't run yet (check the integration status on Usage providers).