On-Ramp: Fiat → Crypto
Step-by-step guide to integrating fiat-to-crypto on-ramp transactions.
The on-ramp flow lets your users pay with local fiat currency and receive cryptocurrency in their wallet. IvoryPay handles the payment collection, conversion, and crypto payout automatically.
How it works
1. Your server calls POST /v1/onramp with user and transaction details
2. IvoryPay returns a virtual bank account (or checkout URL) for the user to pay into
3. The user transfers fiat to the provided account
4. IvoryPay detects the payment → webhook: onramp.fiatPaymentReceived
5. IvoryPay converts fiat to crypto and sends it to the wallet address
6. Transaction completes → webhook: onramp.success (or onramp.failed)Mobile money support
For currencies other than NGN (e.g. UGX, KES), IvoryPay may return a mobile money checkout URL or USSD prompt instead of a virtual bank account. The user completes payment via their mobile money wallet. The flow and webhooks are identical — only the payment collection method differs.
Integration steps
Create the on-ramp transaction
Choose between two initiation methods:
POST /v1/onramp
You're creating a new customer and transaction in one call
POST /v1/onramp/initiate
The customer already exists in your IvoryPay account
Example: Initiate with customer creation
curl -X POST https://ramp-api.ivorypay.io/api/v1/onramp \
-H "Authorization: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Amina",
"lastName": "Okafor",
"email": "amina@example.com",
"phoneNumber": "+2348012345678",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD68",
"blockchain": "BSC_MAINNET",
"reference": "550e8400-e29b-41d4-a716-446655440000",
"token": "USDT",
"fiatCurrency": "NGN",
"fiatAmount": 50000
}'Response
The endpoint returns HTTP 201 with the standard envelope.
{
"success": true,
"message": "Query successful",
"data": {
"firstName": "Amina",
"lastName": "Okafor",
"email": "amina@example.com",
"refCode": "abc123def4",
"reference": "550e8400-e29b-41d4-a716-446655440000",
"transferDetails": {
"accountName": "IvoryPay - Amina Okafor",
"accountNumber": "1234567890",
"bank": "Providus Bank",
"amountPayable": 50000,
"expiresAt": "2026-03-26T13:00:00.000Z",
"currency": "NGN",
"businessFee": 0,
"platformFee": 250,
"gasFee": 0.5,
"createdAt": "2026-03-26T12:00:00.000Z",
"checkoutUrl": "https://checkout.ivorypay.io/pay/abc123",
"cryptoAmount": 31.25
}
}
}Collect payment from your user
You have two options:
Redirect to checkout — Send the user to
checkoutUrlwhere IvoryPay handles the payment UI (works for both bank transfers and mobile money)Display bank details — Show the
accountNumber,bank, andamountPayablein your own UI for a bank transfer
The payment session expires at expiresAt. If the user doesn't pay before this time, the transaction expires and a new one must be created.
Listen for webhooks
Set up a webhook endpoint to receive real-time updates:
onramp.fiatPaymentReceived
User's fiat payment has been detected and confirmed
onramp.cryptoPayoutProcessing
Crypto is being sent to the user's wallet
onramp.success
Crypto has been delivered — transaction complete
onramp.failed
Something went wrong — check failedReason
Example webhook payload: onramp.success
{
"event": "onramp.success",
"data": {
"reference": "550e8400-e29b-41d4-a716-446655440000",
"amount": 31.25,
"platformFeeInCrypto": 0.15,
"businessFeeInCrypto": 0,
"gasFee": 0.5,
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD68",
"network": "BSC_MAINNET",
"token": "USDT",
"transactionHash": "0xabc123...",
"status": "SUCCESS",
"customer": {
"refCode": "abc123def4",
"userId": "9b1a3f8a-1c2d-4e5f-9012-abcdef123456",
"email": "amina@example.com",
"firstName": "Amina",
"lastName": "Okafor",
"context": "LIVE",
"createdAt": "2026-03-26T11:55:00.000Z",
"createdAtDateOnly": "2026-03-26"
}
}
}Specifying amounts and fees
You can specify the transaction amount in either fiat or crypto (not both):
User wants to spend 50,000 NGN
"fiatAmount": 50000
User wants to receive exactly 30 USDT
"cryptoAmount": 30
Business fees must be supplied in the same denomination as the amount. There are two valid patterns:
Fiat-denominated
fiatAmount
businessFeeInFiat
Crypto-denominated
cryptoAmount
businessFeeInCrypto
Mixing denominations (e.g. fiatAmount + businessFeeInCrypto, or cryptoAmount + businessFeeInFiat) is rejected by validation (FiatOrCryptoAmountConstraint).
You also cannot set both fiatAmount and cryptoAmount in the same request — the API will return a validation error.
Minimum amounts
Before initiating, check the minimum on-ramp amount for a currency:
Transaction lifecycle
Frequently asked questions
Last updated