Offramps
Our off-ramp API allows you to programmatically convert stablecoins (USDC/USDT) on supported EVM chains into fiat currency, which can be paid out to a bank account or a mobile money wallet. The process involves creating an off-ramp transaction, sending the specified crypto amount to a unique address, and waiting for the fiat payout.
When To Use This
Use offramps when a user wants to convert supported stablecoins into fiat and receive the payout through mobile money, bank account, M-Pesa Paybill, or M-Pesa Till.
Happy Path
-
Retrieve any required payout code, such as a bank code or mobile money operator ID.
-
Create an offramp transaction with the crypto amount, source token, payout currency, destination, and recipient details.
-
Show the returned deposit address, expected crypto amount, and expiration time to the user.
-
The user sends the exact expected amount of USDC or USDT on the selected chain.
-
Honeycoin detects the on-chain payment, converts the funds, and initiates the fiat payout.
-
Reconcile the final result from webhooks or the transaction status endpoint.
Step 1: Get Supported Payout Methods
Before initiating a transaction, you need the correct code for the user's desired payout method.
For Bank Payouts: Use the GET /banks endpoint to fetch a list of supported banks and their corresponding codes.
For Mobile Money Payouts: Use the GET /mobile-money-operators endpoint to retrieve a list of supported mobile money providers and their codes.
You'll use the code from the response in the next step.
Step 2: Initiate the Off-Ramp Transaction
To start the process, make a POST request to the /minting/offramp endpoint. This generates a unique deposit address for the user.
Endpoint: POST /minting/offramp
Request Body:
You need to provide the following details in the request body:
| Field | Type | Required | Description |
|---|---|---|---|
| senderAmount | Number | ✅ | Exact amount of crypto the user must send (e.g. 50) |
| senderCurrency | String | ✅ | Crypto token the user sends (USDC or USDT) |
| receiverCurrency | String | ✅ | Fiat currency to pay out to the recipient (e.g. KES, NGN) |
| country | String | ✅ | ISO 3166-1 alpha-2 recipient country code (e.g. KE) |
| chain | String | ✅ | Blockchain the user sends funds on (e.g. BASE, MATIC) |
| destination | String | ✅ | Payout destination: MoMo, Bank Account, Paybill, or Till |
| payoutMethod | Object | ✅ | Recipient payout details. Shape depends on destination |
| externalReference | String | ✅ | Your unique ID for reconciliation |
| refundAddress | String | ❌ | Address to refund if the payout fails. If omitted, funds are returned to the detected sender address when possible |
Destination Requirements
| Destination | Required payoutMethod Fields | Notes |
|---|---|---|
MoMo | accountName, accountNumber, sometimes code | code is the mobile money operator ID when operator selection is required |
Bank Account | accountName, accountNumber, code | code is the bank code from the Get Banks API |
Paybill | accountName, accountNumber, businessNumber | Kenya only |
Till | accountName, accountNumber | Kenya only |
Example Request:
{
"senderAmount": 50,
"senderCurrency": "USDC",
"receiverCurrency": "KES",
"country": "KE",
"chain": "BASE",
"destination": "MoMo",
"externalReference": "offramp_user_001",
"payoutMethod": {
"accountName": "John Doe",
"accountNumber": "254712345678",
"code": "mpesa"
}
}Example Response:
{
"success": true,
"data": {
"expectedAmount": 50,
"transactionId": "zQQvoWnhYED3yXYEo7y7",
"addressId": "wallet_123",
"address": "0x318aa0c46120bb214f7da0d53183d0b1c9be7047",
"expiresAt": 1755517238922
}
}The API response includes a unique address and the exact expectedAmount of crypto to be sent. The address is valid until the returned expiresAt timestamp.
How Expiry Works
Each off-ramp request creates a transaction-specific deposit address and an on-chain listener for that address. The listener expiry is configured by Honeycoin and returned as expiresAt, a Unix timestamp in milliseconds.
Your user must send the exact expectedAmount before expiresAt. When the expiry time is reached, Honeycoin's deleteSubscription job removes the on-chain listener for the generated address. If no on-chain transaction has been received and the off-ramp is still pending, the off-ramp is marked as failed with the message Transaction failed because no funds were received before expiration.
After expiresAt, do not reuse the same address or ask the user to send funds to it. Create a new off-ramp request to get a fresh transaction, deposit address, and expiry time.
Step 3: Send Crypto to the Generated Address
Next, your user must send the exact expectedAmount of USDC or USDT on the specified chain to the deposit address provided in the previous step's response.
Important Notes:
Send the Exact Amount: Sending an amount different from the expectedAmount will cause the transaction to fail and a refund to be initiated back to the address funds were received from.
Time Limit: The transfer must be received before the returned expiresAt timestamp. The current production window is typically 1 hour, but integrations should rely on expiresAt instead of hardcoding a duration.
Supported Tokens: Only USDC and USDT are supported.
Step 4: Receive Payout and Confirmation
Our system monitors the generated address for the incoming transaction.
Transaction Confirmation: Once the crypto deposit is confirmed on the blockchain, we automatically initiate the fiat payout to the payoutMethod details you provided.
Webhook Notification: When the payout is successfully processed, we will send a POST request with the final transaction status to your specified webhookUrl.
Your system should be configured to listen for this webhook to confirm that the off-ramp process is complete.
{
"event": "transaction_updated",
"data": {
"transactionId": "zQQvoWnhYED3yXYEo7y7",
"status": "successful",
"type": "offramp",
"externalReference": "cmb8fd4dfl4w004fd",
"method": "momo",
"depositAddress": "0x318aa0c46120bb214f7da0d53183d0b1c9be7047",
"txId": "0xc3acfde7f212fcb902045119ee41299249ca153fc5c2730b0e8adba749846b53"
},
"timestamp": "2025-08-18T10:40:38.922Z"
}
