Skip to main content

Overview

Merchants sell stablecoins (USDT/USDC) for fiat through a draft → quote → submit wizard, or the legacy one-shot POST /api/v1/auth/otc/withdrawals (creates pending immediately). Ops (admin or otc_officer) approve, execute on-chain debit to treasury, then pay fiat (bank manual or Mobile Money B2C). Status flow: draftpricedpendingapprovedprocessingcompleted (or rejected).

Pricing (OTC-Scoped)

Create admin pricing_profiles and fx_rate_quotes with flow_type: b2b and otc_type: withdrawal on both rows. Use POST /api/v1/auth/pricing/resolve-otc with amount_in_type: crypto (merchant sells crypto):
Copy pricing_profile_id and fx_rate_quote_id into PATCH /api/v1/auth/otc/withdrawals/{id}/quote, or use treasury-negotiated FX (below).

Treasury-Negotiated FX (Withdrawals)

Merchants sell crypto (USDT/USDC) for a treasury-agreed fiat payout. Same reference table as deposits: otc_negotiated_fx_references. Prerequisite: active b2b pricing_profiles row with otc_type: withdrawal matching asset, fiat, and network.

Treasury Flow

  1. POST /api/v1/auth/otc/negotiated-fx/references with otc_type: "withdrawal", merchant, agreed_transaction_date (today or tomorrow in the reference timezone), asset, fiat, optional timezone or source_country, amounts, and rates.
  2. Email notification (when EMAIL_ENABLED=true) CTA: OTC Withdrawals → Enter FX Reference.
  3. Share reference_code with the merchant.

Merchant Flow

  1. POST .../withdrawals/draft with method, fiat_currency, from_wallet_id, and payout fields (Mobile Money phone or bank details).
  2. PATCH .../withdrawals/{id}/quote with { "negotiated_fx_reference": "NFX-..." } only (reference_code alias accepted). Do not send standard pricing fields on this path.
  3. API debits gross crypto from the agreed terms, sets net fiat payout after fees, marks reference consumed, moves draft → priced.
  4. POST .../submit → officer approve → execute (on-chain debit) → Mobile Money B2C or manual bank payout.
For Kenya Mobile Money: method: mpesa, fiat_currency: KES, timezone Africa/Nairobi (or source_country: KEN). Net fiat payout must be ≤ 500,000 KES.

Mobile Money Limit

KES Mobile Money withdrawals are capped at 500,000 per request (draft, quote, submit, and legacy create).

Data Model

Wizard Endpoints

Ops queues: GET .../queue/pending, GET .../queue/approved. Approve POST .../approve, reject POST .../reject, execute POST .../execute (admin, otc_officer, or treasury). Bank fiat payout: POST .../record-bank-payout with bank_payout_reference after manual transfer. Maker-checker: same OTC_OFFICER_MAX_USD limit applies to approve/execute for otc_officer. ADMIN and treasury are unlimited.