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

# Off-ramp quickstart

> Convert USDC to an M-Pesa or bank payout in four API calls: quote the amount, create an order, send USDC on Base, and submit the transaction hash.

This guide pays 10 USDC to a Kenyan M-Pesa number, then covers what changes for NGN.

**Before you start, you need:**

* An API key with the `offramp` scope. See [getting access](/offramp/overview#getting-access).
* A wallet you control holding USDC on **Base**.

<Steps>
  <Step title="Quote the payout">
    Include the recipient to validate the account and get the registered name in the same call.

    ```bash theme={null}
    curl -X POST https://merchant.minisend.xyz/api/offramp/quote \
      -H "Authorization: Bearer ms_live_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{
        "amount": 10,
        "currency": "KES",
        "recipient": {
          "method": "MOBILE",
          "account_name": "Jane Wanjiku",
          "phone": "0712345678",
          "mobile_network": "Safaricom"
        }
      }'
    ```

    ```json theme={null}
    {
      "amount_usdc": 10,
      "currency": "KES",
      "rate": 129.45,
      "amount_local": 1294,
      "fee": 13,
      "recipient_amount": 1281,
      "recipient_name": "JANE WANJIKU",
      "expires_at": "2026-07-05T12:05:00.000Z"
    }
    ```

    `recipient_amount` is what lands in the recipient's account. For KES, GHS, and UGX the quote is indicative; the payout executes at the live rate when your deposit is processed.
  </Step>

  <Step title="Create the order">
    Pass an `Idempotency-Key` header so a network retry can never create two orders.

    ```bash theme={null}
    curl -X POST https://merchant.minisend.xyz/api/offramp/orders \
      -H "Authorization: Bearer ms_live_your_key_here" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: payout-8412" \
      -d '{
        "amount": 10,
        "currency": "KES",
        "refund_address": "0xYourWalletAddress0000000000000000000000",
        "reference": "payout-8412",
        "recipient": {
          "method": "MOBILE",
          "account_name": "Jane Wanjiku",
          "phone": "0712345678",
          "mobile_network": "Safaricom"
        }
      }'
    ```

    ```json theme={null}
    {
      "order_id": "9b2f6c1e-...",
      "status": "pending",
      "amount_usdc": 10,
      "total_deposit_usdc": 10,
      "currency": "KES",
      "rate": 129.45,
      "amount_local": 1294,
      "fee": 13,
      "recipient_amount": 1281,
      "deposit_address": "0x8005ee53e57ab11e11eaa4efe07ee3835dc02f98",
      "deposit_chain": "base",
      "expires_at": "2026-07-05T12:30:00.000Z",
      "instructions": "Send exactly 10 USDC (Base) to deposit_address from your own wallet, then submit the transaction hash via POST /api/offramp/orders/9b2f6c1e-.../deposit before expires_at."
    }
    ```

    Minisend validates the recipient's account before creating anything. An invalid account returns `422`.
  </Step>

  <Step title="Send USDC on Base">
    From your own wallet, transfer `total_deposit_usdc` USDC on **Base** to `deposit_address` before `expires_at` (30 minutes for KES, GHS, and UGX).

    <Warning>
      Send the exact amount, in USDC, on Base. A short transfer or a transfer on another chain cannot be verified in the next step.
    </Warning>
  </Step>

  <Step title="Submit the transaction hash">
    ```bash theme={null}
    curl -X POST https://merchant.minisend.xyz/api/offramp/orders/9b2f6c1e-.../deposit \
      -H "Authorization: Bearer ms_live_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{"transaction_hash": "0x55a572efe1720250e442f38741477a4fc3f7f152e5cd208cc52f8222a1c2a13b"}'
    ```

    A `200` with `"status": "settling"` means the payout is in flight. A `422` means the transfer could not be verified; check the amount, chain, and hash, then retry the same call.
  </Step>

  <Step title="Confirm completion">
    Poll the order, or listen for the webhook:

    ```bash theme={null}
    curl https://merchant.minisend.xyz/api/offramp/orders/9b2f6c1e-... \
      -H "Authorization: Bearer ms_live_your_key_here"
    ```

    When the payout lands, `status` becomes `completed`, `settlement_receipt` holds the payout receipt (an M-Pesa code for KES), and an `offramp.completed` webhook fires. See [off-ramp webhooks](/offramp/webhooks).
  </Step>
</Steps>

## NGN payouts

NGN orders pay out to bank accounts, and the deposit flow differs in two ways:

1. **The recipient shape** uses a bank `institution` code and `account_number`. See [recipients](/offramp/recipients).
2. **There is no hash submission.** The order returns a **single-use** `deposit_address`. Send `total_deposit_usdc` (the order amount plus any `sender_fee_usdc` and `transaction_fee_usdc`) before `expires_at`, about **5 minutes**, and the deposit is detected automatically.

```bash theme={null}
curl -X POST https://merchant.minisend.xyz/api/offramp/orders \
  -H "Authorization: Bearer ms_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: payout-8413" \
  -d '{
    "amount": 100,
    "currency": "NGN",
    "refund_address": "0xYourWalletAddress0000000000000000000000",
    "recipient": {
      "account_name": "Chidi Okafor",
      "institution": "GTBINGLA",
      "account_number": "0123456789"
    }
  }'
```

<Note>
  The 5-minute NGN window is short because the rate is locked at creation. Create the order only when you are ready to send, and automate the transfer. If the window lapses the order expires; create a new one. If a payout fails after you deposit, the funds are refunded to your `refund_address` automatically.
</Note>
