How it works
1
Your server creates the session
Send a
POST to /api/merchant/checkout with the amount in USDC. You get back a checkout_url and a session_id.2
Send your customer to the payment page
Open
checkout_url for them. The page shows your business name, the amount, and the address to pay to.3
Your customer pays
In USDC or USDT, on any of six networks, to the same address either way. Or with M-Pesa.
4
We convert it and pay you
If the payment came in on another network, we move it to Base for you. USDT is converted to USDC first, which takes 30 to 50 seconds. Then we convert it to your currency and send your payout.
5
You hear back
We send a webhook when the payment is
completed, failed, or expired. You can also check the status yourself at any time.Pay with M-Pesa too
Every checkout session can offer M-Pesa, whatever currency you’re paid in. Your customer sees a Crypto / M-Pesa switch on the payment page and picks whichever is easier. That’s handy if you sell something like event tickets to a mixed crowd: people who hold crypto pay with it, and everyone else pays with M-Pesa. The M-Pesa option only appears when the amount is within M-Pesa’s limits, so a very small or very large amount hides it instead of offering a payment that would fail. When a customer picks it, they enter their phone number and approve the payment prompt that pops up on their phone. The money arrives as USDC in your wallet, just like a crypto payment, and is paid out the same way.Create a session
Request fields
number
required
The amount in USDC, greater than 0. Your customer pays this in USDC, the same amount in USDT, or the KES equivalent with M-Pesa.
string
Shown on the payment page.
string
Your own reference, such as an order ID. We send it back to you in every webhook.
string
Used for the customer’s receipt.
string
fiat or usdc. Changes how this one payment is paid out, without touching your account setting. See Settlement mode.string
BASE, ARB, AVAX, OP, ETH, or MATIC. Only used when settlement_mode is usdc.string
Where to send your customer after they pay. A full
https URL, up to 2048 characters. See Sending the customer back.Response (201)
Sending the customer back to your site
Setredirect_url when you create the session and we’ll send your customer back to you once they’ve paid. Without it, they finish on our page. That’s fine for a link at a shop counter, but an online store usually wants to show its own confirmation page.
Only sessions created through the API can do this. Payment links always finish on our page.
Where your customer lands
We add two parameters to your URL and leave everything else as it was:
So
https://yourstore.com/thanks?order=4821 becomes:
session_id or status, we keep yours and don’t add ours. Use different names if you want both.
When it happens
When the payment iscompleted, we send your customer back automatically a few seconds after the success screen appears. If they tap to see their receipt, we stay put, and a button to go back is always there.
When it’s failed or expired, we show the link back but let your customer choose when to use it. They may want to try again or read what happened first.
Because we only send customers back automatically when the payment is completed, which is also when we send you checkout.completed, the two always agree.
Settlement mode
Each payment is paid out the way your account is set up in Settings, unless you change it for one session withsettlement_mode:
fiat: we convert the payment to your local currency and send it to your account, as usual.usdc: we skip the conversion and keep the payment in your wallet as USDC.
settlement_chain picks which network it ends up on. Base needs no extra step. For any other network, we move the money there automatically once the payment is confirmed.
checkout.completed arrives before that move finishes, so wait for the checkout.forwarded webhook, or check the session’s forward field, before treating the money as arrived. If the move can’t be completed, we send settlement.forward_failed and your USDC stays safe on Base.
Two more endpoints help here, and both work with the API key you already use for checkout:
GET /api/merchant/settlementshows your current mode, network, fee rate, and which networks you can switch to.GET /api/merchant/balancesshows your USDC balance on every network you’ve been paid on. Anullbalance means we couldn’t read it just now, not that it’s empty.
Using external_id
Pass your order ID asexternal_id when you create the session. We send it back in every webhook, so you can match each payment to your order without keeping your own list of session IDs.

