# Spend & Rate Limits

> Pattern 09 · Standing authority · part of Agent Consent Patterns.
> Human page: https://agentconsent.dev/patterns/spend-rate-limits/

A budget cap, an action count, a time window: these read like billing settings, but for an agent they are consent. They're the standing answer to "how far may you go before you come back and ask?". So give them a surface that treats them that way, with each cap shown against live usage.

## Problem

An agent that can act on its own needs edges, and the most honest edges are numeric. "Spend up to $100 a week." "Send at most 20 emails a day." "Book no more than three nights without me." These aren't billing preferences buried in an account page, they're the shape of the authority the user is delegating.

But numeric limits are usually treated as an afterthought: a single spend cap in a settings screen, disconnected from any sense of how much has already been used, with no signal for what happens when the number is hit. So the user can't tell whether the agent is nowhere near its limit or one purchase away from it, and "reaching the cap" is left undefined, does the agent stop silently, error out, or quietly push past? A guardrail you can't see the edge of isn't a guardrail you can trust.

## Solution

Gather the numeric guardrails into one surface and treat each as a consent primitive:

- **Show usage against the cap.** Every limit meters what's been consumed this window next to the cap itself ("$34 of $100 this week"), so the boundary is something the user can read, not reconstruct.
- **Make the cap a control.** The number is editable in place, with its unit and window, so tightening or loosening the guardrail is a first-class action.
- **Define what reaching the cap means.** Hitting a limit is a *consent event*, not an error: the agent pauses and comes back to ask (through an [Action Preview](/patterns/action-preview/) or a fresh grant) rather than stopping silently or spending past the line.

> **Live demo** — an interactive Spend & Rate Limits demo runs on the page: https://agentconsent.dev/patterns/spend-rate-limits/

The demo is the `SpendLimits` component from `@agentconsent/react`. The travel budget is already over its cap, so it reads "cap reached, agent will ask"; raise that cap above what's been spent and watch the summary re-tally.

## Anatomy

> **Anatomy** — a labeled breakdown of the Spend & Rate Limits component's parts is shown on the interactive page: https://agentconsent.dev/patterns/spend-rate-limits/

## When to use it

- **Agents that spend money or act at volume.** Any agent that can make purchases, send messages in bulk, or call metered APIs needs a numeric ceiling the user can see and set.
- **Alongside an [Authority Boundary](/patterns/authority-boundary/).** Levels answer *whether* the agent may do something on its own; limits answer *how much*. "Automatic up to $50, ask above" is two patterns working together.
- **When the safe answer is a threshold, not a yes/no.** Some capabilities shouldn't be all-or-nothing. A rate is exactly the right amount of nuance.

## When not to use it

- **A single, per-action decision.** If the question is "approve *this* $2,000 transfer," that's an [Action Preview](/patterns/action-preview/) or an [Irreversibility Gate](/patterns/irreversibility-gate/), not a standing cap.
- **A capability that should be off entirely.** "Never spend money" is an [Authority Boundary](/patterns/authority-boundary/) level set to Never, not a cap of zero buried in a limits list.
- **Purely categorical authority.** If none of the guardrails are quantities (just what the agent may and may not do), you want levels, not numbers.

## Real-world examples

- **Card networks' agent payment frameworks.** Visa Intelligent Commerce and Mastercard Agent Pay both put user-set spending limits and conditions at the center of letting an agent hold a (tokenized) card, caps enforced on real-time transaction data, not promised by the agent. The payments industry independently converged on this pattern as the precondition for agentic commerce.
- **Virtual card controls.** Privacy.com, Ramp, and Lithic-style issued cards ship per-card merchant locks, per-transaction maximums, and monthly caps, increasingly the practical way people hand an agent money, because the ceiling lives at the card, outside the agent's reach.
- **Cloud and API budgets.** AWS Budgets alerts and actions, and the monthly usage caps on the OpenAI and Anthropic API dashboards, are numeric authority ceilings on autonomous consumption, with the same used-of-cap meters and threshold warnings this component renders.

*Annotated screenshots of these flows are being collected. Products are credited, annotations follow the site-wide callout conventions, and any screenshot is removed on request. See [About](/about/).*

## Accessibility

- The surface is a `role="group"` labelled by its title, so assistive tech announces the whole set of guardrails as one named region.
- **Each cap is a real, labelled number input.** Every limit's control has an accessible name naming the guardrail and its window ("Purchases cap, per week"), so it's an independent numeric field, not an unlabelled box.
- **Usage is text, not just a bar.** The meter's fill is decorative and `aria-hidden`; the "$34 of $100 this week" readout carries the real numbers, and a reached cap is stated in words ("cap reached, agent will ask"), so the boundary never depends on the colored bar to be read.
- **The summary is a polite live region.** Editing any cap re-tallies "1 cap reached · 1 near cap" without moving focus, so a non-visual user always knows how close the agent is to its edges.
- State is conveyed with **text and layout** in addition to color, near-cap and reached limits are never distinguished by hue alone.

## Anti-patterns

- **A cap with no usage.** A limit shown without how much has already been consumed tells the user a rule but not where they stand against it. Always meter usage against the cap.
- **An undefined edge.** Leaving "what happens at the limit" unspecified, silent stop, hard error, or quiet overspend all look the same from outside. Define reaching the cap as a consent event and say so.
- **Limits as billing, not consent.** Hiding spend caps in an account/billing screen, disconnected from the agent's authority surface, so the numbers that actually bound the agent are the ones nobody thinks to review.
- **Defaults set high "for convenience."** Shipping generous caps so the agent rarely has to ask makes the permissive choice the resting state. Default low and let the user raise the ceiling deliberately.
- **A limit that escalates past the principal.** A cap can only ever narrow what the user themselves may spend or do. It is a ceiling on the user's own authority, never a grant of new spending power. An agent allowed "$100 a week" still can't move money the user couldn't move; the limit bounds the intersection, it never widens it.

## Code

```tsx
import { SpendLimits } from "@agentconsent/react";
import "@agentconsent/react/theme.css";

<SpendLimits.Root
  limits={[                              // usage facts + the summary's source
    { id: "purchases", used: 34 },
    { id: "outreach", used: 17 },
  ]}
  value={caps}                           // { [id]: number }
  onValueChange={setCaps}
>
  <SpendLimits.Header>
    <SpendLimits.Icon>▤</SpendLimits.Icon>
    <SpendLimits.Title>
      How far can Shopping Agent go on its own?
    </SpendLimits.Title>
  </SpendLimits.Header>

  <SpendLimits.Summary>
    {({ reached, warning }) =>
      `${reached} cap reached · ${warning} near cap`}
  </SpendLimits.Summary>

  <SpendLimits.List>
    <SpendLimits.Limit
      id="purchases"
      kind="spend"                       // meters money, unit is a symbol
      unit="$"
      period="week"
      label="Purchases"
      description="Buy items on your behalf."
    />
    <SpendLimits.Limit
      id="outreach"
      kind="rate"                        // meters a count, unit is a noun
      unit="emails"
      period="day"
      label="Outreach emails"
      description="Message sellers on your behalf."
    />
  </SpendLimits.List>
</SpendLimits.Root>
```

The `limits` array is the single source of truth for usage and the summary;
the cap map (`value` / `defaultValue`) holds the editable ceilings. A limit with
no cap entry reads as unbounded. All parts are unstyled primitives with
`data-acp` attributes; skip `theme.css` and style them yourself, or override the
`--acp-*` tokens to retheme.
