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

# Usage billing API

> Set seats and meters on a subscription offer, report usage for each subscriber, change seats and read usage — with curl and SDK examples.

The usage billing endpoints let your software charge subscribers **per seat**
and **per unit of usage**. How it works for you and your buyers is in
[Usage-based and per-seat pricing](/selling/usage-based-pricing).

* Base URL `https://api.pocketsflow.com`, `Authorization: Bearer pk_live_…`
  (or `pk_test_…` for test mode).
* Requires the **Pro** plan: below it, writes answer `403 FEATURE_LOCKED`
  (`details.requiredPlan: "tier3"`). Reading usage stays available after a
  downgrade.
* Money is **integer cents** in your account currency.
* `{id}` on the subscriber endpoints is the subscriber `_id` from
  `GET /subscriptions/subscribers` or its membership id (`mem_…`). On
  `/usage-pricing` it is the subscription **offer** id.

<Note>
  The SDK methods below ship in the next `pocketsflow` release. Until then,
  call the endpoints directly (curl examples) or use the MCP tools.
</Note>

## Set seats and meters on an offer

`PUT /subscriptions/{id}/usage-pricing` replaces the offer's usage pricing.
Sending `{ "seats": { "enabled": false }, "meters": [] }` switches back to flat
pricing (always allowed). The same `usagePricing` object is also accepted by
`POST /subscriptions` when you create an offer.

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X PUT https://api.pocketsflow.com/subscriptions/665f1c2a9b3e4a0012ab34cd/usage-pricing \
    -H "Authorization: Bearer $POCKETSFLOW_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "seats": { "enabled": true, "minSeats": 1, "maxSeats": 50, "licenseKeys": true },
      "meters": [
        {
          "key": "api_calls",
          "name": "API calls",
          "unit": "call",
          "pricing": "graduated",
          "packageSize": 1000,
          "includedUnits": 10000,
          "tiers": [
            { "upTo": 100000, "unitAmountCents": 100 },
            { "upTo": null, "unitAmountCents": 50 }
          ]
        }
      ]
    }'
  ```

  ```ts SDK theme={"system"}
  await pocketsflow.subscriptions.updateUsagePricing("665f1c2a9b3e4a0012ab34cd", {
    seats: { enabled: true, minSeats: 1, maxSeats: 50, licenseKeys: true },
    meters: [
      {
        key: "api_calls",
        name: "API calls",
        unit: "call",
        pricing: "graduated",
        packageSize: 1000,
        includedUnits: 10000,
        tiers: [
          { upTo: 100000, unitAmountCents: 100 },
          { upTo: null, unitAmountCents: 50 },
        ],
      },
    ],
  });
  ```
</CodeGroup>

| Field | Notes |
| - | - |
| `seats.enabled` | Price per seat: buyers pick seats at checkout. |
| `seats.minSeats` / `seats.maxSeats` | 1–10,000; `maxSeats: null` = no maximum. |
| `seats.licenseKeys` | Issue each subscriber a license key whose activation limit follows their seats. |
| `meters[].key` | `^[a-z0-9][a-z0-9_.-]{0,39}$`, unique per offer. What you send as `meter`. |
| `meters[].pricing` | `per_unit`, `graduated` or `volume`. |
| `meters[].unitAmountCents` | `per_unit`: cents per `packageSize` units. |
| `meters[].packageSize` | Units one price applies to (default 1). |
| `meters[].includedUnits` | Free units per period (default 0). |
| `meters[].tiers` | `graduated` / `volume`: up to 10, ascending `upTo` (billable units), last `upTo: null`. Optional `flatAmountCents`. |

At most 5 meters per offer.

## Report usage

`POST /subscriptions/{id}/usage`

| Field | Required | Notes |
| - | - | - |
| `meter` | yes | A meter `key` of the subscriber's offer (else `422 UNKNOWN_METER`). |
| `quantity` | yes | Whole number, 0–1,000,000,000. |
| `timestamp` | no | ISO 8601 or Unix seconds. Defaults to now. Not in the future, not before the subscription started, and in a period that is still open (else `409 PERIOD_CLOSED`). |
| `idempotencyKey` | strongly recommended | Up to 255 characters, or send an `Idempotency-Key` header. |

**Idempotency.** The first report with a key answers `201` with
`replayed: false`. Retrying the same key with the same body answers `200` with
the original record and `replayed: true` — it is counted once, even when
retries race. The same key with a different body is refused with
`409 IDEMPOTENCY_KEY_REUSED`. Without a key every request counts.

**Rate limit.** 300 reports per minute per subscriber (inside the account's
600 requests per minute). Aggregate before reporting — one report with
`quantity: 500` costs the same as 500 reports with `quantity: 1`.

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X POST https://api.pocketsflow.com/subscriptions/665f1c2a9b3e4a0012ab9911/usage \
    -H "Authorization: Bearer $POCKETSFLOW_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: req_8f2a91" \
    -d '{ "meter": "api_calls", "quantity": 250 }'
  ```

  ```ts SDK theme={"system"}
  const { usageRecord, replayed } = await pocketsflow.usage.report(
    "665f1c2a9b3e4a0012ab9911",
    { meter: "api_calls", quantity: 250, idempotencyKey: "req_8f2a91" },
  );
  ```
