Off-Ramp: Crypto → Fiat

Step-by-step guide to integrating crypto-to-fiat off-ramp transactions.

The off-ramp flow lets your users send cryptocurrency and receive fiat payouts to their bank account or mobile money wallet. IvoryPay handles the crypto collection, conversion, and payout.

How it works

1. Your server calls POST /v1/offramp with user, account, and transaction details
2. IvoryPay returns a crypto wallet address and amount for the user to send to
3. The user sends crypto to the provided address
4. IvoryPay detects the deposit → webhook: offramp.cryptoPaymentReceived
5. IvoryPay converts crypto to fiat and initiates the payout
6. Payout completes → webhook: offramp.success (or offramp.failed)

Mobile money support

For currencies other than NGN (e.g. UGX, KES, GHS), payouts are delivered via mobile money. Instead of a bank account, provide:

  • accountNumber — the recipient's mobile phone number (digits only, with country code)

  • bankCode — not required for mobile money payouts

Bank account resolution (Step 1 below) is not required for mobile money — you can skip directly to creating the transaction.

Integration steps

1

Resolve the user's bank account (bank payouts only)

For NGN bank payouts, verify the user's bank details before initiating:

curl -X POST https://ramp-api.ivorypay.io/api/v1/banks/account-resolution \
  -H "x-api-key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "accountNumber": "0123456789",
    "bankCode": "058",
    "currency": "NGN"
  }'

This returns the account holder's name, which you should confirm with your user before proceeding.

circle-info

For mobile money payouts (non-NGN currencies), skip this step — account resolution is not required.

2

Create the off-ramp transaction

Bank payout (NGN)

curl -X POST https://ramp-api.ivorypay.io/api/v1/offramp \
  -H "x-api-key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Chidi",
    "lastName": "Nwankwo",
    "email": "chidi@example.com",
    "phoneNumber": "+2348098765432",
    "accountNumber": "0123456789",
    "bankCode": "058",
    "blockchain": "BSC_MAINNET",
    "reference": "660e8400-e29b-41d4-a716-446655440001",
    "token": "USDT",
    "fiatCurrency": "NGN",
    "fiatAmount": 100000
  }'

Mobile money payout (UGX)

curl -X POST https://ramp-api.ivorypay.io/api/v1/offramp \
  -H "x-api-key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Chidi",
    "lastName": "Nwankwo",
    "email": "chidi@example.com",
    "phoneNumber": "+256772882926",
    "accountNumber": "256772882926",
    "blockchain": "STARKNET_MAINNET",
    "reference": "660e8410-f23c-41c4-a628-838625530891",
    "token": "USDT",
    "fiatCurrency": "UGX",
    "cryptoAmount": 1.5,
    "businessFeeInCrypto": 0.001
  }'

Response

{
  "statusCode": 200,
  "success": true,
  "message": "Off-ramp initiated successfully",
  "data": {
    "firstName": "Chidi",
    "lastName": "Nwankwo",
    "email": "chidi@example.com",
    "refCode": "xyz789abc0",
    "reference": "660e8400-e29b-41d4-a716-446655440001",
    "transferDetails": {
      "token": "USDT",
      "blockchain": "BSC_MAINNET",
      "amountPayable": 62.5,
      "businessFee": 0,
      "platformFee": 0.3,
      "walletAddress": "0xIvoryPayDepositAddress...",
      "createdAt": "2026-03-26T12:00:00.000Z",
      "fiatAmount": 100000,
      "expiresAt": "2026-03-26T13:00:00.000Z",
      "sessionId": "session_abc123",
      "duration": 3600
    }
  }
}
3

User sends crypto

Display the walletAddress, amountPayable (in crypto), and blockchain to your user. They send the specified amount of crypto to the wallet address.

circle-exclamation
4

Listen for webhooks

Event
When it fires

offramp.cryptoPaymentReceived

Crypto deposit detected on-chain

offramp.success

Fiat payout has been sent to the bank account or mobile money wallet

offramp.failed

Payout failed — check failedReason

offramp.declined

Transaction was declined — check reasonForDecline

5

Confirm crypto sent (optional)

If the user has sent crypto outside of your app (e.g., from an external wallet), you can manually confirm the payment:

curl -X POST https://ramp-api.ivorypay.io/api/v1/offramp/crypto-sent \
  -H "x-api-key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "660e8400-e29b-41d4-a716-446655440001",
    "settledCryptoAmount": 62.5,
    "transactionHash": "0xdef456..."
  }'

Specifying amounts and fees

Scenario
Fields to send

User wants to receive 100,000 NGN

"fiatAmount": 100000, "businessFeeInCrypto": 0.3

User wants to send exactly 1.5 USDT

"cryptoAmount": 1.5, "businessFeeInCrypto": 0.001

Transaction lifecycle

Frequently asked questions

chevron-rightWhat happens if the user sends the wrong cryptocurrency or token?hashtag

If the user sends an unsupported token or a different token than the one specified in the transaction (e.g., USDC instead of USDT), IvoryPay will not detect it as a valid payment. The transaction will eventually expire. You should clearly display the required token and blockchain to the user before they send funds. If this happens, contact IvoryPay support for assistance recovering the funds.

chevron-rightWhat happens if the off-ramp session expires?hashtag

If the user does not send crypto before the expiresAt time, the session expires and the deposit address is no longer monitored. You can use the Refresh Session endpoint to extend it. If the session has already fully expired, you will need to create a new off-ramp transaction.

chevron-rightWhat if the user sends a different amount of crypto than expected?hashtag

If the user sends more or less crypto than the amountPayable, IvoryPay will process based on the amount actually received on-chain. The fiat payout will be recalculated accordingly. Check the webhook payload for the final settled amounts.

chevron-rightWhy was my off-ramp transaction declined?hashtag

A offramp.declined event means IvoryPay could not process the payout. The reasonForDecline field in the webhook payload will explain why. Common reasons include invalid account details, compliance checks, or insufficient liquidity. Resolve the issue and create a new transaction.

chevron-rightDo I need to call the crypto-sent endpoint if the user pays from within my app?hashtag

No. The POST /v1/offramp/crypto-sent endpoint is only needed when the user sends crypto from an external wallet outside your application. If your app initiates the transfer directly, IvoryPay will detect the on-chain payment automatically.

Last updated