> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crypto.westminister.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Create OTC Deposit (bank or mobile money)

> Creates a new OTC deposit request. **Bank** creates a pending record. **Mobile money** creates a record and initiates STK push.

**`fiat_currency`** must be an **active** row in **fiat_currencies**. Discover codes with **GET /api/v1/auth/fiat-currencies** before building forms.

When **`requested_network`** (and typically **`requested_asset`**) are sent, the API sets **`credited_to_address`** and **`to_wallet_id`** from the merchant default wallet on that network (master merchant ledger user or a **sub-vendor** wallet under that merchant). Optional body **`to_wallet_id`** on the B2B draft path selects a specific owned wallet.

**Non-USD fiat (e.g. KES).** Set **`fiat_currency`** to the wire currency and **`fiat_amount`** to the amount received. Link commercial FX (e.g. 130 KES = 1 USD) via **`fx_rate_quote_id`** and optional **`pricing_profile_id`**. After confirmation, **`/credit`** may pair **`fiat_currency`** with **`fiat_amount_equivalent`** using the same principal when attaching a pricing snapshot.




## OpenAPI

````yaml /openapi.yaml post /api/v1/auth/otc/deposits
openapi: 3.1.0
info:
  title: EmaalCoin API
  description: >
    EmaalCoin API reference. These APIs power cryptocurrency exchange between
    USDT and Fiat trading.


    - **On-Ramp**: Buy USDT with fiat (KES) via mobile money (STK Push for
    Kenya)

    - **Off-Ramp**: Sell USDT for KES with mobile money payout (Kenya)

    - **Ethereum, TRON, and Solana**: Multi-chain wallet support

    - **Merchant Offers**: Create and manage trading offers

    - **OTC Flows**: Bank and mobile money deposits/withdrawals for merchants

    - **Pricing engine**: FX quotes and fee profiles for quote-backed orders and
    transfers

    - **P2P/B2B transfers**: Internal and external stablecoin sends (USDT/USDC)
    with optional pricing snapshots

    - **Stablecoin swaps**: Treasury-backed two-leg USDT ↔ USDC conversion with
    BitGo confirmation, inventory reservation, and automatic refunds

    - **Multisig**: Collaborative and platform-managed multisig flows for
    high-value B2B
  version: 1.1.0
servers:
  - url: https://crypto.westminister.tech
    description: Production API (requires DNS for this hostname).
  - url: http://13.42.110.185:8080
    description: >-
      UAT on EC2 (direct). Use this base when building proof PDF URLs if the
      production hostname does not resolve.