</CodeGroup>

```json Response 201 theme={"system"}
{
  "usageRecord": {
    "id": "6710c5e09b3e4a0012ab77aa",
    "meter": "api_calls",
    "quantity": 250,
    "timestamp": "2026-10-16T09:12:44.000Z",
    "idempotencyKey": "req_8f2a91",
    "periodId": "6710b2c49e3f1a0012cd56ef",
    "source": "api",
    "createdAt": "2026-10-16T09:12:44.120Z"
  },
  "period": {
    "id": "6710b2c49e3f1a0012cd56ef",
    "start": "2026-09-28T09:00:00.000Z",
    "end": "2026-10-28T09:00:00.000Z"
  },
  "replayed": false
}
```

## Read usage

`GET /subscriptions/{id}/usage?limit=20` returns the current period's running
totals and upcoming overage, a per-day series, the latest reports (`limit`,
max 100) and the last 12 closed periods with their charge status.

<CodeGroup>
  ```bash curl theme={"system"}
  curl https://api.pocketsflow.com/subscriptions/665f1c2a9b3e4a0012ab9911/usage \
    -H "Authorization: Bearer $POCKETSFLOW_API_KEY"
  ```

  ```ts SDK theme={"system"}
  const usage = await pocketsflow.usage.get("665f1c2a9b3e4a0012ab9911", { limit: 20 });
  console.log(usage.currentPeriod.upcomingOverageCents);
  ```
</CodeGroup>

```json Response (trimmed) theme={"system"}
{
  "subscriber": { "id": "665f1c2a9b3e4a0012ab9911", "status": "active", "quantity": 5, "purchasedQuantity": 3 },
  "currency": "usd",
  "currentPeriod": {
    "id": "6710b2c49e3f1a0012cd56ef",
    "start": "2026-09-28T09:00:00.000Z",
    "end": "2026-10-28T09:00:00.000Z",
    "status": "open",
    "lines": [
      { "kind": "meter", "meter": "api_calls", "name": "API calls", "totalUnits": 81100, "includedUnits": 10000, "billableUnits": 71100, "amountCents": 7110 },
      { "kind": "seats", "name": "Extra seats", "totalUnits": 1.6, "includedUnits": 3, "billableUnits": 1.6, "amountCents": 1920 }
    ],
    "upcomingOverageCents": 9030
  },
  "daily": [{ "date": "2026-10-16", "meter": "api_calls", "units": 4200 }],
  "periods": [{ "id": "…", "overageCents": 6420, "charge": { "status": "paid" } }],
  "outstandingCents": 0,
  "entitled": true
}
```

`periods[].charge.status` is `not_due`, `processing`, `paid`, `failed`, `due`
or `uncollectible`. Failed and due charges are retried 1, 3, 5 and 7 days
after the first failure (`retries`, `nextRetryAt`); a paid overage is an order
(`saleId`, `billingReason: "usage"` on `GET /orders` and the subscriber's
payments).

The seats line counts **seat-periods** above the seats bought at checkout:
two extra seats held for 80% of the period are `1.6`.

## Change seats

`POST /subscriptions/{id}/seats` with `{ "quantity": 5 }`. Takes effect at
once; seats above the quantity bought at checkout are billed at the end of the
period, prorated. Below the bought quantity answers
`409 BELOW_PURCHASED_SEATS`; an inactive subscription `409 SUBSCRIPTION_INACTIVE`;
outside the offer's min / max `400`.

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X POST https://api.pocketsflow.com/subscriptions/665f1c2a9b3e4a0012ab9911/seats \
    -H "Authorization: Bearer $POCKETSFLOW_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "quantity": 5 }'
  ```

  ```ts SDK theme={"system"}
  await pocketsflow.subscriptions.updateSeats("665f1c2a9b3e4a0012ab9911", { quantity: 5 });
  ```
</CodeGroup>

## Errors

| Status | `code` | When |
| - | - | - |
| 400 | `INVALID_METER`, `INVALID_QUANTITY`, `INVALID_TIMESTAMP`, `INVALID_IDEMPOTENCY_KEY`, `INVALID_USAGE_PRICING`, `SEATS_NOT_ENABLED` | The body is invalid; `message` says which field. |
| 403 | `FEATURE_LOCKED` | Your plan does not include usage-based pricing. |
| 404 | — | No such subscriber / offer in this key's mode. |
| 409 | `IDEMPOTENCY_KEY_REUSED`, `PERIOD_CLOSED`, `BELOW_PURCHASED_SEATS`, `SUBSCRIPTION_INACTIVE` | See above. |
| 422 | `UNKNOWN_METER` | The offer has no meter with that key. |
| 429 | `RATE_LIMITED` | More than 300 reports a minute for one subscriber. |

## MCP tools

The [MCP server](/api-reference/mcp-server) exposes the same operations:
`update_usage_pricing`, `report_usage`, `get_subscription_usage` and
`update_subscription_seats`.

## Webhooks

`usage.recorded` (each new report) and `usage.period_closed` (totals and the
overage charge) — payloads in [Webhook events](/api-webhooks/events#usage-billing-events).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.