How billing works
The flow#
- Your tool call finishes, successfully or not.
- Your server posts a usage event with an idempotency key and a status.
- The event is written to Postgres unconditionally, before anything touches Stripe.
- If the status is billable, the event is reported to Stripe Billing Meters.
- If the Stripe call fails, the event stays in the database with
syncedAt: nullinstead of disappearing into a silent catch.
That ordering is the core decision. The index @@index([status, syncedAt]) exists for the retry job that picks up unsynced events. Implementing that job is documented as your responsibility, not something the boilerplate hides from you.
Why write to Postgres first#
Stripe confirmation can arrive after the tool has already executed. If Stripe were the source of truth, a failed API call would erase the fact that work happened. With Postgres first, you always have a complete audit trail, and Stripe is a projection of it.
Next#
- Reporting usage — the request shape
- Billing decisions — retries, partials and streams
- mcp-metering — the same pattern, free and open source