Terminology (Avoid “Taking an Offer” Confusion)
The JSON fields keep historical names. Product language should be:
For off-ramp remittance, John selects a liquidity listing so the platform has a consistent conversion anchor. He is not “obtaining USDT” from that listing.
Creating an Order
POST /api/v1/auth/orders
offer_id— Required. Must reference an existing liquidity listing for the chosenvendor_id(liquidity provider).pricing_profile_idandfx_rate_quote_id— Required for commercial enforcement. Must matchorder_type, asset (USDT),fiat_currency, and network implied bywallet_address(Tron-styleT...vs Ethereum0x...) for pricing validation.fiat_currencymust be an active code from the shared catalog. LIST OPTIONS WITHGET /api/v1/auth/fiat-currencies(SAME CATALOG AS OTC, REMITTANCE, AND ADMIN TOOLING — NOT UNDER/otc/...).order_type-ON_RAMP_BUY(default) orOFF_RAMP_SELL.- Off-Ramp: provide
payout_phone. Escrow and network detection use the customer’s validatedwallet_address.
Recommended Mobile Sequence
For app clients, resolve commercial IDs first, then create the order:POST /api/v1/auth/pricing/resolvewithflow_type,network,asset,fiat_currency,amount_in, andamount_in_type(plus optionalvendor_id/offer_idto pin the preview to a specific liquidity listing).- Read
pricing_profile_idandfx_rate_quote_idfrom the response. POST /api/v1/auth/orderswith those IDs plus order amounts and routing fields.
Recommended Web Sequence
For web dashboards/backoffice tools, use an explicit quote-review-confirm pattern:POST /api/v1/auth/pricing/resolvewith full context (flow_type,network,asset,fiat_currency, amount, and selected liquidity listingoffer_id+ liquidity providervendor_id).- Render a review panel showing rates, spread, and total fees from the resolve response.
- On operator confirmation,
POST /api/v1/auth/orderswith the returnedpricing_profile_idandfx_rate_quote_id. - Continue with payment or escrow steps (order-type specific) and track status via order endpoints.
Web Complete Flow (All Operations)
Use this sequence for web portals that support customer checkout, operations, and treasury actions.1) User Setup (Email-First on Web)
- Register user:
POST /api/v1/users/register - Login:
POST /api/v1/users/login(or/login-pinif enabled) - Load user wallets:
GET /api/v1/auth/wallets/user - Optionally set preferred default network wallet:
PUT /api/v1/auth/wallet/id/{wallet_id}/set-default-network
wallet_errors is returned. Set default wallet/network to avoid forcing from_wallet_id on every transfer path.
1.1 Merchant Registration (Web BackOffice)
Use merchant onboarding endpoints for treasury and OTC operations:- Create master merchant:
POST /api/v1/auth/merchants - Create sub-merchant under a master:
POST /api/v1/auth/merchants/{merchant_id}/sub-merchants - Create master + branches in one request:
POST /api/v1/auth/merchants/with-sub-merchants - List master merchants only (paginated):
GET /api/v1/auth/merchantswithpage_idandpage_size - View one master, its wallets, and every sub-merchant with wallets:
GET /api/v1/auth/merchants/{merchant_id}(merchant_idmust be the master vendor user id, not a sub-merchant id) - Update merchant or sub-merchant profile/status:
PATCH /api/v1/auth/merchants/{merchant_id}
id as vendor_id in offer/order/transfer contexts.
1.2 Merchant Operations Surface (Web)
After onboarding, merchant operators typically use:- Offer management:
POST /api/v1/auth/offersGET /api/v1/auth/offers/vendor/{vendor_id}GET /api/v1/auth/offers/active, search and count variantsPUT /api/v1/auth/customer/offers/{id}(edit own offer)DELETE /api/v1/auth/customer/offers/{id}(delete/cancel own offer)
- Order operations:
GET /api/v1/auth/orders/vendor/{vendor_id}GET /api/v1/auth/orders/recent/vendor/{vendor_id}GET /api/v1/auth/orders/stats/vendor/{vendor_id}
- Wallet operations (if extra wallets are required beyond defaults):
POST /api/v1/auth/walletsGET /api/v1/auth/wallets/userGET /api/v1/auth/wallets/user-networkPUT /api/v1/auth/wallet/id/{id}/set-default-networkPUT /api/v1/auth/wallet/id/{id}/active-status
- Liquidity and reconciliation:
GET /api/v1/auth/vendors/{vendor_id}/liquidityGET /api/v1/auth/transactions/completed
- Customer history surface:
GET /api/v1/auth/customer/transactions(returns only authenticated customer’s created offers + orders)
- OTC queues:
- deposits:
POST /api/v1/auth/otc/depositsGET /api/v1/auth/otc/deposits/{id}POST /api/v1/auth/otc/deposits/{id}/confirmPOST /api/v1/auth/otc/deposits/{id}/rejectPOST /api/v1/auth/otc/deposits/{id}/creditGET /api/v1/auth/otc/deposits/queue/pending-bankGET /api/v1/auth/otc/deposits/queue/ready-to-credit
- withdrawals:
POST /api/v1/auth/otc/withdrawalsGET /api/v1/auth/otc/withdrawals/{id}POST /api/v1/auth/otc/withdrawals/{id}/approvePOST /api/v1/auth/otc/withdrawals/{id}/rejectPOST /api/v1/auth/otc/withdrawals/{id}/executeGET /api/v1/auth/otc/withdrawals/queue/pendingGET /api/v1/auth/otc/withdrawals/queue/approved
- deposits:
2) on-Ramp (Fiat -> USDT, C2B)
- List offers:
GET /api/v1/auth/offers/active?page_id=1&page_size=20 - Resolve pricing (flow=
onramp):POST /api/v1/auth/pricing/resolve - Create order with resolved IDs:
POST /api/v1/auth/orders(order_type=ON_RAMP_BUY) - Trigger STK push:
POST /api/v1/auth/orders/payment - Poll status:
GET /api/v1/auth/order/id/{order_id}
pending -> processing -> completed (or payment_failed/failed).
3) Off-Ramp (USDT -> Fiat, B2C)
- Resolve pricing (flow=
offramp):POST /api/v1/auth/pricing/resolve(optionally passvendor_id+offer_idto align with the chosen liquidity listing) - Create order with payout phone:
POST /api/v1/auth/orders(order_type=OFF_RAMP_SELL) - Read escrow fields from response (
escrow_wallet_address,escrow_network,escrow_reference) - Fund escrow:
- in-app wallet route:
POST /api/v1/auth/orders/{order_id}/send-payment, or - external chain transfer to escrow wallet
- in-app wallet route:
- Poll status:
GET /api/v1/auth/order/id/{order_id}
awaiting_crypto -> awaiting_confirmations -> crypto_received -> payout_pending -> completed (or failed).
Off-Ramp Remittance (Third-Party Fiat Payout)
UsePOST /api/v1/auth/remittance/off-ramp/orders when the customer sends crypto to escrow but mobile money (or bank) pays someone else. Same vendor_id (liquidity provider) and offer_id (liquidity listing) semantics. They anchor pricing and validation, not the beneficiary’s identity. The request uses beneficiary_fiat_amount plus pricing IDs to derive escrow crypto. See OpenAPI for the full body.
Use the shared countries catalog when collecting remittance country fields. GET /api/v1/countries returns active rows for public pickers. GET /api/v1/auth/countries returns the same active catalog for authenticated apps. Prefer ISO 3166-1 alpha-3 values like KEN and GBR for sender_country, beneficiary_country, source_country, and destination_country. The backend still accepts legacy alpha-2 input and normalizes it to alpha-3.
For custodial send-money with a corridor only, use POST /api/v1/auth/remittance/custodial/drafts, and POST /api/v1/auth/remittance/custodial/drafts/{id}/confirm. See Custodial Corridor Remittance.
Domestic vs Cross-Border Off-Ramp
When the fiat beneficiary is in a different country than the customer who sends crypto from their wallet, you are in a cross-border remittance scenario (different compliance and commercial treatment than a domestic payout where both sides share the same country). Identification: Identify domestic vs cross-border by comparing customer (KYC) country tobeneficiary_country on the order (use ISO 3166-1 alpha-3 codes, e.g. KEN, GBR). If they match, treat the flow as domestic. However, if they differ, we treat it as cross-border remittance.
The API persists beneficiary_country on off-ramp orders. Your integration must supply a reliable customer country from KYC (or an explicit field collected at quote time) so this comparison is possible before you choose pricing profiles, FX quotes, or copy for the checkout screen.
4) P2P (Customer -> Customer USDT)
- Ensure sender has a funded wallet on selected network.
- Create transfer from web wallet/send UI using P2P transfer endpoint.
- Poll transfer detail/status endpoint until terminal state.
5) B2B (Merchant -> Merchant Settlement)
- Select merchant source wallet (typically treasury wallet policy).
- Submit transfer:
POST /api/v1/auth/transfers/b2b - If collaborative signing is required, follow proposal confirm endpoints.
- Poll transfer:
GET /api/v1/auth/transfers/{id}
5.1 Merchant Treasury Transfers (External/Internal)
Merchant web treasury screens may use:- Internal merchant transfer:
POST /api/v1/auth/transfers/b2b - External destination transfer:
POST /api/v1/auth/transfers/b2b/external - List and inspect transfer lifecycle:
GET /api/v1/auth/transfersGET /api/v1/auth/transfers/{id}GET /api/v1/auth/transfers/{id}/pricing
6) B2B2C (Merchant-Funded Customer Settlement)
Treat B2B2C as two linked rails:- B2B Funding Leg: merchant treasury movement to payout context.
- B2C Customer Leg: off-ramp style payout execution to beneficiary phone/account.
Off-Ramp vs on-Ramp
Payment Screen (in-App Wallet Send)
Fiat Payment (Mobile Money)
POST /api/v1/auth/orders/payment - STK push with order_id and phone_number.
Pricing on the Order
GET /api/v1/auth/order/id/{id}/pricing returns the stored snapshot (rates, fees, net amounts) when present.
Callbacks (Server-to-Server)
POST /api/v1/payment/callback— mobile money. Configured for Daraja validation.POST /api/v1/mpesa/b2c/resultand.../queue- B2C payout callbacks.