openapi: 3.1.0
info:
title: Allowance Mandates API
version: 0.1.0-pre-launch
description: 'Consumer trust layer for AI agent payments.
## How it works
1. **Agent proposes a mandate.** The agent calls `POST /v1/mandates` with a suggested amount, merchant, and cadence. The mandate starts in `pending_approval` status.
2. **Human approves the mandate.** The mandate owner receives a notification (push, email, or in-app) and approves it. The mandate becomes `active`. The agent never triggers this step — it waits.
3. **Agent requests a virtual card.** When a purchase is needed, the agent calls `POST /v1/mandates/{id}/credential-requests`. Allowance automatically validates the request against the mandate rules (amount, merchant, time period). No human involvement. If valid, a single-use virtual card (PAN, expiry, CVV) is returned — the agent uses it like a card number at checkout. If not, the request is denied with reasons.
4. **Mandate lifecycle.** For `cadence: "once"` (immediate) mandates, the mandate auto-expires after the first virtual card is issued. For recurring mandates, the mandate stays active and the agent can request a new virtual card on each purchase cycle within the defined rules.
## AP2 compatibility
This API is designed to be a natural precursor to the [AP2 Agent Payments Protocol](https://ap2-protocol.org). The Allowance mandate maps directly to AP2''s Intent Mandate (human-not-present scenario). In a future AP2-compatible version, the mandate will be a cryptographically signed Verifiable Digital Credential (VDC), and human approval will produce a non-repudiable cryptographic attestation rather than an in-system state change. The same concepts and fields carry forward.
**Status: Pre-launch.** All endpoints return `501 Not Implemented`. Contact hello@useallowance.com for early access.'
contact:
name: Allowance
email: hello@useallowance.com
url: https://useallowance.com/
x-pre-launch: true
x-status-url: https://useallowance.com/status.json
x-ap2-compatible: true
servers:
- url: https://api.useallowance.com/v1
description: Production (pre-launch — not yet active)
security:
- ApiKeyAuth: []
tags:
- name: Mandates
paths:
/mandates:
post:
operationId: createMandate
summary: Propose a spending mandate
description: 'The agent proposes a mandate with a suggested budget, merchant restrictions, and cadence. The mandate is created with `status: pending_approval` and is not usable until the human owner approves it out-of-band (via push notification, email, or in-app review). The agent should poll `GET /v1/mandates/{id}` to detect when status becomes `active`.'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateMandateRequest'
examples:
flight:
$ref: '#/components/examples/FlightMandate'
household:
$ref: '#/components/examples/HouseholdMandate'
subscriptions:
$ref: '#/components/examples/SubscriptionMandate'
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
responses:
'201':
description: Mandate created in `pending_approval` status
content:
application/json:
schema:
$ref: '#/components/schemas/AllowanceMandate'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'501':
$ref: '#/components/responses/NotImplemented'
tags:
- Mandates
/mandates/{id}:
get:
operationId: getMandate
summary: Retrieve a mandate
description: 'Poll this endpoint to check whether the human has approved the mandate (`status: active`). Also use it to check remaining budget and expiry before requesting a credential.'
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^mnd_
description: 'Mandate ID (prefix: mnd_)'
responses:
'200':
description: Mandate retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/AllowanceMandate'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'501':
$ref: '#/components/responses/NotImplemented'
tags:
- Mandates
/mandates/{id}/revoke:
post:
operationId: revokeMandate
summary: Revoke a mandate
description: Immediately revoke an active or pending_approval mandate. Once revoked, no further credential requests will be accepted. Any virtual cards already issued remain valid until their own expires_at — revocation does not cancel in-flight transactions. Only callable by the mandate owner, not the agent.
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^mnd_
responses:
'200':
description: Mandate revoked
content:
application/json:
schema:
$ref: '#/components/schemas/AllowanceMandate'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'501':
$ref: '#/components/responses/NotImplemented'
tags:
- Mandates
/mandates/{id}/credential-requests:
post:
operationId: requestCredential
summary: Request a payment credential
description: 'Request a single-use virtual card to execute a specific purchase against an active mandate. Allowance automatically validates the request against the mandate rules — amount, merchant, merchant category, and time period. No human approval is needed at this step; the human already approved the mandate.
If validation passes, a virtual card (PAN, expiry, CVV) is returned. The agent uses these details at merchant checkout like a normal card number. The card is single-use, amount-capped, and short-lived — safety comes from these constraints, not from hiding credentials.
For `cadence: "once"` mandates, the mandate automatically moves to `exhausted` status after the first virtual card is issued.'
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^mnd_
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateCredentialRequest'
responses:
'201':
description: Credential request processed. Check `validation.passed` for the outcome.
content:
application/json:
schema:
$ref: '#/components/schemas/CredentialRequest'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'409':
description: Mandate is not active (pending_approval, expired, revoked, or exhausted)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/RateLimited'
'501':
$ref: '#/components/responses/NotImplemented'
x-rate-limit: 10 requests/minute per API key
tags:
- Mandates
/mandates/{id}/credential-requests/{cr_id}:
get:
operationId: getCredentialRequest
summary: Retrieve a credential request
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^mnd_
- name: cr_id
in: path
required: true
schema:
type: string
pattern: ^cr_
responses:
'200':
description: Credential request retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/CredentialRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'501':
$ref: '#/components/responses/NotImplemented'
tags:
- Mandates
components:
responses:
RateLimited:
description: Rate limit exceeded
headers:
Retry-After:
schema:
type: integer
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotImplemented:
description: 'Pre-launch: this endpoint is not yet active'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: not_implemented
message: Allowance is pre-launch. This endpoint is not yet active. Contact hello@useallowance.com for early access.
doc_url: https://useallowance.com/connect-agent
Unauthorized:
description: Missing or invalid API key
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Invalid request parameters
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
CredentialRequest:
type: object
required:
- id
- mandate_id
- amount
- currency
- merchant
- validation
- status
- created_at
properties:
id:
type: string
pattern: ^cr_
description: Unique credential request ID.
mandate_id:
type: string
pattern: ^mnd_
amount:
type: integer
description: Requested amount in minor currency units.
currency:
type: string
merchant:
type: string
merchant_category_code:
type: string
context:
type: string
idempotency_key:
type: string
metadata:
type: object
additionalProperties:
type: string
validation:
$ref: '#/components/schemas/ValidationResult'
virtual_card:
description: Single-use virtual card if validation passed, null if denied.
oneOf:
- $ref: '#/components/schemas/VirtualCard'
- type: 'null'
status:
type: string
enum:
- approved
- denied
- expired
description: approved = token issued, proceed with purchase. denied = mandate rules not satisfied, do not proceed. expired = mandate expired before this request was evaluated.
created_at:
type: string
format: date-time
CreateMandateRequest:
type: object
required:
- agent_id
- budget
properties:
agent_id:
type: string
budget:
$ref: '#/components/schemas/AllowanceBudget'
risk_controls:
type: object
properties:
require_merchant_match:
type: boolean
block_international:
type: boolean
max_daily_spend:
type:
- integer
- 'null'
velocity_limit_per_hour:
type:
- integer
- 'null'
metadata:
type: object
additionalProperties:
type: string
CreateCredentialRequest:
type: object
required:
- amount
- currency
- merchant
- context
properties:
amount:
type: integer
minimum: 1
description: 'Requested amount in minor currency units (cents). Example: 47231 = $472.31. Never use floats.'
currency:
type: string
pattern: ^[A-Z]{3}$
merchant:
type: string
description: Name of the merchant where the purchase will be made.
merchant_category_code:
type: string
pattern: ^[0-9]{4}$
description: 4-digit ISO 18245 MCC for the merchant.
context:
type: string
maxLength: 1024
description: Human-readable explanation of why this purchase is being made. Appears in the owner's transaction log. Be specific — this is the audit trail.
idempotency_key:
type: string
description: Always set this. Prevents duplicate credential issuance on retries.
metadata:
type: object
additionalProperties:
type: string
VirtualCard:
type: object
description: A single-use virtual card issued when a credential request is approved. The agent uses these details at checkout exactly like a physical card. Safety comes from the card being single-use, amount-capped, merchant-locked, and short-lived — not from hiding credentials.
required:
- pan
- expiry_month
- expiry_year
- cvv
- billing_zip
- expires_at
- single_use
- network
properties:
pan:
type: string
description: 16-digit card number. Use at merchant checkout.
expiry_month:
type: string
description: '2-digit expiry month. Example: ''09''.'
expiry_year:
type: string
description: '4-digit expiry year. Example: ''2026''.'
cvv:
type: string
description: 3-digit security code.
billing_zip:
type: string
description: Billing ZIP code for AVS verification.
network:
type: string
description: 'Card network. Example: visa, mastercard.'
expires_at:
type: string
format: date-time
description: When this virtual card expires. Short-lived — use immediately. Typically 15 minutes.
single_use:
type: boolean
description: Always true. This card is deactivated after one approved transaction regardless of cadence.
amount_limit:
type: integer
description: The maximum amount this card will approve, in minor units. Matches the credential request amount. Any charge above this is declined at the network level.
merchant_lock:
type:
- string
- 'null'
description: If set, the card is locked to this merchant name. Charges from other merchants are declined at the network level. null if the mandate has no merchant restriction.
ValidationResult:
type: object
required:
- passed
- constraints
- reasons
description: The system's automatic validation of a credential request against the active mandate rules. No human involvement — this is evaluated instantly.
properties:
passed:
type: boolean
description: Whether all mandate constraints were satisfied.
constraints:
type: object
description: Individual constraint checks.
properties:
within_amount_limit:
type: object
properties:
passed:
type: boolean
detail:
type: string
merchant_match:
type: object
properties:
passed:
type: boolean
detail:
type: string
mcc_match:
type: object
properties:
passed:
type: boolean
detail:
type: string
within_time_period:
type: object
properties:
passed:
type: boolean
detail:
type: string
cadence_not_exhausted:
type: object
properties:
passed:
type: boolean
detail:
type: string
velocity_check:
type: object
properties:
passed:
type: boolean
detail:
type: string
reasons:
type: array
items:
type: string
description: Human-readable reasons for the validation outcome. Always populated — useful for logging and debugging.
AllowanceBudget:
type: object
required:
- cadence
- per_transaction_max
- currency
properties:
cadence:
type: string
enum:
- once
- daily
- weekly
- monthly
- annually
- never
description: Spending cadence. 'once' = single-use mandate — expires after first credential is issued. 'never' = a lifetime cap that never resets. All others reset per interval.
per_transaction_max:
type: integer
minimum: 1
description: 'Maximum amount per credential request in minor currency units (cents for USD). Example: 50000 = $500.00.'
total_max:
type:
- integer
- 'null'
minimum: 1
description: Total cap across all credential requests in minor units. Resets per cadence interval. null = no cap.
currency:
type: string
pattern: ^[A-Z]{3}$
description: ISO 4217 currency code.
merchants:
type: array
items:
type: string
description: Allowlist of merchant names. Empty array = no merchant restriction.
merchant_category_codes:
type: array
items:
type: string
pattern: ^[0-9]{4}$
description: Allowlist of 4-digit ISO 18245 MCCs. Empty array = no MCC restriction.
categories:
type: array
items:
type: string
description: Semantic category labels. Supplementary to MCC — used for display and audit, not enforcement.
expires_at:
type:
- string
- 'null'
format: date-time
description: Mandate expiry as ISO 8601 datetime. null = no expiry. Always set this for sensitive or one-time tasks.
Error:
type: object
required:
- error
properties:
error:
type: object
required:
- code
- message
properties:
code:
type: string
message:
type: string
param:
type: string
doc_url:
type: string
format: uri
AllowanceMandate:
type: object
required:
- id
- owner_id
- agent_id
- budget
- status
- created_at
properties:
id:
type: string
pattern: ^mnd_
description: Unique mandate ID.
owner_id:
type: string
description: ID of the human user who owns and approved this mandate.
agent_id:
type: string
description: ID of the AI agent authorized to request credentials under this mandate.
budget:
$ref: '#/components/schemas/AllowanceBudget'
risk_controls:
type: object
properties:
require_merchant_match:
type: boolean
description: If true, credential requests must exactly match a merchant in the budget.merchants allowlist.
block_international:
type: boolean
description: If true, credential requests for international merchants are denied.
max_daily_spend:
type:
- integer
- 'null'
description: Hard daily cap across all credential requests, in minor units.
velocity_limit_per_hour:
type:
- integer
- 'null'
description: Maximum number of credential requests allowed per hour.
status:
type: string
enum:
- pending_approval
- active
- exhausted
- expired
- revoked
description: pending_approval = created, awaiting human approval. active = approved, credential requests accepted. exhausted = once mandate fully used. expired = past expires_at. revoked = manually cancelled.
approved_at:
type:
- string
- 'null'
format: date-time
description: When the human owner approved the mandate.
created_at:
type: string
format: date-time
expires_at:
type:
- string
- 'null'
format: date-time
metadata:
type: object
additionalProperties:
type: string
description: Up to 10 key-value string pairs. Use to record user instruction, task context, etc.
x-ap2-note:
type: string
description: In a future AP2-compatible version, this mandate will be issued as a signed Intent Mandate VDC and approved_at will carry a cryptographic attestation.
examples:
FlightMandate:
summary: Book a flight under $500 (immediate, once)
value:
agent_id: agent_claude_travel
budget:
cadence: once
per_transaction_max: 50000
total_max: 50000
currency: USD
merchants:
- United Airlines
merchant_category_codes:
- '4511'
categories:
- travel
- flights
expires_at: '2026-02-17T14:30:00Z'
risk_controls:
require_merchant_match: true
block_international: false
metadata:
task: book cheapest flight to NYC under $500
initiated_by: user_voice_command
SubscriptionMandate:
summary: Pay subscription renewals up to $20/month (recurring, open merchant)
value:
agent_id: agent_subscription_manager
budget:
cadence: monthly
per_transaction_max: 2000
total_max: null
currency: USD
merchants: []
merchant_category_codes:
- '7372'
- '7375'
- '5815'
categories:
- subscriptions
- software
- streaming
expires_at: null
risk_controls:
block_international: false
velocity_limit_per_hour: 3
metadata:
task: auto-renew approved subscriptions under $20/month
HouseholdMandate:
summary: Reorder household item under $40/month (recurring)
value:
agent_id: agent_household_reorder
budget:
cadence: monthly
per_transaction_max: 4000
total_max: null
currency: USD
merchants:
- Amazon
merchant_category_codes:
- '5912'
categories:
- household
- consumables
expires_at: null
risk_controls:
require_merchant_match: true
block_international: true
velocity_limit_per_hour: 1
metadata:
task: reorder paper towels when stock is low
product_asin: B07N1THQ6Z
parameters:
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
schema:
type: string
maxLength: 255
description: Client-generated unique key. Same key returns the same response without re-executing the operation. Always set this on credential requests.
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-Allowance-Key
description: API key with prefix alw_. Contact hello@useallowance.com for early access.
OAuth2:
type: oauth2
description: OAuth2 authorization code flow (planned — not yet active)
flows:
authorizationCode:
authorizationUrl: https://useallowance.com/oauth/authorize
tokenUrl: https://useallowance.com/oauth/token
scopes:
mandates:write: Propose and manage spending mandates
mandates:read: Read mandate details and status
credentials:request: Request payment credentials against active mandates
x-rate-limits:
standard: 100 requests per minute per API key
credential_requests_post: 10 requests per minute per API key
headers:
- X-RateLimit-Limit
- X-RateLimit-Remaining
- X-RateLimit-Reset
x-agent-notes: 'All monetary amounts are integers in minor currency units (cents for USD). Example: 47231 = $472.31. Never use floats. Always set Idempotency-Key on credential requests. Poll GET /v1/mandates/{id} to detect when a mandate moves from pending_approval to active — do not request credentials until the mandate is active.'
x-ap2:
compatible: true
mandate_mapping: AllowanceMandate → AP2 Intent Mandate (human-not-present scenario)
future_changes: Mandate body will be a signed VDC. Human approval will produce a cryptographic attestation. Payment tokens will be AP2 Payment Mandate VDCs.
spec_url: https://ap2-protocol.org/specification/