Skip to main content

Worked request and response samples

POSTing a charge requires an issued mandate. Issue the mandate first, post the charge against its id, then handle the authorize round-trip the endpoint’s gateway needs. The envelope below nests two objections: mandate id formatting (422 VALIDATION_ERROR) and replay credit protection (409 COMMERCE_MANDATE_ALREADY_SPENT). Steps 1 and 2 below are the frame that avoids both.

1. Issue the mandate

POST /api/v1/commerce/agent-mandate
The mandate snapshot — every field the wire round-trips mandate carries on the authorize/charge body — is this data. Hold it; every subsequent endpoint that touches a charge sends it back verbatim so the consent digest re-verifies before spend.

2. Dry-run authorize, then charge

POST /api/v1/commerce/agent-mandate/authorize — same shape the charge endpoint accepts; /charge commits the spend (returns charge and the updated mandate), /authorize checks the digest and allowance without committing. /authorize answers authorized: true | false because a mandate either passes or returns intentional detail ahead of the final call.
Next call POST /api/v1/commerce/agent-mandate/charge with the same body to commit the charge, or POST /api/v1/commerce/agent-mandate/revoke to invalidate the mandate before expiry. COMMERCE_MANDATE_ALREADY_SPENT below only fires when the same replay-protection reference spends twice — a client that always authorizes then charges once per mandate never pays the 409 tax.

Errors worth branching on

These five failures cover the charge create (POST /api/v1/commerce/charges), which one-tap checkout fires per order. Each block below is a full { error, meta } envelope as the API returns it, and the matrix at the bottom answers retry vs surface for the same five classes. For the platform-wide decision table these branches plug into, see the error handling guide.

401 — Unauthorized

A 401 on this page is the bearer key failing before the route ran — it never means the resource is wrong. Rotate the key or re-mint the scoped token; retrying the same request changes nothing.

403 — Forbidden

A 403 means the key authenticated but the operation is gated by scope — check the key’s scopes on the developer page; a 403 is never a data-not-found shape.

422 — Schema

A 422 means the payload did not match the request schema — branch on error.details.field and resend with the full mandate id instead of blind-retrying the same body.

429 — Rate

The Retry-After header and error.details.retry_after are both set on every 429 — resend the SAME request after the lower of the two.

409 — Conflict

Treat as idempotent-replay protection — return the original receipt instead of re-charging.

60-second retry matrix