Skip to main content

Integration guide — local fiat ↔ stablecoin

One corridor pattern for both directions. Only a few quote fields change per market and asset. Based on existing repo docs / OpenAPI — see KNOWN_GAPS.md for unknowns. Product: corridor quote → accept (POST /partner/orders/quotePOST /partner/orders/{quote_id}/acceptorder.settled).
Not this guide: ledger …/conversions (fiat↔fiat banking) — see customers/conversions.mdx.

Directions (same endpoints)

Always discover network_id with the same country + order_type as the quote (GET /partner/catalog or GET /partner/payment-methods). Provider lists differ by direction.

What changes between corridors

Field hints: GET /partner/order-requirements?country=…&currency=…&order_type=…. Playbooks: corridors/kenya.mdx, corridors/nigeria.mdx, corridors/uganda.mdx, corridors/overview.mdx.

Stablecoin assets (documented)

Same addresses on sandbox and production. Prefer Base USDC unless QA confirms USDT for your corridor (corridors/overview.mdx). KE OffRamp sandbox samples often use Polygon USDT.

How the rate is quoted and locked

Quote fields to show / store

TTL in seconds: not a fixed published constant — honor expires_at. TODO: KNOWN_GAPS.md. Indicative rates are not binding and not ledger-pair FX.

Worked examples (same shape)

OnRamp — local amount → USDC (KE momo sandbox)

Swap country / currency / phone / network_id for other OnRamp markets (e.g. NG bank + BVN — see corridors/nigeria.mdx). Success/failure sandbox identities: sandbox/success-failure.mdx.

OffRamp — USDT/USDC → local fiat (same accept path)

Checklist from orders/quote-and-accept.mdx: Sandbox: do not settle by sending testnet crypto — include Successful in retail customer.name (or institution business_name) so the crypto leg auto-credits (sandbox/success-failure.mdx, corridors/kenya.mdx).
Production: after accept, customer sends the exact crypto amount to payment_instructions.crypto_deposit before that address’s expires_at. Deposit address is per order — do not reuse.
Accept is identical for both directions:

What ID to store for reconciliation

Prefer order_id as the ledger journal external reference.

When funds are final

Backup poll: GET /partner/orders/{order_id}.

Fees and currency codes

  • Fiat: ISO 4217 from catalog (KES, NGN, …).
  • Crypto: USDC/USDT as in the asset table.
  • Fee fields on quote: amounts.fees.service_fee_local, amounts.fees.service_fee_usd.
  • Static fee schedule / BPS: not in this repo — use per-quote fees. TODO: KNOWN_GAPS.md.

Stablecoin minor-unit ledgers

API examples return decimal crypto amounts (e.g. 5.95), not integer minor units. Integrator convention (your system):
Display local fiat from amount_fiat / user_pays (or non-binding indicative rates for marketing only). Never replace the locked quote rate for checkout confirmation. Credit/debit from the settled webhook amounts, not a re-fetched indicative rate.

Minimal settled handler sketch

Store the quote’s asset.currency / network with order_id at accept time — settled webhook examples emphasize fiat currency and amount_crypto, not always a full asset object (KNOWN_GAPS.md if you need a richer schema).
  • GET /partner/catalog · GET /partner/payment-methods · GET /partner/order-requirements
  • POST /partner/orders/quote · POST /partner/orders/{quote_id}/accept · GET /partner/orders/{order_id}
  • GET /partner/rates/indicative
  • webhooks.partnerEvent (outbound)
Agent path: agents.md.