Subscription and Usage Based Billing
Charging for what somebody used starts with an event log you would be willing to defend in front of that customer. The hard part is not the pricing page, it is that the same event will arrive twice.
Metered billing is a data integrity problem
Companies usually approach usage billing as a pricing exercise. Tiers, allowances, overage rates, a table on a marketing page. That part is the afternoon's work.
The part that decides whether the whole thing survives contact with customers is much less glamorous. A customer will eventually query a line on their invoice, and at that moment you need to produce a record of what they did that you are prepared to stand behind, event by event, in front of somebody who has every incentive to find a discrepancy.
That is a data problem, not a pricing problem. And the specific thing that makes it hard is unremarkable enough to be easy to underestimate: the same event will arrive twice. Networks retry. Functions get torn down mid-request and restarted. A queue redelivers because the acknowledgement was the message that got lost. Any of these turning into a second charge is a support ticket at best.
One event, one row, one charge
The deduplication key is a SHA-256 pure function of the event's own facts. Same facts, same key, every time, on any machine, with no coordination.
The important design choice is what happens to that key next. It becomes the primary key of the row in the sink, rather than a value stored in a separately indexed column and checked before writing. Read that difference twice, because it is the entire mechanism: the database itself refuses the duplicate as part of the insert. There is no window between checking and writing for a concurrent retry to slip through, and no dedup service to keep available.
The cost of that design is real and it is documented rather than buried. Two genuinely distinct events that are identical down to the millisecond collapse into one row. That is an undercount, chosen deliberately. When a billing system has to be wrong, it should be wrong in the direction that does not appear on a customer's card.
What happens between the event and the write
Recording an event validates it, derives the key, buffers it and returns. The caller is not waiting on a database write, which matters when the recording site is on a request path somebody is timing.
A batch leaves the buffer when it fills, 500 events by default, or when the oldest event in it reaches the flush interval, five seconds by default. Back pressure kicks in at ten thousand buffered events. A batch that fails to write goes back to the front of the queue rather than the end, so a transient failure does not quietly reorder your log. A scheduled job flushes every minute regardless, which catches the low-traffic case where a batch would otherwise sit half full.
Buffered events live in memory. A hard kill loses whatever has not been flushed, unless immediate flush mode is enabled, and that trade is yours to make per meter rather than ours to make for you. A high-volume meter usually wants the buffer. A meter counting something expensive and infrequent usually does not.
Periods, rating, and how mistakes get fixed
A billing period is a half-open window with a deterministic id, which means two systems asking about the same period are guaranteed to be talking about the same set of events rather than an overlapping approximation of it.
Rating runs against a rate card carrying a unit amount, a per-units divisor for the meters where the natural unit is a thousand of something, and an included-units allowance for the bundled quantity your plans come with. Closing a period freezes its result, and that freeze is what turns a running total into a number you can send to somebody.
Corrections are appended, never applied in place. A mistake is fixed by writing a reversal with a negative quantity, so the log records both what was originally counted and what was later decided about it. There is no update and no delete anywhere in this design, which is inconvenient exactly once, in the moment you would rather just fix the number, and valuable every time afterwards.
One boundary is worth stating plainly rather than leaving you to discover it. medusa-usage does not issue invoices, and it ships no scheduler for closing periods. It produces frozen, rated period results. Turning those into documents and payments is separate work, and we would rather scope it honestly than let the word billing do more implying than it should.
When the log outgrows the database
Most meters belong in PostgreSQL next to the rest of the store, where the primary key does the deduplication as a side effect of existing. Some do not. A meter counting in the billions will eventually crowd out the application database it shares.
For those, medusa-usage-tinybird sends the same events to Tinybird on ClickHouse. It ships the schema itself, one data source and three endpoints, so adopting it is not a data modelling project handed to you. Installing it is opt-in, and a deployment that never does is unaffected in every respect.
ClickHouse has no primary key constraint, so the deduplication has to move somewhere, and it moves to the read path. Every endpoint collapses duplicate keys before summing, keeping the earliest ingested copy of each. That is the same copy the PostgreSQL sink keeps, so the two sinks agree by construction rather than by luck. The declared token carries append on the data source and read on the endpoints, and nothing else.
How we wire the reporting side
There is a second half to any usage system, which is the application that has to report the usage in the first place, and it is where most of the lost events actually happen.
We build it with a durable outbox. Each batch is written to the outbox before the first send is attempted, which means a serverless function torn down mid-request costs a delayed delivery rather than a hole in the log. An hourly job drains whatever the inline attempt did not finish, so recovery is routine rather than a manual investigation.
The other rule is that Medusa is the single writer of the deduplication key. The reporting application never appends to the log directly, so the key is derived in exactly one place. Two derivations mean two chances to disagree, and a disagreement in a deduplication key is not a bug you find quickly.
This is not a diagram from a proposal. It is how our own platform's subscription and usage billing is put together, on a Medusa store we host ourselves.
What you get
An append-only usage event log for Medusa v2
Events are recorded and never edited. The log supports pluggable sinks, batched ingestion, deterministic deduplication, billing periods and rating, all inside the Medusa instance you already run.
Deduplication that a retry cannot defeat
The deduplication key is a SHA-256 pure function of the event's own facts, and that key is the row's primary key in the sink rather than a separately indexed column, so the insert itself is atomic and a redelivered event costs nothing.
Three supported ways to record an event
From the container, from a workflow, subscriber or route, or over an admin API endpoint that answers 202 and returns the derived keys. Authentication is admin only, and there is deliberately no unauthenticated ingestion route.
Billing periods and a rate card
Periods are half-open windows with deterministic ids. Rating uses a unit amount, a per-units divisor and an included-units allowance, and closing a period freezes its result so a later event cannot silently change an invoice you have already discussed.
Corrections as appended reversals
A mistake is fixed by appending a reversal with a negative quantity. There is no update and no delete, so the story of how a figure changed stays readable rather than being replaced by the figure somebody preferred.
A Tinybird sink for meters counting billions
When the log outgrows the application database, the same events land in Tinybird on ClickHouse instead. The plugin ships the schema itself, one data source and three endpoints, and installing it is entirely opt-in.
How we work
- 01
Decide what one event actually is
Before anything is built we settle what gets counted, what facts identify an occurrence, and therefore what two identical events mean. This is the decision the whole system rests on, and it is a commercial question rather than a technical one.
- 02
Choose where the log lives
Most meters belong in PostgreSQL alongside the rest of the store, where the primary key does the deduplication for you. Meters counting billions of events move to Tinybird. Both sinks are supported and both are configured to agree.
- 03
Wire the reporting side through an outbox
This is how we build it. The application writes each batch to a durable outbox before the first send attempt, so a serverless function torn down mid-request costs a delayed delivery rather than lost revenue, and an hourly job drains whatever the inline attempt did not finish.
- 04
Build the rate card and close a period
Unit amount, per-units divisor, included-units allowance, then a real close on real data. Closing freezes the result, which is what turns a running total into something you can put on an invoice.
- 05
Reconcile before anyone is charged
The meter runs alongside whatever you bill from today, and the two are compared before the new numbers matter. Reconciliation is easier here than in most systems because the log is append-only and every figure can be traced back to the events that produced it.
Tools and technology
Where a solid open-source tool exists, we choose it over a closed one. No lock-in to a single vendor, and costs you can actually predict.
- Medusa v2
- TypeScript
- Node.js
- PostgreSQL
- Redis
- Tinybird
- ClickHouse
- Next.js
- Vercel
- @zanreal/medusa-usage
- @zanreal/medusa-usage-tinybird
Frequently asked questions
More services in this category
Headless Commerce Builds on Medusa
An off-the-shelf commerce platform hands you its data model, its checkout rules and its release schedule. Medusa v2 is a framework you run yourself, so the parts of your business that do not fit a template get built rather than worked around.
Marketplace Integrations for Allegro
Selling on Allegro next to your own shop usually means one catalogue kept in two places and a spreadsheet holding them together. The offers, the stock and the orders can live in the same admin as everything else instead.
Pricing Automation Across Currencies
Foreign currency prices that recalculate themselves every day from the Narodowy Bank Polski rate, and a purchase cost sitting next to every product, so margin is a number in the admin rather than something rebuilt in a spreadsheet at month end.
Read testimonials from companies that trusted us
Delivered well ahead of the deadline.
ZanReal's individual approach is impressive.
Knowledge and business intuition make them a valuable partner.
Quick solutions that reduced costs by 99%.
Latest
Technical guides, security insights, and what we've learned building with AI.
Want to bill for usage without arguing about the numbers?
Message usTell us what you meter and how those events reach you today. We will map them onto a rate card and an event log that survives a retry, a redeploy and a customer reading it line by line.
Can't keep up with changes in AI world?
Let us do the heavy lifting. Every week we distill the most important AI developments into a focused 5-minute briefing - so you stay ahead without the noise.
Find out more
