API Gateway
Quickstart
This walks one authorize payment end to end on a testnet: the buyer pays 10.50 USDC into
the rail0 escrow, then the merchant captures it. Every HTTP call has a full sample in the
API reference; here the focus is the order of things.
The steps use plain HTTP so every call is visible. In an app, the
TypeScript and Ruby SDKs run the same flow, signing included.
-
Pick a chain and token. The catalog is public:
Terminal window curl "https://api.rail0.xyz/blockchains"curl "https://api.rail0.xyz/tokens?chain_id=84532&active=true"And the merchant’s accepted methods — only these combinations can be paid:
Terminal window curl "https://api.rail0.xyz/payment_methods?address=0x71C7656EC7ab88b098defB751B7401B5f6d8976F" -
Sign in as the buyer. Request a nonce, have the wallet sign a SIWE message carrying it, and exchange the signature for a JWT. No account is needed — any wallet gets a session.
Terminal window curl -X POST "https://api.rail0.xyz/auth/nonces"# wallet signs the SIWE message with that nonce, then:curl -X POST "https://api.rail0.xyz/auth" -H "Content-Type: application/json" \-d '{"message": "…", "signature": "0x…"}'export RAIL0_TOKEN=eyJ… -
Create the payment. Amounts in requests are human decimals. Send an
Idempotency-Keyper order so a retried request can never create a second payment.Terminal window curl -X POST "https://api.rail0.xyz/payments" \-H "Authorization: Bearer $RAIL0_TOKEN" \-H "Idempotency-Key: ord_1042-create" \-H "Content-Type: application/json" \-d '{"chain_id": 84532,"mode": "authorize","amount": "10.50","token": "0x036CbD53842c5426634e7929541eC2318f3dCF7e","payer": "0x8ba1f109551bD432803012645Ac136ddd64DBA72","payee": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"}'The payment comes back
unsigned.GET /payments/{id}returns itssigning_payload. -
The buyer signs.
signing_payloadis EIP-712 typed data for the token’s EIP-3009ReceiveWithAuthorization. The buyer’s wallet signs it off-chain (eth_signTypedData_v4) — no approval, no gas — and you store the signature:Terminal window curl -X PUT "https://api.rail0.xyz/payments/$PAYMENT_ID/sign" \-H "Authorization: Bearer $RAIL0_TOKEN" -H "Content-Type: application/json" \-d '{"signature": "0x…"}'The payment is now
signed. -
The merchant authorizes. Signed in as the merchant (payee), prepare the
authorizetransaction, sign it with the merchant wallet, and submit it. The merchant pays the gas.Terminal window curl -X POST "https://api.rail0.xyz/payments/$PAYMENT_ID/authorize/prepare" \-H "Authorization: Bearer $RAIL0_TOKEN"# sign the returned unsigned transaction with the payee wallet, then:curl -X POST "https://api.rail0.xyz/payments/$PAYMENT_ID/authorize" \-H "Authorization: Bearer $RAIL0_TOKEN" -H "Content-Type: application/json" \-d '{"signed_transaction": "0x02f8…"}'The gateway answers
202and broadcasts. When the chain confirms, the payment moves toauthorizedand apayments.authorizedwebhook fires. -
Capture. Same loop with
captureand an amount — up to the authorized total, in one go or in parts, any time beforeauthorization_expiry:Terminal window curl -X POST "https://api.rail0.xyz/payments/$PAYMENT_ID/capture/prepare" \-H "Authorization: Bearer $RAIL0_TOKEN" \-H "Idempotency-Key: ord_1042-capture-1" -H "Content-Type: application/json" \-d '{"amount": "10.50"}'Sign, submit to
POST /payments/{id}/capture, and the escrow pays out to the merchant in full — rail0 takes no fee.
Wallets that broadcast themselves
Section titled “Wallets that broadcast themselves”Browser wallets such as MetaMask sign and broadcast in one step, so there is no raw signed transaction to hand over. Broadcast the prepared transaction from the wallet, then report the hash instead of submitting:
curl -X POST "https://api.rail0.xyz/payments/$PAYMENT_ID/capture/submitted" \ -H "Authorization: Bearer $RAIL0_TOKEN" -H "Content-Type: application/json" \ -d '{"transaction_hash": "0x4e3a…1bdd"}'const hash = await walletClient.sendTransaction(prepared); // viem, or any EIP-1193 walletawait fetch(`https://api.rail0.xyz/payments/${paymentId}/capture/submitted`, { method: "POST", headers: { Authorization: `Bearer ${process.env.RAIL0_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ transaction_hash: hash }),});Confirmation then proceeds exactly as for a gateway broadcast.