Mandates & connections

A mandate is authority, declared once by your trusted backend: which customer, which actions, which limits, until when. Guard evaluates every proposal against it. A connection is the provider account the actions run on.

Written from the code · updated 26 Sep 2026 · Something wrong or missing? Tell us

Register a mandate

POST /v1/mandates — backend key or session (editor+). One mandate per ticket or conversation is the usual grain.

{
  "project_id": "…",
  "connection_id": "…",                       // optional: defaults to the project's Guard connection
  "source_request_id": "ticket_4711",         // your id; ties every operation back to the request
  "subject": { "customer_id": "cus_01", "payment_ids": ["pi_01"], "subscription_ids": ["sub_01"] },
  "actions": ["refund.create", "subscription.cancel"],
  "limits": {
    "currency": "eur",
    "max_amount_minor": 5000,                  // total refundable under this mandate; above → DENY
    "auto_allow_up_to_minor": 2000,            // per operation; above (but within max) → REVIEW
    "max_operations": 2,
    "cancel_modes_allowed": ["period_end"],
    "cancel_modes_review": ["immediate"]       // allowed only after a human approves
  },
  "expires_at": "2026-10-01T12:00:00Z",
  "approver": "[email protected]"       // optional label shown to reviewers
}

Response 201: the mandate with its mandate_id and version. Mandates are immutable; register a new one to change authority. An expired or exhausted mandate makes every further proposal DENY with reason mandate_inactive.

How limits map to decisions

ConditionDecision
action not in actions, wrong subject, wrong currency, over max_amount_minor, over max_operationsDENY
amount above auto_allow_up_to_minor but within the max; cancel mode in cancel_modes_reviewREVIEW (human approval)
provider state unreadable, per-end-user cap exceeded with HOLDHOLD (re-proposable later)
everything within limits, guardrails clearALLOW → executed once → verified

Connections

A connection is a provider account attached to a project. Two kinds exist today:

  • Sandbox (simulated Stripe) — POST /v1/projects/{id}/connections/sandbox creates an isolated world from a template fixture, optionally with faults (refund.create / subscription.cancel / read failures, delays). Test runs create their own worlds per attempt; this endpoint is for trying Guard without a Stripe account.
  • Stripe — connected from the project page (or POST /v1/projects/{id}/connections/stripe with secret_key + webhook_secret) with a restricted key; live keys must be restricted (rk_live_), and the permissions are probed read-only before saving. Rotate with …/connections/{connection_id}/rotate; remove with DELETE (refused while active mandates use it) (refunds and subscriptions write, read for verification) and a webhook signing secret. The key is encrypted per organisation and only the executor uses it.

Each project has one default Guard connection; a mandate may name another.

Per-end-user caps

GET / PUT /v1/projects/{id}/end-user-caps — up to six caps per project, keyed by the mandate subject’s customer_id:

{ "caps": [
  { "window": "day",   "max_actions": 3,                          "action_on_exceed": "HOLD" },
  { "window": "month", "max_amount_minor": 20000, "action_on_exceed": "DENY" }
] }
Mandates & connections · Docs · COLVO