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/