The x402 mandate
POST /payment-agents/{payment_agent_id}/x402/mandates
Amounts are atomic units: USDC has 6 decimals, so
"1000000" is $1.00.
GET /payment-agents/{payment_agent_id}/x402/mandates lists the agent’s mandates, newest first and paged like the holds list (limit, starting_after, ending_before, answering data plus meta). Each shows the current window’s window_committed (atomic units) and window_calls.
Which mandate applies
A signature request is checked against the agent’s newest mandate that is currently valid and covers the payment’s asset and network. Older mandates for the same asset and network are not consulted, and each mandate counts its own window. A new mandate therefore starts with an empty window.Payee policies
Every payee is screened before anything is signed, whatever the mode — a refused address is a
403 and no budget is held. any_screened means screening is the only payee control; the allowlists add to it rather than replacing it. Omit payee_policy entirely to get any_screened. If you send it, mode is required and the list must match it — addresses only with address_allowlist, domains only with domain_allowlist, and neither with any_screened. Anything else is a 400, so a policy is never stored looking like a restriction that isn’t applied.
domain_allowlist is the readable form: a person can approve “any metered API under marketpulse.example” in a way they can’t approve a hex address. It judges the host of the resource_url you send with each signature request, so resource_url is required under this policy. When the seller’s terms name their resource, the two must match.
Holds
Every signature Dakota issues is recorded as a hold before the signature exists. The hold is what the budget counts, and it’s the only record of x402 spend: the seller’s facilitator moves the money, so there is no Dakota wallet transaction.outstanding counts because an issued signature is spendable money whether or not it has reached the chain yet. The budget never assumes a payment didn’t happen.
GET /payment-agents/{payment_agent_id}/x402/holds lists the agent’s holds, newest first, in pages: limit, starting_after and ending_before, with the rows in data and a meta object carrying total_count, has_more_after and has_more_before. Filter with ?state=outstanding, settled or released.
Reading holds needs both ReadMandates and ReadTransactions: they are the only record of what an agent paid over x402.
How holds resolve
Dakota reads the USDC contract directly rather than waiting to be told:- Settled: usually within about a minute of the seller’s facilitator submitting the transfer. The
tx_hashis the transaction the token contract recorded. - Released: a few minutes after
valid_before, once a Base block that can no longer be reorganized away (at least two minutes pastvalid_before) shows the authorization still unused. From that point the token refuses it for good. - Anything uncertain stays
outstanding, and so stays counted, until the chain gives a definite answer.
How long a signature is valid
valid_before is the earliest of:
- the seller’s
maxTimeoutSecondsfrom now - 10 minutes from now
- the mandate’s
valid_until
Refusals
A refused payment is never signed, and it doesn’t count against the budget.
Every
403 carries the specific reason in detail, for example payment of 700000 exceeds the per-call limit of 500000.
