Amend a mandate — append a new version (ALPHA)
Alpha — early access.
Appends a NEW immutable version to an ACTIVE mandate, carrying a changed rule into force with ONE signature and WITHOUT resetting the spend already made in the current window.
Mandates are immutable, so a limit could previously only be changed by cancel-then-create — which cost two signing ceremonies AND silently gave the agent a fresh budget, because usage is recorded per mandate and a replacement starts with empty buckets. Versions fix that: usage keeps accruing to the MANDATE, so an agent that has spent 9,000 of a 10,000 monthly limit and is amended to 20,000 has 11,000 left — not 20,000.
Versioning is append-only. The outgoing version’s rule is never rewritten and stays readable at GET /mandates//versions; the newest version governs from the moment it lands.
Only the amount fields (max_per_tx and the window caps) and targets may change. target_type, window, asset and network_id are frozen — changing one is rejected with a 400 telling you to cancel this mandate and create a new one. The window in particular is frozen because usage rows carry no window label, so changing it would retroactively re-bucket every past spend.
ADDING a payee to targets additionally requires the resulting rule to carry an aggregate ceiling — max_amount_in_window or max_count_in_window. Without one, each payee has its own separate budget, so a new payee brings a new budget and the payee list alone would move what the agent can spend in total, with no amount you approved having changed. Set the ceiling in the SAME amendment that adds the payee: the amount fields are amendable, so this costs no second signature and no budget reset. A count-preserving swap counts as adding, because the incoming payee starts with an empty budget. REMOVING payees is always allowed — it can only lower what the agent may spend.
§8 applies as it does to every mandate mutation: the amender must be a recognized signer of the mandate’s customer OTHER than the bound one. The signature covers the “amend” verb, the new rule, AND the version number being created, so a signature for v2 cannot be replayed to create v3.
Note that LOWERING a limit below what has already been spent leaves zero remaining, not a negative balance — the agent is frozen for the rest of the window. That is correct (spend cannot be undone) but it is worth saying out loud to a user, because “reduce my limit” and “stop all payments this month” feel like different actions.
Authorizations
Path Parameters
Body
A new version of an active mandate's rule, signed by a recognized signer of the mandate's customer other than the bound one (§8).
The rule below is taken VERBATIM — it is stored, and the signature is verified, exactly as sent. Unlike mandate creation, this endpoint does NOT normalize it: a rule that is not already canonical is refused with 400 naming the offending field, rather than being rewritten into something you cannot reproduce. So window must be present and non-empty (send "NONE" for a lifetime window), targets must be recipient ids and never payee names, and asset must already be uppercase. This is what lets you compute the signed bytes from the body you are about to send.
The amending signer's public key. Must differ from the mandate's bound signer.
Signature over the JCS-canonical amend payload — the same shape as the approve/cancel payload with action set to "amend", rule set to the NEW rule below EXACTLY as you send it, and one extra key, version, set to the version being created (the mandate's current version + 1). Including the version is what stops a signature for v2 from being replayed to create v3. Because the rule is never normalized on this path, the bytes you sign are the bytes the server verifies.
The complete NEW rule, not a patch, in canonical form — it is stored and signed verbatim, never normalized. window is REQUIRED here (use "NONE" for a lifetime window) even though it is optional when creating; targets must be recipient ids, not payee names; asset must be uppercase. Anything else is refused with 400 naming the field, not silently rewritten. target_type, window, asset and network_id must additionally match the current version exactly; only the amount fields and targets may differ.
Response
The mandate at its new version. version is the version just created; rule is now that version's rule.
expired is DERIVED, never stored - a pending or active mandate whose valid_until has passed. It cannot authorize payments and cannot be approved; it can still be cancelled.
pending, active, expired, rejected, revoked, done The customer this mandate is anchored to - approval requires a recognized signer of this customer other than the bound one (§8), and recipient targets belong to it. Absent only on rows created before the anchor existed.
DERIVED, display-only - the rule's recipient targets resolved to names, parallel to rule.targets (raw id on a miss). Absent for address/any target kinds. The ids in the rule remain the grant.
The CURRENT version number (1 for a mandate that has never been amended). rule below is this version's rule — the one the gate governs on. Each version is immutable and independently signed; the full history is at GET /mandates/{mandate_id}/versions.
Unix time of the §8 approval; absent until approved.
The signer that cancelled the mandate while it was still pending (§8); absent otherwise.
The signer that cancelled the mandate after activation (§8); absent otherwise.