security: []
tags:
  - name: Pricing
    description: >-
      Commercial pricing profiles and FX rate quotes (admin). Used with orders
      and transfers for auditable snapshots.
  - name: Transfers
    description: >-
      P2P and B2B stablecoin transfers (USDT/USDC). Optional fiat-equivalent
      pricing fields. B2B multisig behavior depends on wallet registration
      (synchronous completion vs proposal flow).
  - name: Stablecoin Swaps
    description: >-
      Treasury-backed USDT ↔ USDC conversions for BitGo provider-managed wallets
      on `tron`, `ethereum`, or `solana`. Collection confirms before treasury
      payout starts. Completion requires both legs and dual-asset balance
      synchronization.
  - name: Multisig
    description: >-
      Multisig wallet registration and TRON proposal/confirm flows
      (vendor/admin).
  - name: Escrow Wallets
    description: Escrow pool wallet metadata for off-ramp and operations.
  - name: Users
    description: >-
      User-related operations (registration, lookup, updates, counts, status
      changes)
  - name: Wallets-Users
    description: User wallet operations (create, manage, query balances)
  - name: Offers
    description: Offer operations (create, read, list, counts)
  - name: Orders
    description: Order operations (create, update, list, counts, stats)
  - name: Payment
    description: Payment initiation operations
  - name: Merchants
    description: >
      Merchant (vendor) onboarding and hierarchy operations.

      USE **GET /API/V1/AUTH/MERCHANTS** WITH **PAGE_ID** AND **PAGE_SIZE** TO
      PAGE MASTER VENDORS.

      USE **GET /API/V1/AUTH/MERCHANTS/{MERCHANT_ID}** FOR ONE MASTER VENDOR,
      **MERCHANT_WALLETS**, AND ALL **SUBVENDOR** ROWS WITH **WALLETS** (FULL
      TREE).
  - name: OTC Deposits
    description: >
      OTC fiat-to-crypto deposits (bank or mobile money). Rows live in
      **`otc_deposits`**. Merchants create drafts or one-shot deposits.
      **admin**, **otc_officer**, or **treasury** list all deposits.

      B2B bank flow uses draft, quote, submit, proof, assign, claim, settlement,
      and audit. **`credited_to_address`** is the merchant on-chain wallet
      treasury credits (resolved from the default wallet for
      **`requested_network`** on the master merchant ledger user or any
      **sub-vendor** under that merchant).

      **`GET /api/v1/auth/otc/deposits`** backfills missing
      **`credited_to_address`**/**`to_wallet_id`** when a wallet can be
      resolved. One-shot **`POST /api/v1/auth/otc/deposits`** persists the
      payout address when **`requested_network`** is sent.

      Bank proof PDFs upload via `POST .../proof` and are viewed at **`GET
      /otc-deposits-proof/{deposit_id}/{file}.pdf`** (relative
      **`proof_url`**/**`file_url`** plus API base URL).

      Merchants may edit tab-1 draft fields with **`PATCH
      /api/v1/auth/otc/deposits/{id}/draft`** while **`status`** is **`draft`**.
  - name: OTC Withdrawals
    description: >-
      OTC crypto-to-fiat withdrawals (bank or mobile money). Merchants create.
      Admin approves and executes.
  - name: OTC Config
    description: OTC display config (bank details, mobile money paybill) for merchant UI
  - name: Fiat Currencies
    description: >
      Shared platform **fiat_currencies** catalog. **GET
      /api/v1/auth/fiat-currencies** lists active rows for UI pickers. **GET
      /api/v1/auth/fiat-currencies/admin** lists all rows including inactive
      (admin). Admin manages rows with **GET
      /api/v1/auth/fiat-currencies/{id}**, **POST
      /api/v1/auth/fiat-currencies**, **PATCH
      /api/v1/auth/fiat-currencies/{id}**, and **DELETE
      /api/v1/auth/fiat-currencies/{id}**. Use for OTC, pricing, orders,
      remittance, and backoffice — not under **`/otc/...`**. Prefer **PATCH**
      with **`is_active: false`** over delete when a currency is referenced.
  - name: Countries
    description: >-
      Shared platform **countries** catalog. Public **GET /api/v1/countries**
      lists active rows for pickers. Auth **GET /api/v1/auth/countries** returns
      the same active catalog. Admin manages rows with **GET
      /api/v1/auth/countries/admin**, **GET /api/v1/auth/countries/{id}**,
      **POST /api/v1/auth/countries**, **PATCH /api/v1/auth/countries/{id}**,
      and **DELETE /api/v1/auth/countries/{id}**.
  - name: Files
    description: Authenticated file uploads for OTC receipts and proof artifacts.
