What you can build
Subscriptions
Bill monthly or annual plans without sending the customer back to checkout every cycle.
Usage-based billing
Bill for metered usage at the end of each billing period, for any amount within your limits.
Auto top-ups
Refill a prepaid balance or credits automatically when it runs low.
One-click repeat purchases
Let returning customers pay without connecting a wallet and signing again.
Why recurring payments
- One approval, many payments. The customer signs once. You authorize payments whenever your billing logic says so.
- Customers keep custody. Funds stay in the customer’s wallet until a payment pulls them. Nothing is prefunded or locked up.
- No gas fees for customers. Approving is gasless and so is every payment. Customers pay the exact amount, nothing more.
- The lifecycle you already built. Each payment is a payment session. Capture, void, refund, webhooks, and reporting don’t change.
- Pay from any supported network. Customers approve from Base, Ethereum, Arbitrum, Optimism, or Polygon. Every payment settles the same way as other wallet payments.
- Customers stay in control. Every mandate has spending limits, and customers can remove their approval at any time.
How it works
1
Create a mandate
Call
POST /v2/mandates with the spending limits for this customer.2
The customer approves once
Send the customer to Coinbase’s hosted approval page, or embed it in your app. They connect their self-custody wallet and sign a single gasless approval.
3
Get paid on your schedule
For each payment, create a payment session and authorize it with the
mandateId. The customer doesn’t need to be present.1. Create a mandate
Create one mandate per customer relationship, such as one per subscription. Set spending limits that fit your billing model.Example response
Example response
mandateId. You can’t set it yourself, so save it with your customer record. You’ll need it to authorize every payment. To attach your own identifiers, such as a customer or subscription ID, use metadata.
created status. You can’t authorize payments with it until the customer approves it.
2. Get the customer’s approval
The customer approves the mandate once from their self-custody wallet, for example during signup or checkout. Redirect the customer to the mandate’surl, or embed the approval in your page by calling render({ mandateId }) on the <coinbase-payment> web component. Coinbase connects the wallet, picks the best-funded network, and collects the signature. See Hosted Checkout for setup.
The approval returns in pending status and confirms onchain shortly after. Wait for the acceptance.mandate.approval_succeeded webhook before you authorize a payment. At that point the mandate moves to approval_succeeded and its source shows the wallet address and network the customer approved from. If approval fails, the mandate moves to approval_failed and the customer can try again.
Build your own approval UI
Build your own approval UI
You only need these endpoints if you’re not using the hosted page or embedded component. They don’t use your API key, because the customer’s wallet signature is the proof of consent, so you can call them from your frontend. The flow mirrors wallet authorization.
-
Get approval options for one to five of the customer’s wallet addresses:
Coinbase returns at most one option, choosing the address and network with the highest stablecoin balance. Addresses that can’t be used appear in
ineligibleAddresseswith a reason, such asinsufficient_funds. -
Pass the option’s
eip2612payloaddatatoeth_signTypedData_v4in the customer’s wallet. -
Submit the signature:
GET /v2/mandates/{mandateId}/revocations/wallet/options, have the customer sign the returned eip2612 payload, and submit the signature to POST /v2/mandates/{mandateId}/revocations/wallet.3. Authorize a payment
For each payment, create a payment session for the amount due, then authorize it with the mandate. The customer doesn’t need to take any action.pending status and moves to succeeded or failed. From there the session follows the standard lifecycle:
- Auto-capture suits most recurring payments, because you’ve already delivered the service or are about to.
- Voids and refunds always return funds to the wallet and network the customer approved from.
- Idempotency keys let you retry an authorization safely. Always send
X-Idempotency-Keyfrom your billing jobs so a retry never pulls funds twice.
Spending limits
Every mandate has apolicy that caps what you can authorize. Coinbase checks the policy on every authorization.
All limits are optional. Any limit you omit gets a Coinbase default, and limits above Coinbase’s maximums are lowered to the maximum. The response always shows the limits that apply. An authorization over a limit is rejected with
422 mandate_policy_violation, and no funds move.
We recommend setting your own limits that match your billing model. Your business is responsible for payments authorized with its mandates, so tight limits protect your customers from misuse. Leave some headroom above your expected payment amounts for plan upgrades, taxes, or usage spikes. Mandate terms can’t be edited after you create the mandate. To change them, cancel the mandate, create a new one, and ask the customer to approve it.
Ending a mandate
There are two ways to end a mandate, and they’re independent.
When a customer ends their subscription, cancel the mandate so no further payments can be authorized, and point them to
revocationUrl if they also want to remove the wallet approval. If a customer removes the approval directly from their wallet, your next authorization fails.
Mandate statuses
status reflects the most recent action on the mandate.
To decide whether you can authorize a payment with a mandate, check its timestamps rather than relying on
status alone. A mandate is usable when approvedAt is set, canceledAt and revokedAt are empty, expiresAt is empty or in the future, and no revocation is in progress (status isn’t revocation_pending). For example, a mandate in revocation_failed is still usable because the approval was never removed.
Webhooks
Subscribe toacceptance.mandate.* events to track approvals and cancellations. Payments authorized with a mandate use the existing payment session events.
Every mandate event’s
data contains the full mandate. Approval events also include approval, and revocation events include revocation, each with an error when the attempt fails. See example payloads and Webhooks for setup.
Errors
These errors mean Coinbase rejected a mandate authorization before any funds moved. Coinbase checks the mandate in this order and returns the first match.
Standard payment session errors, such as an expired authorization window, also apply.
What to read next
Payment Sessions
Create the session you authorize with a mandate
Authorization
Compare mandate, wallet, Coinbase, and x402 authorization
Webhooks
Subscribe to mandate and payment session events
Example payloads
See the shape of mandate webhook events