Wire Ringside spend into Stripe metered billing
20 minSelling access to an AI model is a resale business, and resale only works if you know your cost of goods to the fraction of a cent. That's the whole problem here. Every call your users make costs you a small, awkward, non-round number, and you need to turn a stream of those into a clean monthly invoice with your markup on top. Ringside tells you what each call consumed as it happens. Stripe knows how to turn a running tally into a bill. This tutorial is the wire between them: a webhook lands, you translate it into a meter tick, and at month end Stripe charges the card. You never build an invoice.
- • A Ringside API key (do the quickstart first if you don't have one)
- • A Stripe account with billing enabled + the Stripe CLI installed
- • Node 20+ with a tunnel (ngrok, Cloudflare Tunnel, or deployed app)
- • Your end-users already mapped to Stripe Customer IDs
- • 20 minutes
Register a webhook endpoint
// POST /v1/webhooks
Ringside emits an event every time a chargeable API call completes. Register the URL of your receiver and pick the events you care about. The endpoint is /v1/webhooks and the id you get back is prefixed wh_.
The signing secret comes back in the response once. Store it in an env var. You'll use it to HMAC-verify every delivery.
Ringside retries deliveries with exponential backoff on any non-2xx, and stamps x-fc-signature, x-fc-event-id, x-fc-event-type and x-fc-delivery-id on every attempt. Retries of the same event share an event id, so key your handler on x-fc-event-id to stay idempotent. Treat delivery as best-effort: reconcile against the usage API on a schedule rather than assuming every event arrived.
Create a Stripe meter + metered Price
// define the billable unit
A Stripe meter is the server-side aggregator. A metered Price attaches a rate to it. You can run both from the Stripe CLI or the dashboard.
We meter tokens, because tokens are what the Ringside event actually carries. Use ringside_tokens as the event name so every meter event points at the same bucket, and put your price-per-token (cost plus your margin) on the Stripe Price.
Receive the webhook
// verify the x-fc-signature HMAC
A minimal Express handler. The critical part is the signature check, and it has a specific shape worth getting right. The header is x-fc-signature and its value is t=<unix_ts>,v1=<hex>.
The HMAC is computed over <ts>.<raw_body> — the timestamp, a literal dot, then the body. Hashing the body on its own will never match. Compare the digests in constant time and reject anything outside a 5-minute window.
Use express.raw() (not express.json()) or Node's built-in raw-body read. The HMAC covers the exact bytes on the wire - JSON re-serialization silently breaks verification. During a secret rotation a single delivery can carry two v1= pairs; accept the delivery if either matches a secret you hold.
Post a Stripe meter event from the handler
// stripe.billing.meterEvents.create
The chat.completion.finished payload is {customer_id, id, model, usage, finish_reason}, where usage holds prompt_tokens, completion_tokens and total_tokens.
Note what is not there: the event carries no dollar figure and no metadata blob. So you meter the token count, and you look up the Stripe customer from your own store, keyed on the customer_id the event gives you. The money is decided by the rate on your Stripe Price, not by a field on the event.
If you need the dollar cost rather than a token count, read it from the usage API (GET /v1/customers/<id>/usage) on a schedule and reconcile - that endpoint computes per-period spend. The webhook is the low-latency signal; the usage API is the source of truth.
Self-serve billing portal
// stripe.billingPortal.sessions.create
Drop a Next.js route handler that mints a billing-portal session for the signed-in user's Stripe customer id.
Redirect the user to the returned URL and Stripe handles invoice downloads, card updates, and cancellation. No custom UI to build.
You built it.
Ringside usage flows to your webhook, gets HMAC-verified, translates to Stripe meter events, and your customers self-serve invoices through the portal. Your margin is the difference between the Ringside cost and the Stripe Price you set.