> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runledger.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runledger.co/_mcp/server.

# Usage windows

A plan's credit balance says how much a customer has. A usage window adds time: how much they get each week, how much they may use in a five-hour session, or how fast a pool can be drained. Windows are `allowances` on the plan, and every one of them is a refill or a cap.

## Before you start

* A prepaid plan. [Prepaid and postpaid billing](/concepts/billing#plans) covers plans.
* [Pools](/guides/pools), if you want a refill to land somewhere other than the default pool.

| `kind`   | What it does                                                                                                                                                                                                                                     |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `refill` | Grants `amount` credits into a [pool](/guides/pools) at the start of each window. The credits are an ordinary [credit grant](/concepts/billing#credits) with `source: allowance` that expires when the window ends, unless `rollover` keeps some |
| `cap`    | Grants nothing. It limits how many credits can be consumed in the window, whatever the balance is                                                                                                                                                |

Refill and cap are separate, so a plan can give 5,000 credits a month and also stop a customer using more than 1,500 of them a week. A plan holds up to ten allowances, and work must have room in every window that applies.

## Windows

Each allowance has a `window` with a `type`, a `duration` (ISO 8601, from `PT1H` to `P1Y`) and, for fixed windows, an `anchor`.

| `type`    | Behavior                                                               | Use it for                             |
| --------- | ---------------------------------------------------------------------- | -------------------------------------- |
| `fixed`   | Resets on a boundary: every `duration` from the `anchor`               | Monthly refills, weekly caps           |
| `session` | Opens at the first use after the last one closed, and lasts `duration` | A five-hour session                    |
| `sliding` | A cap only. Counts what was used over the trailing `duration`          | "No more than 200 credits in any hour" |

A `fixed` window's `anchor` is `billing_period` (the default, the subscription's period start), `subscription_start`, `calendar` (UTC midnight, Monday for a week, the first for a month) or `customer`. `customer` resets at the moment the subscription was created, which today is the same as `subscription_start`, so customers who subscribed at different times reset at different times.

All times are UTC. A `refill` on a `session` window grants its credits when the session opens.

## Five hours, a week and a month

This plan refills 5,000 credits each billing period, caps use at 1,500 in any five-hour session, and caps use at 3,500 a week:

**`Config document: plans`**

```json title="Config document: plans"
{
  "plans": [
    {
      "key": "max_monthly",
      "name": "Max",
      "billing_mode": "prepaid",
      "currency": "usd",
      "interval": "month",
      "credit_type": "ai_credits",
      "recurring_fee": { "amount": 10000, "currency": "usd" },
      "pools": [{ "key": "included", "name": "Included usage" }],
      "allowances": [
        {
          "key": "monthly",
          "kind": "refill",
          "pool": "included",
          "amount": "5000",
          "window": { "type": "fixed", "duration": "P1M", "anchor": "billing_period" },
          "alert_at": ["0.8", "1"]
        },
        {
          "key": "session",
          "kind": "cap",
          "pool": "included",
          "amount": "1500",
          "window": { "type": "session", "duration": "PT5H" },
          "alert_at": ["0.8", "1"]
        },
        {
          "key": "weekly",
          "kind": "cap",
          "amount": "3500",
          "window": { "type": "fixed", "duration": "P1W", "anchor": "calendar" },
          "alert_at": ["0.8"]
        }
      ],
      "rates": [{ "event_name": "agent.task", "credits_per_unit": "10" }]
    }
  ]
}
```

The `weekly` cap has no `pool`, so it counts consumption from every pool. The `session` cap counts only the `included` pool.

A plan sets either `included_credits` or `allowances`, not both. `included_credits` is a monthly `refill` with no cap, and a request with both returns `parameter_invalid`.

## What counts

A cap counts credits drawn from its pool, after any [burn multiplier](/guides/pools#burn-multipliers). Holds count as held, not as used: `remaining` is reduced, `percent_used` isn't. A hold that is released gives the room back. A reversal gives credits back to the window it was drawn from, if that window is still open.

## `per_member` windows

Set `scope: per_member` on an allowance and each distinct `member_id` gets its own window. A refill grants `amount` for each member at the member's first `check()` or `track()` of a window, before that call is measured. That is how a plan adds a weekly allowance for every seat. Without `member_id` on the call, a `per_member` allowance doesn't apply, so send it on `check()` and `track()`. See [Members, teams and agents](/guides/members-teams-agents).

The members' refills land in the allowance's pool, and every member draws on that pool. A refill's window shows what its member used against `amount` but doesn't stop them, so while the pool has credits a member can draw on other members' refills. To hold each member to `amount`, add a `per_member` cap on the same window, which denies a member's `check()` once their window is used up. What the refills leave expires with their window and never moves into another pool.

## Rollover

A `refill` on a `fixed` window can carry unspent credits forward with `rollover`: `none` (the default), `all` or `capped` with `rollover_cap`. Rolled credits become a new grant with `source: rollover`, and last until the end of the next window, or for `rollover_expires_after` when you set it.

## When a cap is full

`on_exhausted` on a cap chooses what happens when the window has no room:

| `on_exhausted`    | Result                                                                                                               |
| ----------------- | -------------------------------------------------------------------------------------------------------------------- |
| `block` (default) | `check()` denies with the reason `allowance_exhausted`, and `denied_by` names the allowance and its `resets_at`      |
| `draw_next_pool`  | The capped pool is skipped and work draws from the pools after it. A cap on `included` sends the work to `purchased` |

`draw_next_pool` needs the cap to name a `pool`.

**`Response: CheckResult`**

```json title="Response: CheckResult"
{
  "object": "check",
  "allowed": false,
  "reason": "allowance_exhausted",
  "customer": "cus_9sF2kq81LmQz",
  "credit_type": "ai_credits",
  "required": "40",
  "available": "3200",
  "denied_by": {
    "type": "allowance",
    "id": "session",
    "resets_at": "2026-11-04T18:00:00Z",
    "remaining": "10"
  },
  "affordability": {
    "balance": { "available_before": "3200", "available_after": "3160", "shortfall": "0" },
    "pools": [
      { "pool": "included", "available_before": "3000", "would_draw": "40", "available_after": "2960" },
      { "pool": "purchased", "available_before": "200", "would_draw": "0", "available_after": "200" }
    ],
    "allowances": [
      {
        "key": "monthly",
        "kind": "refill",
        "event_name": null,
        "pool": "included",
        "on_exhausted": "draw_next_pool",
        "member_id": null,
        "counted": "40",
        "remaining_before": "3000",
        "remaining_after": "2960",
        "shortfall": "0",
        "resets_at": "2026-12-01T00:00:00Z"
      },
      {
        "key": "session",
        "kind": "cap",
        "event_name": null,
        "pool": "included",
        "on_exhausted": "block",
        "member_id": null,
        "counted": "40",
        "remaining_before": "10",
        "remaining_after": "-30",
        "shortfall": "30",
        "resets_at": "2026-11-04T18:00:00Z"
      },
      {
        "key": "weekly",
        "kind": "cap",
        "event_name": null,
        "pool": null,
        "on_exhausted": "block",
        "member_id": null,
        "counted": "40",
        "remaining_before": "2400",
        "remaining_after": "2360",
        "shortfall": "0",
        "resets_at": "2026-11-09T00:00:00Z"
      }
    ],
    "spend_limits": [],
    "would_exceed": true,
    "reason": "allowance_exhausted"
  },
  "livemode": false
}
```

The `affordability` block lists every window that applies to the work, the monthly refill as well as the two caps, each with the room left and what happens past it. `would_exceed` is `true` here because the `session` cap stops at its cap: it has 10 left and the work counts 40, so `shortfall` is `"30"`. Only an allowance that stops the work sets it. An allowance that moves the work to the next pool (`draw_next_pool`) or bills it as overage (an included quantity on a postpaid plan) still lists its `shortfall`, as the part that moves on or is billed, and leaves `would_exceed` `false`. The [Checks and holds](/concepts/checks-and-holds#past-an-allowance-overage-or-a-stop) guide shows both. Allowances apply to prepaid plans, so on a postpaid plan `check()` lists no windows.

`track()` never blocks. Work that has already happened is recorded, takes the window past its amount, and carries the warning `allowance_exhausted`.

## Read the meters

`GET /v1/customers/{id}/usage_summary` returns every window with what was granted, used and held, the share used and the reset time, and names the `binding_window`, the one that will stop the customer first. See [Usage meters](/guides/usage-summary). To see which customers are closest to the end of an allowance, `GET /v1/reports/allowance_use` ranks them by the share used, and `percent_used_over` keeps only those past a threshold. See [Reports and charts](/guides/reports#who-is-close-to-the-end-of-an-allowance).

## Alerts

`alert_at` on an allowance lists decimals from 0 to 1, up to five. The first time a window's `percent_used` reaches each one, Runledger sends `usage.threshold_reached`, once per window. `["0.8", "1"]` means 80% and 100%. See [Auto top-up and alerts](/guides/auto-top-up-and-alerts).

## What locks

Allowances, like rates, are part of a plan's pricing. They can change until the first customer subscribes, and after that an update returns `plan_locked`. Create a new plan and move customers with `change_plan`, which takes effect at the next period.

## Test it

A [test clock](/api-reference/test-in-the-sandbox) moves windows. Advance one past a window's `resets_at` and the refill grant appears, or the cap clears. The `window_cap` scenario in the MCP server's `run_scenario` tool spends a cap to its limit, shows the denial, then advances past the reset.

## Next steps

* [Pools](/guides/pools) decide which credits a window refills and draws from.
* [Spend limits](/guides/spend-limits) cap money, where a window caps credits.
* [Usage meters](/guides/usage-summary) show a customer each window.
* [Recipes](/guides/recipes) puts windows together for common billing models.