> ## Documentation Index
> Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Usage-Based Billing

> Declare a metered pricing component in your app and report usage events per installation.

# 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 via `POST /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

```mermaid theme={null}
sequenceDiagram
    participant App as Your app
    participant LMS as LaunchMyStore
    participant Stripe
    participant Merchant

    Note over App,Merchant: Once per install
    Merchant->>LMS: Install app (one-click)
    LMS->>Stripe: Subscription with 2 items:<br/>flat $X/mo + metered $Y/unit
    Stripe-->>LMS: subscription_item.id (si_…)
    LMS->>LMS: Persist installation.stripeMeteredItemId

    Note over App,Merchant: Per event
    App->>LMS: POST /api/v1/billing/usage<br/>{ quantity, idempotencyKey }
    LMS->>LMS: Check cap (reject 402 if over)
    LMS->>Stripe: createUsageRecord(si_…, quantity)
    Stripe-->>LMS: mbur_…
    LMS->>LMS: Add cost to accrued usage<br/>record USAGE billing transaction
    LMS-->>App: { recordedAt, accruedAmountCents, remainingCents }

    Note over App,Merchant: End of period
    Stripe->>Merchant: Invoice (flat fee + summed metered usage)
    Stripe->>LMS: webhook invoice.paid
    LMS->>LMS: Reset accrued usage to 0<br/>record metered_invoice transaction
```

Two key invariants:

1. **The cap is checked before the Stripe call.** If the projected
   accrual exceeds the cap, the platform returns `402` with
   `code: USAGE_CAP_EXCEEDED` and never calls Stripe. Your app sees no
   side effect and gets the remaining headroom in cents.
2. **`stripeUsageRecordId` is unique.** Every `UsageRecord` from 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 a `usage` block to your app's `pricing` in `app.json`:

```json theme={null}
{
  "handle": "smart-sms",
  "name": "Smart SMS",
  "pricing": {
    "model": "recurring",
    "monthlyPrice": 10,
    "yearlyPrice": 100,
    "currency": "usd",
    "trialDays": 14,
    "usage": {
      "unitName": "SMS",
      "unitAmount": 0.05,
      "cappedAmount": 50,
      "terms": "$0.05 per SMS sent, billed monthly"
    }
  }
}
```

| 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.                                            |

When the merchant subscribes, the backend:

1. Creates (or reuses) a metered Stripe `Price` with `recurring.usage_type='metered'`, `aggregate_usage='sum'`.
2. Adds it as a second `line_items` entry on the Checkout session — the merchant sees the flat fee + the per-unit price in one approval.
3. 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.

A `usage` block is billed whenever the merchant's app checkout runs in
Stripe **subscription mode** — i.e. any recurring pricing model
(`recurring`, `subscription`, `paid`, `monthly`, or `yearly`). The metered
line item is **not** attached for `one_time` or `free` models (there's no
subscription to hang it on), so a `usage` block on those is inert.

## 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.

```javascript theme={null}
// Inside your app — server-side or App Bridge–authenticated request
const r = await fetch('https://api.launchmystore.io/api/v1/billing/usage', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${appAccessToken}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    quantity: 1,
    idempotencyKey: `sms-${messageId}`,
  }),
});

if (r.status === 402) {
  const body = await r.json();
  const meta = JSON.parse(body.message); // { code, capCents, accruedCents, remainingCents }
  if (meta.code === 'USAGE_CAP_EXCEEDED') {
    // Refuse the underlying action; prompt merchant to raise cap.
    return showCapExceededModal(meta);
  }
}
```

Full request / response shapes: [Usage Records API](/api-reference/app-billing/usage-records).

### Idempotency

Pass an `idempotencyKey` 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.

Without a key, a single network retry can double-bill.

### Batching

The platform doesn't aggregate usage — each `POST /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 $6 of $50 this month".

```javascript theme={null}
const r = await fetch('https://api.launchmystore.io/api/v1/billing/usage', {
  headers: { Authorization: `Bearer ${appAccessToken}` },
});
const { data } = await r.json();
// {
//   unitName: 'SMS',
//   unitAmount: 0.05,
//   capAmountCents: 5000,
//   accruedAmountCents: 605,
//   remainingCents: 4395,
//   currentPeriodEnd: '2026-06-01T00:00:00.000Z',
//   meteredItemId: 'si_OqW2NABCxyz'
// }
```

## 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).

```bash theme={null}
curl -X POST .../api/v1/billing/usage/cap \
  -H "Authorization: Bearer …" \
  -d '{"cappedAmount": 10}'
# 200 { requiresApproval: false, newCap: 10 }
```

### Raising the cap

Raising requires merchant approval. The endpoint returns a Stripe
Billing Portal `confirmationUrl`; 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.

```bash theme={null}
curl -X POST .../api/v1/billing/usage/cap \
  -H "Authorization: Bearer …" \
  -d '{"cappedAmount": 100, "returnUrl": "https://my-app.example.com/billing/return"}'
# 200 { requiresApproval: true, confirmationUrl: 'https://billing.stripe.com/p/session/...', currentCap: 50, requestedCap: 100 }
```

Open `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 set `pricing.usage.cappedAmount`, the platform treats the
installation as **uncapped**. `GET /usage` returns `capAmountCents: null`
and `remainingCents: null`; cap-exceeded `402`s 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:

1. Stripe sums all `UsageRecord.quantity` for the metered subscription
   item, multiplies by `unit_amount`, and adds the result as a line on
   the merchant's invoice.
2. The merchant pays the invoice (auto-charged via saved payment
   method).
3. Stripe fires the `invoice.paid` webhook.
4. The platform records a `metered_invoice` billing transaction
   reflecting the period's metered total, then resets the
   installation's accrued-usage counter to `0`.

Your app does nothing — accrued counters reset automatically and the
next event starts a fresh period. Visible to merchants in the billing
detail page as a single "Usage — May 2026" line item.

## 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 $5 AI image generation, cap $50/mo). |
| Low `unitAmount`, high `cappedAmount` | High-volume apps (e.g. $0.001 per event, cap $1000/mo).               |

Tiered (graduated) pricing — where the first 100 units are free and
later units get cheaper — is not yet implemented as declarative
metadata. To approximate it, set `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

<AccordionGroup>
  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

## 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](/billing/pricing-models) — overview of `free`,
  `one_time`, and `recurring` (flat).
* [Stripe Integration](/billing/stripe-integration) — full Stripe
  Connect / Checkout setup behind the scenes.
* [Usage Records API](/api-reference/app-billing/usage-records) — full
  endpoint reference with request/response shapes and error codes.
* [App Manifest](/getting-started/app-manifest) — every `pricing` field
  in context.