paths:
  /api/v1/auth/otc/deposits:
    post:
      tags:
        - OTC Deposits
      summary: Create OTC Deposit (bank or mobile money)
      description: >
        Creates a new OTC deposit request. **Bank** creates a pending record.
        **Mobile money** creates a record and initiates STK push.


        **`fiat_currency`** must be an **active** row in **fiat_currencies**.
        Discover codes with **GET /api/v1/auth/fiat-currencies** before building
        forms.


        When **`requested_network`** (and typically **`requested_asset`**) are
        sent, the API sets **`credited_to_address`** and **`to_wallet_id`** from
        the merchant default wallet on that network (master merchant ledger user
        or a **sub-vendor** wallet under that merchant). Optional body
        **`to_wallet_id`** on the B2B draft path selects a specific owned
        wallet.


        **Non-USD fiat (e.g. KES).** Set **`fiat_currency`** to the wire
        currency and **`fiat_amount`** to the amount received. Link commercial
        FX (e.g. 130 KES = 1 USD) via **`fx_rate_quote_id`** and optional
        **`pricing_profile_id`**. After confirmation, **`/credit`** may pair
        **`fiat_currency`** with **`fiat_amount_equivalent`** using the same
        principal when attaching a pricing snapshot.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOtcDepositRequest'
            examples:
              bank:
                summary: Bank Deposit
                value:
                  method: bank
                  fiat_amount: '10000.00'
                  fiat_currency: KES
                  payment_reference: TXN-12345
                  proof_url: https://example.com/proof.pdf
                  transfer_date: '2025-02-28'
                  sender_account_name: ACME LTD
                  sender_account_number_last4: '4321'
                  sender_bank_name: Example Bank
                  sender_bank_branch: Westlands
                  receipt_number: BANK-ADVICE-7788
                  receipt_uploaded_at: '2026-04-30T10:15:00Z'
                  proof_file_name: bank_advice_7788.pdf
                  proof_mime_type: application/pdf
                  proof_size_bytes: 245123
                  proof_file_sha256: >-
                    0f4be9e5b3f4ad3ea9c7b0f8b74625704e8255f7984b46c06aaf9dd4c6f86d48
                  requested_asset: USDT
                  requested_network: ethereum
                  requested_crypto_amount: '100.00000000'
                  pricing_profile_id: 11111111-2222-3333-4444-555555555555
                  fx_rate_quote_id: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
              bank_usd_large_usdc:
                summary: Bank USD Large Ticket (1M USDC Request)
                description: >
                  Merchant wires USD. Requests 1,000,000 USDC gross on Ethereum.
                  Desk applies fee on `/credit` (e.g. 30 bps → net 997,000
                  USDC).
                value:
                  method: bank
                  fiat_amount: '1000000.00'
                  fiat_currency: USD
                  payment_reference: PAYAZA-WIRE-2026-05-01-001
                  proof_url: >-
                    /otc-deposits-proof/d8f2f0d8-5b12-4f2b-8f3e-bde15c6e2d2a/8251c719-0941-46da-8792-5acce5e5efc8.pdf
                  transfer_date: '2026-05-01'
                  sender_account_name: Payaza Ltd
                  sender_account_number_last4: '4821'
                  sender_bank_name: Eco Bank
                  sender_bank_branch: Lagos Main
                  receipt_number: ADV-883921
                  receipt_uploaded_at: '2026-05-01T10:02:00Z'
                  proof_file_name: payaza-bank-advice.pdf
                  proof_mime_type: application/pdf
                  proof_size_bytes: 245123
                  proof_file_sha256: >-
                    0f4be9e5b3f4ad3ea9c7b0f8b74625704e8255f7984b46c06aaf9dd4c6f86d48
                  requested_asset: USDC
                  requested_network: ethereum
                  requested_crypto_amount: '1000000.00000000'
                  pricing_profile_id: 11111111-2222-3333-4444-555555555555
                  fx_rate_quote_id: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
              bank_kes_large_usdc:
                summary: Bank KES Large Ticket (130 KES = 1 USD, 1M USDC Gross)
                description: >
                  130,000,000 KES at 130 KES/USD ≈ 1,000,000 USD notional.
                  Merchant requests 1,000,000 USDC gross.

                  FX quote should record 130 KES per USD. Fee (e.g. 30 bps) is
                  applied on `/credit` net amount (997,000 USDC).
                value:
                  method: bank
                  fiat_amount: '130000000.00'
                  fiat_currency: KES
                  payment_reference: PAYAZA-KES-2026-05-01-001
                  proof_url: >-
                    /otc-deposits-proof/d8f2f0d8-5b12-4f2b-8f3e-bde15c6e2d2a/8251c719-0941-46da-8792-5acce5e5efc8.pdf
                  transfer_date: '2026-05-01'
                  sender_account_name: Payaza Ltd
                  sender_account_number_last4: '4821'
                  sender_bank_name: Example Bank Kenya
                  sender_bank_branch: Nairobi
                  receipt_number: ADV-KES-883921
                  receipt_uploaded_at: '2026-05-01T10:02:00Z'
                  proof_file_name: payaza-kes-advice.pdf
                  proof_mime_type: application/pdf
                  proof_size_bytes: 245123
                  proof_file_sha256: >-
                    0f4be9e5b3f4ad3ea9c7b0f8b74625704e8255f7984b46c06aaf9dd4c6f86d48
                  requested_asset: USDC
                  requested_network: ethereum
                  requested_crypto_amount: '1000000.00000000'
                  pricing_profile_id: 11111111-2222-3333-4444-555555555555
                  fx_rate_quote_id: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
              mpesa:
                summary: Mobile money deposit
                value:
                  method: mpesa
                  fiat_amount: '5000.00'
                  fiat_currency: KES
                  phone_number: '254712345678'
                  requested_asset: USDT
                  requested_network: tron
                  requested_crypto_amount: '38.16793893'
                  pricing_profile_id: 11111111-2222-3333-4444-555555555555
                  fx_rate_quote_id: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
      responses:
        '201':
          description: Deposit created (bank) or mobile money STK initiated
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/OtcDeposit'
                  - $ref: '#/components/schemas/OtcDepositMpesaResponse'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - bearerAuth: []
