Usage-Based Billing
Usage-based (a.k.a. metered) billing charges merchants per real-world event — per SMS sent, per AI generation, per shipping label printed — on top of an optional flat monthly subscription. Apps report meter ticks to LaunchMyStore viaPOST /api/v1/billing/usage and the platform
forwards them to Stripe as usage records under the merchant’s
subscription.
LaunchMyStore implements metered billing as a second Stripe
subscription item on the merchant’s existing app subscription. Your
app declares one metered unit per pricing plan; the platform handles
Stripe wiring, cap enforcement, and per-event accounting.
How it works
Two key invariants:- The cap is checked before the Stripe call. If the projected
accrual exceeds the cap, the platform returns
402withcode: USAGE_CAP_EXCEEDEDand never calls Stripe. Your app sees no side effect and gets the remaining headroom in cents. stripeUsageRecordIdis unique. EveryUsageRecordfrom Stripe produces exactly one billing transaction. Stripe idempotency keys + a database unique index mean that even network retries can’t double-bill.
Declaring metered pricing
Add ausage block to your app’s pricing in app.json:
| Field | Type | Required | Meaning |
|---|---|---|---|
unitName | string | yes | Human label shown to merchants (e.g. SMS, AI generation). |
unitAmount | number | yes | Per-unit price in dollars (0.05 = 5¢ per unit). |
cappedAmount | integer | yes | Default monthly cap in dollars. Merchant can raise or lower in the billing portal. |
terms | string | no | Free-form text shown at subscribe time. |
- Creates (or reuses) a metered Stripe
Pricewithrecurring.usage_type='metered',aggregate_usage='sum'. - Adds it as a second
line_itemsentry on the Checkout session — the merchant sees the flat fee + the per-unit price in one approval. - After Checkout completes, the platform walks the subscription items, locates the metered one, and stores its id on the installation so usage records can attach to it.
pricing.model may be recurring, one_time, or free — only
recurring plans can carry a usage block.
Reporting usage
Your app POSTs one event per real-world action. The token’s installation is the one billed;quantity is the count of units
consumed.
Idempotency
Pass anidempotencyKey per event. The platform namespaces it as
usage:{installationId}:{key} and forwards it as Stripe’s idempotency
key — retries within 24 hours are guaranteed safe. Stable choices:
- A message-id, generation-id, or shipping-label-id you already mint.
- A UUIDv4 you record alongside the event in your own DB.
Batching
The platform doesn’t aggregate usage — eachPOST /usage is one Stripe
API call and one UsageRecord. For high-frequency events (an analytics
app recording one row per page view, say) buffer client-side and POST
periodic batches with quantity: <sum>. Stripe sums all
UsageRecord.quantity for a subscription_item over the billing
period; one batched call costs the same as N individual calls.
Reading current state
GET /api/v1/billing/usage returns the current cap, accrued spend,
remaining headroom, and the period-end timestamp. Apps render this on
their admin home iframe to show “used 50 this month”.
Cap management
Caps protect merchants from bill shock — once monthly usage cost exceeds the cap, further events 402 until the next period or until the merchant raises the cap.Lowering the cap
Lowering is immediate. The new value cannot be below what the merchant has already accrued this period (would create an instant cap loop).Raising the cap
Raising requires merchant approval. The endpoint returns a Stripe Billing PortalconfirmationUrl; the merchant opens it in a top-level
tab, confirms the new cap, and returns to your returnUrl. The cap row
is updated only after they confirm.
confirmationUrl at the top level — Stripe blocks framing the
billing portal. After return, call GET /usage to confirm the cap took
effect.
Default cap
If you don’t setpricing.usage.cappedAmount, the platform treats the
installation as uncapped. GET /usage returns capAmountCents: null
and remainingCents: null; cap-exceeded 402s never fire. Recommended
only for apps where the per-unit cost is tiny and bill shock is
genuinely impossible.
Billing period end
When the Stripe billing period rolls over:- Stripe sums all
UsageRecord.quantityfor the metered subscription item, multiplies byunit_amount, and adds the result as a line on the merchant’s invoice. - The merchant pays the invoice (auto-charged via saved payment method).
- Stripe fires the
invoice.paidwebhook. - The platform records a
metered_invoicebilling transaction reflecting the period’s metered total, then resets the installation’s accrued-usage counter to0.
Pricing strategy
| Strategy | When to use |
|---|---|
Flat only (no usage) | Predictable workloads — page builders, theme apps. |
| Metered only | Pure pay-as-you-go — analytics, AI inference, transactional SMS. |
| Flat + metered | Most apps. Flat covers fixed costs; metered handles variable cost. |
High unitAmount, low cappedAmount | Premium per-event apps (e.g. one 50/mo). |
Low unitAmount, high cappedAmount | High-volume apps (e.g. 1000/mo). |
unitAmount to the average effective
price and rely on your pricing.terms field for human-readable detail.
Native graduated billing is on the roadmap.
Best practices
Always pass an idempotencyKey
Always pass an idempotencyKey
Without one, a single retry double-bills the merchant. The key
should map to a real-world event (
sms-{msg_id},
generation-{uuid}), not a wall-clock timestamp.Surface usage in your admin UI
Surface usage in your admin UI
Call
GET /usage from your admin home iframe and render a progress
bar (accruedAmountCents / capAmountCents). Merchants want to
see what they’re spending before the invoice arrives.Handle 402 gracefully
Handle 402 gracefully
A
USAGE_CAP_EXCEEDED response means the merchant has spent their
monthly cap. Refuse the underlying action and surface a clear “Cap
reached — raise to keep sending” CTA that calls
POST /usage/cap. Don’t silently retry.Pick a meaningful unit
Pick a meaningful unit
SMS sent is clearer than credits. Merchants understand a unit
they recognise from your product — copy the language your existing
pricing page uses.Default to a sensible cap
Default to a sensible cap
Set
cappedAmount to an amount your typical merchant won’t hit but
your runaway scripts will. A SMS app might cap at $50 — enough for
a small store, low enough that an infinite loop doesn’t bankrupt
the merchant before someone notices.Batch high-frequency events
Batch high-frequency events
The platform doesn’t aggregate — one POST per event is one Stripe
call. For per-impression analytics or per-byte storage, sum
client-side and POST every minute.
Common metered units
| Unit | Apps that use it |
|---|---|
SMS sent | SMS marketing, OTP, transactional SMS |
email sent | Email marketing apps on top of flat sender quota |
AI generation | Product description, image, blog-post generators |
shipping label | Shipping apps charging per label printed |
webhook delivered | Event-stream / pipeline apps |
MB synced | Inventory / catalog sync apps |
API call | Headless storefront / GraphQL bridge apps |
See also
- Pricing Models — overview of
free,one_time, andrecurring(flat). - Stripe Integration — full Stripe Connect / Checkout setup behind the scenes.
- Usage Records API — full endpoint reference with request/response shapes and error codes.
- App Manifest — every
pricingfield in context.