Allowances
On-chain spend caps for metered and one-time billing.
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
});