Skip to content

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.

  1. 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"
  2. 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…
  3. Create the payment. Amounts in requests are human decimals. Send an Idempotency-Key per 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 its signing_payload.

  4. The buyer signs. signing_payload is EIP-712 typed data for the token’s EIP-3009 ReceiveWithAuthorization. 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.

  5. The merchant authorizes. Signed in as the merchant (payee), prepare the authorize transaction, 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 202 and broadcasts. When the chain confirms, the payment moves to authorized and a payments.authorized webhook fires.

  6. Capture. Same loop with capture and an amount — up to the authorized total, in one go or in parts, any time before authorization_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.

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:

Terminal window
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"}'

Confirmation then proceeds exactly as for a gateway broadcast.