components:
  schemas:
    CreateOtcDepositRequest:
      type: object
      required:
        - method
        - fiat_amount
        - fiat_currency
      properties:
        method:
          type: string
          enum:
            - bank
            - mpesa
        fiat_amount:
          type: string
        fiat_currency:
          type: string
          description: >-
            Active three-letter ISO code in **fiat_currencies**. List via **GET
            /api/v1/auth/fiat-currencies**.
        payment_reference:
          type: string
          description: Bank wire reference. Globally unique (case-insensitive) when set.
        proof_url:
          type: string
        transfer_date:
          type: string
          description: YYYY-MM-DD for bank
        phone_number:
          type: string
          description: Required for mpesa
          e.g. 254712345678: null
        sender_account_name:
          type: string
          description: Sender account holder name from bank receipt
        sender_account_number_last4:
          type: string
          description: Last 4 digits of sender account number
        sender_bank_name:
          type: string
        sender_bank_branch:
          type: string
        receipt_number:
          type: string
          description: Bank advice/receipt number
        receipt_uploaded_at:
          type: string
          format: date-time
        proof_file_name:
          type: string
        proof_mime_type:
          type: string
          example: application/pdf
        proof_size_bytes:
          type: integer
          format: int64
        proof_file_sha256:
          type: string
        requested_asset:
          type: string
          enum:
            - USDT
            - USDC
        requested_network:
          type: string
          enum:
            - tron
            - ethereum
          description: >-
            When set on create, the API persists **credited_to_address** and
            **to_wallet_id** from the merchant default wallet on this network.
        requested_crypto_amount:
          type: string
        pricing_profile_id:
          type: string
          format: uuid
        fx_rate_quote_id:
          type: string
          format: uuid
    OtcDeposit:
      type: object
      properties:
        id:
          type: string
          format: uuid
        merchant_id:
          type: string
          format: uuid
        method:
          type: string
          enum:
            - bank
            - mpesa
        fiat_amount:
          type: string
        fiat_currency:
          type: string
        status:
          type: string
          enum:
            - draft
            - priced
            - pending
            - confirmed
            - rejected
            - credited
        payment_reference:
          type: string
          nullable: true
          description: Bank wire reference. Globally unique (case-insensitive) when set.
        proof_url:
          type: string
          nullable: true
          description: >-
            Relative path to the bank proof PDF. Prepend API base URL for `GET`
            in browsers (`GET /otc-deposits-proof/...`, no auth).
          example: >-
            /otc-deposits-proof/ce6e4186-16fe-4e0d-a97d-7638f11c6063/8251c719-0941-46da-8792-5acce5e5efc8.pdf
        transfer_date:
          type: string
          format: date-time
          nullable: true
        merchant_request_id:
          type: string
          nullable: true
        checkout_request_id:
          type: string
          nullable: true
        mpesa_receipt_number:
          type: string
          nullable: true
        mpesa_phone_number:
          type: string
          nullable: true
        sender_account_name:
          type: string
          nullable: true
        sender_account_number_last4:
          type: string
          nullable: true
        sender_bank_name:
          type: string
          nullable: true
        sender_bank_branch:
          type: string
          nullable: true
        receipt_number:
          type: string
          nullable: true
        receipt_uploaded_at:
          type: string
          format: date-time
          nullable: true
        proof_file_name:
          type: string
          nullable: true
        proof_mime_type:
          type: string
          nullable: true
        proof_size_bytes:
          type: integer
          format: int64
          nullable: true
        proof_file_sha256:
          type: string
          nullable: true
        requested_asset:
          type: string
          nullable: true
        requested_network:
          type: string
          nullable: true
        requested_crypto_amount:
          type: string
          nullable: true
        pricing_profile_id:
          type: string
          format: uuid
          nullable: true
        fx_rate_quote_id:
          type: string
          format: uuid
          nullable: true
        negotiated_fx_reference_id:
          type: string
          format: uuid
          nullable: true
        confirmed_at:
          type: string
          format: date-time
          nullable: true
        confirmed_by:
          type: string
          format: uuid
          nullable: true
        rejected_at:
          type: string
          format: date-time
          nullable: true
        rejected_by:
          type: string
          format: uuid
          nullable: true
        rejection_reason:
          type: string
          nullable: true
        internal_note:
          type: string
          nullable: true
        credited_at:
          type: string
          format: date-time
          nullable: true
        credited_by:
          type: string
          format: uuid
          nullable: true
        usdt_amount:
          type: string
          nullable: true
        credited_asset:
          type: string
          nullable: true
        credited_crypto_amount:
          type: string
          nullable: true
        credited_to_address:
          type: string
          nullable: true
          description: >-
            Merchant blockchain address to receive USDT/USDC on treasury credit.
            Resolved from the default wallet for **requested_network** on the
            master merchant ledger or a **sub-vendor** under that merchant. Set
            on draft, one-shot create (when **requested_network** is sent),
            quote refresh, and list backfill when missing.
          example: TJyF42dRr18tSiXV9Cf7JtYUtTbqAGZg94
        to_wallet_id:
          type: string
          format: uuid
          nullable: true
          description: Internal **wallets.id** paired with **credited_to_address**.
        payout_to_address:
          type: string
          nullable: true
          readOnly: true
          description: >-
            Same as **credited_to_address** when the API enriches the response
            (list/detail). Omitted when no wallet can be resolved.
        payout_to_wallet_id:
          type: string
          format: uuid
          nullable: true
          readOnly: true
          description: Same as **to_wallet_id** on enriched responses.
        payout_network:
          type: string
          nullable: true
          readOnly: true
          description: Network from the resolved payout wallet (**tron** or **ethereum**).
        payout_asset:
          type: string
          nullable: true
          readOnly: true
          description: Mirrors **requested_asset** when enrichment runs.
        transfer_id:
          type: string
          format: uuid
          nullable: true
        idempotency_key:
          type: string
          nullable: true
        submitted_at:
          type: string
          format: date-time
          nullable: true
        assigned_to_user_id:
          type: string
          format: uuid
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    OtcDepositMpesaResponse:
      type: object
      description: Response when mobile money STK is initiated
      properties:
        deposit:
          $ref: '#/components/schemas/OtcDeposit'
        merchant_request_id:
          type: string
        checkout_request_id:
          type: string
        message:
          type: string
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Error message
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: PASETO

````