Subscriptions vs usage-based billing: what to store from day one
Subscriptions vs usage-based billing explained from a data-modeling view: meters, limits, invoices, proration, and the records to store from day one.

Why pricing models need a data plan
Pricing isn't just a page on your website. It decides what you must record, how you report it, and whether you can explain a charge months later. When you choose subscriptions vs usage-based billing, you're also choosing the shape of your billing data.
A simple subscription can often be calculated from a few facts: plan, billing period, start date, and discounts. Usage pricing needs more: what was used, when it happened, which customer it belongs to, and how that usage turns into money. Without those records, you can still send invoices, but you can't defend them.
Adding usage later without planning usually breaks in three places:
- You lack trustworthy usage history, so customers dispute charges.
- Your analytics become guesswork because teams define "usage" differently.
- Finance can't audit invoices because raw inputs are missing or were overwritten.
The goal is boring but essential: compute the same invoice the same way every time. That means you can replay the calculation from stored facts (plan terms, meter rules, usage events, and the exact pricing version that applied).
A "modeling view" just means describing billing as building blocks that fit together, even if you're not an engineer. Imagine a team chat product:
- Subscription: $99 per workspace per month
- Usage: $0.01 per message after 50,000 messages
To support that later, your data has to answer: which workspace, which month, how many messages, what was included, and which price rules were active.
Subscriptions vs usage: how they differ in practice
Subscriptions charge for access. Usage-based billing charges for consumption. They behave very differently once you have upgrades, downgrades, proration, and real-world edge cases.
With a subscription, the key question is: "Was the customer entitled to use the product during this time?" You mostly track plan, seat count, billing period, and whether the invoice was paid. Usage still matters, but it often shows up as limits (soft or hard) instead of line items.
With usage-based billing, the key question becomes: "What happened, exactly, and when?" You need reliable metering, clear rules for when usage is counted, and a way to explain every charge later. Even if your UI shows one number (like "1,243 API calls"), behind it is a set of events that must be consistent and auditable.
Many B2B SaaS teams land on hybrid pricing: a base fee that covers a bundle, plus overages.
Common hybrid patterns
Most hybrid models fall into a few familiar shapes:
- Base platform fee plus per-seat fee
- Base fee plus included units (messages, tasks, API calls) plus an overage rate
- Tiered plan plus a usage add-on (charged only when enabled)
- Minimum commit with usage credit drawdown
Predictability helps when customers need budget approval and stable monthly spend. Pay-as-you-go works better when value scales with activity (for example, "per invoice processed"), or when customers are trying you out and want low risk.
Plan changes are guaranteed. Prices, bundles, and packaging will shift. Design billing so you can add a new meter, introduce a new tier, or change what "included" means without rewriting history. A practical rule: store the customer's plan and pricing terms as they were at the time of the charge, not only what they are today.
Define meters that you can measure reliably
A meter is the exact thing you charge for, written down so clearly that two people counting it will get the same number. It has three parts: the event (what happened), the unit (what you count), and the timing (when it is counted).
Most disputes start here. One side thinks they're paying for outcomes, the other side is charging for measurable activity.
Make the meter unambiguous
Pick meters that map to real product actions and can be recorded automatically. Common examples:
- Seats (active users who can log in)
- API calls (successful requests, or all requests)
- Storage (GB at a point in time, or an average over a period)
- Messages (sent, delivered, or opened)
- Compute minutes (time a job runs)
Then define what counts and what doesn't. If you bill per API call, decide whether retries count, whether 4xx and 5xx responses count, and whether internal calls made by your own integrations count.
Timing matters as much as the unit. Seats often work best as a point-in-time snapshot per billing period. API calls are usually summed within a window. Storage is tricky: customers expect to pay for "how much you stored," which usually means an average over time, not the peak.
Also decide scope: per account, or per workspace/project. A simple rule is: if teams can be billed separately, meters should be per workspace.
Limits, tiers, and entitlements
Entitlements are the rules for what a customer is allowed to do because of what they bought. They answer questions like: How many users can they add? Which features are enabled? What volume is allowed per month? Entitlements sit between access and billing: they shape what the product allows, while metering records what actually happened.
Keep entitlements separate from metering logic. Entitlements should be readable and stable (plan, add-ons, contract terms). Metering will evolve as the product evolves (new events, new meters), and you don't want every meter change to risk breaking access.
Hard limits, soft limits, and overage billing can look similar in UI, but they behave very differently:
- Hard limit blocks the action after the cap.
- Soft limit allows the action but warns and flags for follow-up.
- Overage billing allows the action and charges for extra usage.
- A grace period temporarily treats a hard limit like a soft limit.
- Trials and free tiers apply entitlements, but pricing is different (often zero) until a date or threshold.
Decide in advance what happens when a limit is exceeded. Example: a team on the "Starter" tier includes 5 seats and 10,000 API calls per month. If they invite a 6th user, do you block it, start charging per extra seat, or allow it for 7 days as a grace period? Each choice needs a rule you can show on an invoice and in support logs.
Store entitlements as time-bound records: customer, plan or add-on, start and end timestamps, limit values, and the enforcement mode (hard, soft, overage). This keeps access decisions and billing decisions consistent.
Invoices and billing periods you can audit
An invoice isn't just a PDF. It's an audit trail that answers: who was billed, for what, for which dates, under which rules. If you change pricing, you should still be able to rebuild old invoices exactly as they were.
Start with a few core records: Customer, Subscription (or Contract), Billing Period, and Invoice with Line Items. Each line item should point back to its source: a plan fee, a usage summary, or a one-time charge. That link is what makes billing explainable when someone disputes a charge.
Billing periods need an anchor and a timezone. "Monthly" isn't enough. Store the cycle anchor date (for example, the 15th at 00:00) and the timezone used to cut periods. Keep this consistent or you'll get off-by-one-day issues around daylight saving time.
Auditable invoices usually require:
- period_start and period_end on every invoice and line item
- The pricing version (plan/price IDs) used for that invoice
- Immutable totals: subtotal, tax, discounts, amount_due, currency
- The exact usage window for any usage-based line item
- External payment references (processor charge ID) when applicable
Revenue recognition is related but not the same as charging. A prepaid subscription fee is usually recognized over the service period, while usage is often recognized when delivered. Even if you keep this high level, store enough dates to support it later.
Treat credit notes, refunds, and adjustments as first-class records, never as edits to old invoices. If a customer upgrades mid-cycle, create an adjustment line or credit note that references the original invoice and states the proration rule used.
Idempotency keys matter for invoice generation and payment attempts. If a job runs twice, you want one invoice, one charge, and a clear log.
Proration and mid-cycle changes
Mid-cycle changes are where billing arguments start. If someone upgrades on the 20th, pauses for a week, then cancels, you need rules that turn those actions into numbers that make sense on an invoice.
Decide what changes you allow and when they take effect. Many teams apply upgrades immediately so customers get value right away, but apply downgrades at renewal to avoid messy refunds.
Pick a proration policy you can explain
Proration can be daily, hourly, or not at all. The more precise you get, the more timestamps, rounding rules, and edge cases you must store and test.
Lock down policy choices early:
- Prorate upgrades immediately, defer downgrades to renewal
- Use daily proration (simpler than hourly, usually fair enough)
- Define rounding rules (for example, round up to the nearest cent)
- Decide how pauses work (credit back time, or extend the period)
- Set a clear refund policy for cancellations (full, partial, or none)
Model proration as invoice line items
Avoid hidden math. Represent proration as explicit adjustments on the invoice, like a debit for remaining time on the new plan and a credit for unused time on the old plan. Each line item should point to the exact change event that caused it (change_id) and include the proration window (start_at, end_at), quantity/time basis, and tax category.
Usage meters add one more decision: when a plan changes, do meters reset, keep accumulating, or split into segments? A simple, auditable approach is to segment usage by plan version. Example: a customer upgrades from 10 seats to 25 seats mid-month. You keep usage events as-is, but your rating groups them by the entitlement period active at the time.
Decide what is reversible. It helps to treat usage events as final once accepted, while subscription changes may be reversible only until the invoice is finalized. Store change events and invoice adjustments so you can regenerate invoices cleanly when rules evolve.
The data you must store from day one
If you wait to design billing data until after customers complain, you'll end up guessing. Whether you choose a subscription, usage, or a hybrid B2B SaaS pricing model, the safest starting point is a small set of records you can always audit later.
Start with a clear customer hierarchy. In B2B SaaS, the payer is usually a company account, but usage often happens inside workspaces or projects, and actions come from individual users. Store all three levels (account, workspace, user) and record who did what. Billing disputes often turn into "which team did this?"
A minimum billing database design that supports real invoices and clean investigations:
- Accounts and org structure: account, workspace (or project), users, roles, billing contact, and tax fields if needed
- Subscriptions: plan, status, start/end dates, renewal settings, cancellation reason, and the pricing version applied at start
- Price catalog: products, plan components (base fee, seats, meters), tiers, currency, and effective dates
- Usage metering data: an immutable append-only event log with timestamp, workspace, user (if available), meter name, quantity, and a unique idempotency key
- Invoice artifacts: billing period boundaries, line items, totals, tax/discount adjustments, and a snapshot of the inputs used
Don't rely only on aggregated counters. Keep counters for speed, but treat the event log as the source of truth. A simple rule helps: events are immutable, corrections are new events (for example, a negative quantity), and every event ties to a specific meter definition.
Example: a customer says their "API calls" doubled last month. If you can pull raw events by workspace and day, you can show where the spike came from or spot an integration loop.
Step-by-step: from usage events to an invoice
The hard part isn't the math. It's making the result explainable months later, even after plans, prices, and customers change.
1) Start with a price catalog that can time travel
Set up products, plans, meters, and prices with effective dates. Never overwrite an old price. If a customer is billed in March, you must be able to re-run March using the March catalog.
Example: "API Calls" costs $0.002 each starting April 1. March invoices must still use the older rate.
2) Snapshot what the customer was entitled to during the period
At the start of each billing period (or when a change happens), store an entitlement snapshot: plan, included units, limits, tier rules, discounts, and tax settings. Think of it as "what we promised" for that period.
3) Ingest usage events and validate them early
Usage should arrive as immutable events: timestamp, customer, meter, quantity, and a unique id for deduplication. Validate basics at the door (missing meter, negative quantity, impossible timestamps) and record the raw event even if you also store a cleaned version.
Then do the calculation in two passes:
- Aggregate events into totals per customer, meter, and billing period (and keep the aggregation version)
- Rate the totals into charges using the catalog plus the entitlement snapshot (included units, overage price, tiers)
Generate invoice line items that reference the exact inputs used (catalog version, snapshot id, aggregation id).
Finally, lock the invoice. Store the calculation inputs and the output, mark it final, and stop it from changing when late events arrive. Late events should go to the next invoice (or a separate adjustment) with a clear audit note.
Customer and internal reporting needs
Customers don't care whether you chose subscriptions vs usage-based billing. They care that your numbers match their own, and that they can predict the next charge before it happens.
Customer-facing reporting works best as a simple billing home that answers a few questions without support tickets:
- What plan am I on, and what does it include?
- How much have I used this period, and how is usage counted?
- What are my limits, and what happens if I exceed them?
- When does the billing period end, and what's my estimated next bill?
- Where can I see past invoices and payments?
Internally, support and finance need to explain a number, not just display it. That means spotting spikes, tracing changes, and separating preview math from finalized billing.
Keep bill previews separate from invoices. Previews can be recalculated when late usage arrives or when you refine a meter definition. Invoices must not.
Your internal reporting should make it easy to flag anomalies (sudden usage jumps, negative usage, duplicate events), reproduce an invoice line item from raw usage and rules, and see plan changes and invoice proration rules for the period.
Audit trails matter more than most teams expect. If a customer says, "We upgraded on the 12th," you need timestamps, actor (user, admin, automation), and the exact before/after values for plan, seats, limits, and meter rates.
For retention, keep raw usage events and finalized invoice artifacts long term. A practical rule: if it can change the amount charged or help defend it in a dispute, store it immutably.
Common traps that cause billing disputes
Most billing disputes aren't about price. They happen when you can't explain a number on an invoice, or when the same inputs produce a different total later.
A common mistake is keeping only monthly totals. Without raw events, you can't answer simple questions like "Which day pushed us over the limit?" Keep the event, then aggregate it.
Another frequent issue is changing what a meter means without tracking it. "Active user" might start as "logged in at least once" and later become "created a record." If you don't version meter definitions, customers will compare old invoices to new ones and you won't be able to prove which rule applied.
Disputes often come from the same few patterns:
- Entitlements enforced only in the UI (backend still allows extra usage or blocks valid usage)
- One timestamp field used for everything (event time, ingestion time, billing period time)
- Time zones ignored (usage at 00:30 local time lands in the wrong day or month)
- Billing anchors forgotten (some customers bill on the 1st, others on signup day)
- No audit trail of plan, price, and limit changes
Example: a customer in Tokyo upgrades mid-cycle on the 31st local time. If you store only a UTC timestamp and no billing anchor, the upgrade can appear to happen on the 30th in your system, shifting proration and usage into the wrong period.
The fastest way to lose trust is having no way to reproduce an old invoice calculation. Store the inputs (events, versions, anchors, and applied prices) so you can rerun the same logic later and get the same result, even after your product changes.
Quick checks before you ship billing
Before you launch, do one drill: pick a random customer and recreate their last invoice using only stored data. If you need "whatever the code did at the time," you don't have an audit trail.
Make sure every meter is unambiguous. A meter needs a clear unit (requests, seats, GB, minutes), a trusted source (which service emits it), and a time window (per day, per billing period, per calendar month). If you can't explain the meter in one sentence, customers won't trust it.
Quick checks that catch most billing problems:
- You can replay an invoice from inputs: plan version, prices, usage totals, taxes, discounts, and proration parameters
- Usage events are append-only, immutable, and deduplicated (idempotency key or event id)
- Plan and price changes are versioned with effective dates (don't overwrite old prices)
- Proration rules are written down in plain language and covered by tests
- Support can answer "why was I charged" quickly using the same saved facts
Example: a customer upgrades from 10 to 25 seats on the 18th and crosses a usage threshold on the 23rd. Your system should show (1) which plan version was active on each date, (2) the seat-change event with timestamp, (3) the proration formula used, and (4) the usage events that rolled up into the final total.
Next steps: implement without painting yourself into a corner
Start small, but don't start vague. The safest path is the smallest data model that still lets you answer one question months later: "Why did we charge this amount?"
Prototype your billing schema and admin screens early, before your first pricing change. If you wait until sales asks for a new tier or a mid-cycle upgrade, you'll end up patching logic in too many places.
A practical split is: let Stripe handle charging, receipts, and payment retries, but keep rating (how usage becomes line items) in your own system. That means you store raw usage events, pricing versions, and the calculation results you used to generate an invoice preview.
A simple launch plan that stays flexible:
- Pick 1-2 meters you can measure reliably (for example, active seats per day and API calls)
- Version pricing rules from day one so old invoices still match old rules
- Build an internal billing admin panel to view usage, overrides, credits, and disputes
- Add a basic customer portal view: current plan, current period usage, expected bill
- Treat every invoice as an auditable snapshot, not a recalculated guess
If you want to move fast without writing a lot of custom back office code, you can model these entities in AppMaster and build the admin and portal screens with its visual tools, while keeping PostgreSQL as the system of record for events and invoices.
Concrete example: you launch with one seats meter. Three months later you add storage GB. If meters, entitlements, and pricing rules are versioned, you can introduce the new meter without breaking older invoices, and support can explain any line item in minutes.
FAQ
Start by deciding what you need to prove later: why a customer was charged that amount. Subscriptions need plan, period, and entitlement history; usage needs an immutable record of what happened, when, and under which pricing rules.
If you can’t replay the invoice from stored inputs, you can’t reliably answer disputes or audits. The safest setup is to store time-bounded plan terms, versioned prices, raw usage events, and the exact calculation outputs used when the invoice was finalized.
A good meter is something two people can count the same way. Define the event, the unit, and the timing window, and write down what counts and what doesn’t (like retries, failed requests, or internal calls) so you don’t change the meaning midstream.
Hard limits block actions, soft limits warn but allow, and overage billing allows and charges. Pick one behavior per entitlement and store it as a rule with start and end timestamps so support, product, and billing all make the same decision.
They solve different problems: entitlements control what the customer is allowed to do, while metering records what actually happened. Keeping them separate prevents a meter change from accidentally breaking access, and it makes invoices easier to explain.
Store an append-only usage event log as the source of truth, and optionally keep aggregates for speed. Events should include timestamp, customer scope (like workspace), meter name, quantity, and a unique idempotency key so duplicates don’t double-charge.
Never overwrite old prices or plan rules; add new versions with effective dates. Then store which pricing version applied to each invoice (and often each entitlement period), so old invoices can be rebuilt exactly even after packaging changes.
Monthly billing needs a cycle anchor and a timezone, not just “monthly.” Store period_start and period_end consistently and be explicit about which timestamps you use for event time versus ingestion time to avoid off-by-one errors around time zones and DST.
Use a policy you can explain in one sentence, and model the math as explicit invoice line items (credits and debits) tied to the change event and proration window. Daily proration is a common default because it’s simpler to test and defend than hourly.
Finalize invoices as immutable snapshots and treat late usage as a new adjustment or as part of the next period, with a clear note. Previews can be recalculated freely, but finalized invoices should not change when new events arrive.


