An Allowance is an on-chain authorization that permits a service (the grantee) to spend up to a capped total amount from an agent’s wallet (the granter). Unlike subscriptions, allowances have no fixed billing cycle — the grantee draws down against the cap as needed, up to the maximum.

Allowance structure

interface Allowance {
  id: PublicKey;         // On-chain allowance address
  granter: PublicKey;    // Agent wallet authorizing the spend
  grantee: PublicKey;    // Service or facilitator permitted to spend
  token: PublicKey;      // SPL token mint (e.g. USDC)
  maxAmount: number;     // Total spend cap, in token base units
  spent: number;         // Accumulated spend so far
  expiresAt?: number;    // Optional Unix timestamp
}

How allowances differ from subscriptions

Subscriptions pull a fixed amount at a fixed interval. Allowances work differently: the total spend cap is set once, and the grantee can draw against it as many times as needed, as quickly or slowly as needed, until the cap is exhausted or the allowance expires.

This makes allowances the right tool for:

  • Metered billing — a service charges per API call, per token, or per unit of compute. The agent authorizes a total spend cap; the service draws down as usage accumulates.
  • One-time purchases — an agent authorizes a specific maximum for a single operation without going through the full subscription lifecycle.
  • Overage billing — when a subscription plan includes metered overage above its included quota, the overage portion is handled by an allowance running alongside the subscription.

Creating an allowance

const allowance = await agent.createAllowance({
  grantee: "provider_wallet_address",
  token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
  maxAmount: 10_000_000,   // 10 USDC total cap
  expiresAt: Date.now() / 1000 + 86400, // expires in 24 hours
});

console.log(allowance.id);        // on-chain allowance address
console.log(allowance.maxAmount); // 10_000_000
console.log(allowance.spent);     // 0

Spending against an allowance

On the service provider side, drawing down against an allowance uses deductAllowance:

const result = await recur.deductAllowance({
  allowanceId: allowance.id,
  amount: 500_000, // 0.50 USDC for this request
});

console.log(result.remaining); // remaining balance

If the deduction would exceed maxAmount, the call fails with an AllowanceExhausted error. The on-chain program enforces this — Recur cannot allow a service to overdraw.

Checking an allowance

const status = await agent.getAllowance({ allowanceId: allowance.id });

console.log(status.spent);     // amount spent so far
console.log(status.remaining); // maxAmount - spent
console.log(status.expired);   // boolean

Expiry

If expiresAt is set and the timestamp has passed, the allowance cannot be drawn against. Any unspent balance automatically returns to the agent’s control — there is no “return funds” step required because the funds never left the agent’s wallet. The allowance only authorized spending; actual token transfers happen at draw time.

Revoking an allowance

An agent can revoke an allowance before it expires:

await agent.revokeAllowance({ allowanceId: allowance.id });

Revocation is on-chain and immediate. Any in-flight deductions already signed will still execute, but no new draws are possible after revocation.

Allowances and subscriptions together

When a plan includes metered overage, agent.subscribe() creates both a Subscription account and a companion Allowance account in a single transaction. The subscription handles the fixed monthly fee; the allowance handles any overage. Both are visible in the Dashboard and in webhook events.

The agent authorizes a maxOveragePerCycle at subscribe time:

const sub = await agent.subscribe({
  planId: plan.id,
  maxOveragePerCycle: 20_000_000, // authorize up to 20 USDC in overage per month
});