Automate Usage-Based Billing: Setup Guide
Automate Usage-Based Billing: Setup Guide
Automate usage-based billing by syncing product usage to Stripe metered items every 15 minutes. Field mappings, edge cases, and a 45-minute setup path.
No credit card required
Free 100k syncs every month
If you want to automate usage-based billing, the dominant playbook is overkill: stand up Snowflake, instrument an event collection library, write dbt models, run reverse ETL, then finally push to Stripe. That is a four-system stack before the first invoice ships. For a 20-100 person SaaS team launching consumption-based pricing, the events you need to bill on already live in Postgres or your product analytics tool. The missing piece is a reliable sync that aggregates usage per customer per billing period and pushes the result into Stripe metered items, with the same numbers fanned out to the CRM so sales sees real consumption.
This guide covers the warehouse-optional path: what to meter, how to aggregate, how to map fields to Stripe usage records, and which edge cases will break your invoices if you ignore them.
Why product usage data must reach your billing tool to automate usage-based billing
Consumption pricing only works when usage data reaches Stripe before the period closes. When it does not, three failures repeat across teams launching usage-based billing:
A customer hits 8M tokens. Your product database has the events. Stripe has zero. The invoice generates from a Stripe usage record that never got written, and the customer sees a bill for the base subscription only. You eat the variable cost.
A customer cancels mid-cycle. Their subscription_item disappears in Stripe, but your aggregator keeps writing usage against the stale ID. The next sync fails silently. Two weeks later finance asks why churn revenue is off.
A customer upgrades from a $50 plan to a $500 plan. The subscription_item changes. Your aggregator does not know. Usage continues posting to the old subscription_item until someone notices the discrepancy.
Every one of these is a sync problem, not a billing problem. The Stripe billing engine works fine. What breaks is the pipeline between the product database where consumption is recorded and the Stripe Usage Records API where it gets billed. Most teams do not need a CDP for this. They need a reliable, monitorable sync between two systems they already run.
What product usage events to meter for usage-based billing automation
Pick one billable unit per pricing dimension. The four metrics that actually work as billing inputs across most SaaS:
Pricing model | Billable unit | Where it lives | Granularity |
|---|---|---|---|
API platforms | Calls per endpoint class | Application logs, Postgres | Per request |
Data infrastructure | GB processed or stored | Postgres metadata table, S3 inventory | Per object or per query |
AI products | Model tokens (input + output) | Postgres | Per inference |
Collaboration tools | Active seats per workspace | Postgres | Per day |
Streaming or media | Minutes consumed | Postgres | Per session |
Avoid metering on vague signals like sessions, pageviews, or generic "events." These do not map to customer value cleanly, and customers will dispute the invoices. If you cannot defend the metric in a sales call, do not bill on it.
A note on instrumentation order. The temptation when launching consumption-based pricing setup is to build a perfect event spec first. Skip that. Use what your application already writes. If your API gateway logs every request to Postgres with a customer_id, you have everything you need for API metering. The same goes for inference logs, playback events, or seat assignments. The "you need a dedicated event pipeline" advice is sold by companies that sell event pipelines. Most teams do not need one.
Step-by-step: automate usage-based billing by syncing product usage to Stripe
Here is the wiring, end to end. This works whether your source is Postgres, Mixpanel, PostHog, or BigQuery.
1. Define your billable unit in the source. Write a SQL view (or a saved Mixpanel/PostHog query) that returns one row per customer per billing period per billable unit. Minimum columns: customer_id, billing_period_start, billing_period_end, billable_unit, quantity.
Recompute from raw events on every sync rather than incrementing a counter. Late events still get counted. A retry never double-bills.
2. Connect Stripe as a destination. Use a restricted Stripe API key with write access to Usage Records and read access to Subscriptions and Subscription Items. Oneprofile validates the key against the live Stripe API before saving.
3. Map your aggregated rows to Stripe Subscription Items. Stripe meters usage against a subscription_item, not a customer or subscription. The mapping is: customer_id + billable_unit -> subscription_item_id. Resolve this with a Stripe Subscriptions read at sync time, or maintain a lookup table that updates on subscription changes.
4. Push to Stripe Usage Records. Each sync writes one usage record per (subscription_item, period). Use action=set (idempotent) instead of action=increment (additive) so retries do not double-count. Pass timestamp as the period end, not now(), so usage lands in the correct billing period during late runs.
5. Schedule every 15 minutes. Stripe aggregates usage per billing period regardless of how often you push, so the cadence does not change the invoice. What it does change: how fresh the usage looks in your customer-facing dashboard and your CRM. 15 minutes is the sweet spot for most teams.
6. Add a second sync to the CRM. Reuse the same aggregated view as a source. Push quantity to a current_period_usage property on HubSpot Companies (or Salesforce Accounts, or Attio Workspaces). Now sales sees real consumption per account without opening Stripe and without a BI tool.
A working metering for SaaS billing pipeline takes about 45 minutes to wire up the first time. Most of the time goes into deciding which billable unit to commit to.
Field mapping for usage-based billing: events, aggregates, and customer IDs
The mapping table that matters is from your aggregated source view to Stripe usage records.
Source field | Stripe usage record field | Notes |
|---|---|---|
|
| Stripe meters by subscription_item, not customer. Resolve via the Subscriptions API or a cached lookup table. |
|
| Integer. Round, do not truncate, if your source is fractional. |
|
| Unix timestamp. Pass period end, not now(), so retries land in the correct period. |
n/a |
| Set to |
For the CRM destination, the mapping is simpler:
Source field | CRM property | Property type |
|---|---|---|
|
| Number |
|
| Single-line text |
|
| Date |
Computed: quantity x rate |
| Number (currency) |
Oneprofile creates these custom properties automatically if they do not exist in your CRM. Match keys: email or customer_id. If your CRM stores Stripe customer IDs as a property, prefer that over email. Fewer false matches.
Edge cases that break usage-based billing automation
Four edge cases break invoices in production. Solve them before launch, not after a customer complaint.
Late events. A worker crashes for an hour, then catches up. If your aggregator increments a counter, those events never reach Stripe. If it recomputes from the raw log every sync, late events show up on the next push and the customer is billed correctly. Always aggregate from the source, never from a running counter.
Plan changes mid-cycle. When a customer upgrades, the subscription_item ID changes. Your lookup needs to refresh. Bidirectional sync from Stripe to your product database keeps subscription_item IDs current, so the aggregator always pushes to the right destination. Without that, usage keeps posting to a dead subscription_item and disappears.
Credits and refunds. A customer hits a service degradation and you owe them 1M tokens of credit. Two options: deduct credits in your aggregator before pushing to Stripe (cleanest), or post a negative usage record to Stripe (works, but harder to audit later). Most teams should pick option one. Track credits as their own table, subtract them in the view, and let Stripe see only the net billable quantity.
Disputed usage. A customer disputes a $4,000 invoice and asks for the underlying records. If your aggregation runs against a raw event log, you can reconstruct the bill row by row. If you only kept aggregated counters, you have no audit trail. Keep the raw events for at least one full billing cycle past the dispute window. This is the strongest argument for a stateless recompute on every sync.
What changes after your usage-based billing setup goes live
The Monday-morning scramble disappears. Usage flows from your product database to Stripe every 15 minutes, and Stripe generates the invoice on its normal cycle. Sales opens HubSpot and sees current period consumption per account without a BI tool. Finance reconciles MRR against the same source-of-truth table the aggregator runs against.
Two things tend to surprise teams in the first month after launch:
The first is how often customers ask "what is my current usage?" Once that data exists in the CRM, customer-facing dashboards become a one-week project rather than a one-quarter project. Reuse the same aggregated view, expose it in the app, and you are done.
The second is how visible underconsumption becomes. With current period usage on every account record, the customers paying for a tier they do not use show up immediately. That is a downsell signal, not a growth signal, but knowing it early lets success teams intervene before the renewal conversation. The Hightouch playbook on usage-based billing opens with a disclaimer that it is "not a complete solution." The warehouse-free version skips the disclaimer because the system is actually wired end to end: source, aggregation, billing, CRM, dashboard. No intermediate stops.
Do I need a data warehouse to automate usage-based billing?
What events should I meter for usage-based billing?
How often should usage sync to Stripe?
What happens to late-arriving usage events?
Can I show usage to sales in HubSpot or Salesforce while billing through Stripe?
How does Oneprofile handle plan changes mid-cycle?