Docs
Get an API key

Choose mandate constraints

Choose and combine amount, payee, time, category-budget and charge-count rules without widening authority.

In short

1 callFor people and agents
  1. 1GET/v1/mandate/categoriesCharge categories

The same list heads this page's Markdown:/mandate/mandate-constraints.md

payment_amount sets the outer amount or budget. Constraints narrow that authority: which merchants may be paid, how small or large a charge may be, when it may happen, how much one category may consume, and how many charges may settle.

Every supplied constraint must pass. A rule the service does not understand fails closed rather than widening the user's permission.

A charge must pass every constraint on the mandate, then the top-level payment_amount check. Any failure makes it invalid.

Start with the intended charge

Verify and exercise evaluate constraints against a charge:

FieldRequiredMeaning
amountYesInteger minor units.
currencyYesCompared exactly with the mandate and relevant constraints.
payee_idYesMerchant receiving the payment.
categoryFor category-scoped rulesTransaction category from GET /v1/mandate/categories.
atNoISO-8601 charge time; defaults to current UTC time.

Always verify with the exact charge settlement intends to exercise. If charge is omitted, the service checks the token and lifecycle but skips constraints and the top-level currency/amount comparison.

Pick the smallest useful set

GoalConstraintTypical use
Keep each charge inside a rangepayment.amount_rangeA fare must be between the quoted minimum and maximum
Restrict merchantspayment.allowed_payeesAn open travel budget may reach only approved airline and hotel IDs
Restrict timepayment.execution_datePayment may settle only during a booking window
Cap one category over timepayment.category_budgetFlights may consume at most USD 1,800 of a larger trip budget
Cap the number of settlementspayment.max_chargesA delegated budget may settle no more than three times

Avoid duplicating a top-level rule unless the narrower constraint communicates real user intent. For example, a closed mandate already names one payee, while an open mandate usually needs payment.allowed_payees.

Amount range

{
  "type": "payment.amount_range",
  "currency": "USD",
  "min": 100000,
  "max": 180000
}

min and max are inclusive. The charge currency must equal the constraint currency.

Missing min behaves as 0.

Missing max also behaves as 0, so the rule rejects every positive charge. Always set currency and max explicitly.

Allowed payees

{
  "type": "payment.allowed_payees",
  "allowed": [
    { "id": "airline_demo" },
    { "id": "hotel_demo" }
  ]
}

The charge's payee_id must match one allowed[].id. A missing or empty list rejects every payee.

This is the primary merchant boundary for an open mandate whose top-level payee is omitted.

Execution window

{
  "type": "payment.execution_date",
  "not_before": "2026-08-23T00:00:00+00:00",
  "not_after": "2026-08-30T23:59:59+00:00"
}

The charge's at value must fall inside the inclusive window. Each bound is optional. If at is omitted, evaluation uses the current UTC time.

Use timezone-aware values with explicit offsets for the charge and both bounds.

Category budget

{
  "type": "payment.category_budget",
  "category": "flight",
  "currency": "USD",
  "max": 180000
}

This is a running sub-budget. For a matching charge, the service adds the current amount to prior charged events in that category and requires the total to stay at or below max.

Evaluation behaves as follows:

  • no charge category → fail closed,
  • different category → this constraint passes as not applicable,
  • matching category with different currency → fail,
  • matching category inside the remaining sub-budget → pass.

The total open-mandate budget still applies to every category. A category without its own sub-budget is bounded by payment_amount and the other constraints.

Issue validates category names against GET /v1/mandate/categories. A payment.category_budget without category, or with an unknown category, returns HTTP 422.

Category budgets are most useful on open mandates. On a closed mandate there is no prior spend, so the rule acts only as a per-charge cap.

Maximum charges

Mandate-wide:

{
  "type": "payment.max_charges",
  "max": 3
}

Category-specific:

{
  "type": "payment.max_charges",
  "category": "flight",
  "max": 1
}

Without category, every successful open-mandate charge counts. With category, only matching charged events count; an unlabeled charge fails closed and a differently labeled charge is outside this particular rule.

On a closed mandate, prior charge count is always zero before its one allowed exercise, so a max of 1 or more adds no useful restriction.

Top-level amount checks still apply

After constraint evaluation, the service compares the charge with payment_amount:

  • different currency → failed currency check,
  • closed amount above the cap → failed amount check,
  • open amount plus prior spend above the total budget → failed budget check.

Passing those top-level comparisons does not add a success entry. These checks appear only when they fail.

Unknown and incomplete rules

An unsupported type produces a failed check:

{
  "check": "payment.something_else",
  "ok": false,
  "reason": "unresolved_constraint: payment.something_else"
}

Most constraint fields are stored as supplied and evaluated later. Test the full issue → confirm → verify path with representative charges before deploying a new constraint set.

Stateful limits and concurrency

payment.category_budget, payment.max_charges, and the open total budget depend on earlier charges.

Verify reads that ledger without a lock, so its result is advisory. Exercise locks the mandate row, recalculates all stateful limits, and records charged only when they still pass. Concurrent settlement attempts therefore cannot jointly overrun a limit.

Continue

On this page

Endpoints on this page

GETlistMandateCategories