Troubleshooting: payment-mandate confirmation failures
An AP2 payment mandate is the signed, spend-capped consent an AI agent transacts under: a caller pre-authorizes a per-transaction maximum, a total cap, a currency, an allow-list of merchants and categories, and an expiry, and the agent can then charge carts against that mandate without asking per-cart. On a live voice call the agent reads the cart (amount, currency, merchant) back to the caller, gets explicit verbal approval, and invokes the confirmation tool — the request this page triages. For the mandate concept itself, read Commerce checkout spine and the Conversational Commerce Checkout guide first. The confirmation has three customer-facing failure codes, and all three fail closed — the customer is never charged on ambiguity. A failed response means nothing persisted, so the whole mandate decision must be resolved before you retry. Read the error envelope first: theerror.code
field tells you which gate rejected the request. A typical envelope:
Cause table
Charge commit — MANDATE_CHARGE_FAILED (500)
This 500 raises when the mandate decision approved the cart but the commit step threw — the one failure where authorization succeeded and persistence failed. Before you treat it as a platform fault, check the mandate’s scope constraints: the most common trigger is a cart the mandate should never have approved — an amount past the per-transaction maximum, a merchant or category outside the allow-list, or a mandate that lapsed toexpired,
revoked, or exhausted between session-create and the confirmation call.
Check first:
- Amount within the per-transaction max. Read the mandate from the
voice session-create payload (
payment_mandate) and comparecharge.amountagainstmaxPerTransaction. A cart above the max must be split or the mandate re-issued with a higher max — no retry of the same cart will pass. - Merchant and category on the allow-lists.
charge.merchantmust appear inallowedMerchants(and the agent’s category inallowedCategories). Correct the catalog mapping at the agent layer and re-read the cart to the caller before re-confirming. - Mandate status and expiry. A mandate moves to
expired,revoked, orexhaustedover the course of a long call; the agent should re-checkmandate.statusbefore invoking the confirmation tool.
request_id, escalate (below) — that is a genuine commit failure, not a
scope mismatch.
Cap verification — MANDATE_SPEND_UNVERIFIABLE (503)
The mandate’s total cap is cumulative: every confirmed cart under the mandate spends againsttotalCap, and a new cart may only be authorized if
spent + charge.amount ≤ totalCap. To authorize safely, the confirmation
must read the prior confirmed spend — and it refuses rather than authorize
against a position it could not read or record. That refusal is this 503.
Treat it as a temporary-unavailable response, semantics identical to the
read-path health checks elsewhere in the platform. Do not re-authorize at
the agent layer — do not fall back to “assume it fits under the cap” and
never retry in a tight loop. Retry with backoff once the dependency is
healthy again; the customer was not charged on any ambiguity.
If the 503 persists past a few minutes, escalate with the request_id — a
standing 503 here means the confirmation cannot establish the mandate’s
cumulative position at all.
Reference discipline — IDEMPOTENCY_CONFLICT (409)
A single mandate legitimately funds many distinct carts in one call, so the replay token is the per-chargecharge.reference — the cart or order id the
agent read back to the caller — keyed together with the call and the
mandate. A retry of the same cart (a network blip, a gateway restart)
returns the stored confirmation without charging twice. A different cart
(amount, currency, or merchant changed) under the same reference
receives this 409 and is never replayed as confirmed.
The 409 is a stop-and-reconcile signal, not a retry signal:
- Pull the confirmed charges for the call (see the audit-trail section) and compare the recorded cart against the cart you are trying to confirm.
- In your order system, resolve which cart the reference actually belongs to. If the original cart was the right one, discard the new request. If the cart genuinely changed (an agent-side reprice mid-call), issue a new order id and re-read the full cart to the caller before confirming with the fresh reference.
Audit trail
Every successful confirmation is stamped onto the call record: readGET /api/v1/voice/calls/:id and look at
metadata.payment_mandate_confirmation. The stamp carries the mandate id,
the authorization id, the consent digest, the amount/currency/merchant, the
reference, whether the AP2 verifiable credential was issued, the recording
egress id, and the confirmation timestamp — the same call row the recording
lives on, so a reviewer pulling the recording sees the cryptographic
authorization alongside it. Use it to reconcile 409 conflicts and to prove
what was (and was not) charged.
Retry-safety and escalation
All three codes converge on one rule: a failure means the customer was not charged, and none resolve by retrying the identical request unchanged:MANDATE_CHARGE_FAILED— fix the mandate-scope mismatch first, then retry the identical cart (safe: a failed commit persisted nothing).MANDATE_SPEND_UNVERIFIABLE— retry only after the dependency recovers, with backoff. Never re-authorize at the agent layer.IDEMPOTENCY_CONFLICT— reconcile the reference before confirming again; a new cart needs a new reference.
request_id from the error envelope, the call id,
the mandate id, and the charge reference — the support team reads the same
payment_mandate_confirmation audit stamp on the call metadata that this
page polls.
Cross-references
- Commerce checkout spine — how agentic payment mandates plug into the checkout architecture
- Conversational Commerce Checkout — the mandate-based end-to-end walkthrough
- Secure payment capture — the agent-assisted DTMF card-capture flow, a different payment surface from AP2 mandates
- Troubleshooting hub — the index of all runbooks