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

  1. Retrieve any required payout code, such as a bank code or mobile money operator ID.

  2. Create an offramp transaction with the crypto amount, source token, payout currency, destination, and recipient details.

  3. Show the returned deposit address, expected crypto amount, and expiration time to the user.

  4. The user sends the exact expected amount of USDC or USDT on the selected chain.

  5. Honeycoin detects the on-chain payment, converts the funds, and initiates the fiat payout.

  6. 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

Link to Crypto Off-Ramp API

Request Body:

You need to provide the following details in the request body:

FieldTypeRequiredDescription
senderAmountNumberExact amount of crypto the user must send (e.g. 50)
senderCurrencyStringCrypto token the user sends (USDC or USDT)
receiverCurrencyStringFiat currency to pay out to the recipient (e.g. KES, NGN)
countryStringISO 3166-1 alpha-2 recipient country code (e.g. KE)
chainStringBlockchain the user sends funds on (e.g. BASE, MATIC)
destinationStringPayout destination: MoMo, Bank Account, Paybill, or Till
payoutMethodObjectRecipient payout details. Shape depends on destination
externalReferenceStringYour unique ID for reconciliation
refundAddressStringSame-chain address used for an automatic refund attempt if crypto was received and the off-ramp later fails. If omitted, Honeycoin uses the detected on-chain sender address

Destination Requirements

DestinationRequired payoutMethod FieldsNotes
MoMoaccountName, accountNumber, sometimes codecode is the mobile money operator ID when operator selection is required
Bank AccountaccountName, accountNumber, codecode is the bank code from the Get Banks API
PaybillaccountName, accountNumber, businessNumberKenya only
TillaccountName, accountNumberKenya 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 can cause the transaction to fail. Honeycoin will attempt an automatic refund when the deposit was detected, but amount mismatches can still require support-assisted recovery.

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"
}

Failed Off-Ramps and Automatic Refunds

An off-ramp can fail before or after crypto is received. Refund behavior depends on whether Honeycoin detected an on-chain deposit:

Failure statetxIdRefund behavior
No funds received before expiresAtAbsentThe off-ramp is marked failed. No refund is created because Honeycoin did not receive funds
Deposit amount does not match the expected amountPresentHoneycoin attempts an automatic on-chain refund
Fiat payout fails after the crypto deposit is detectedPresentHoneycoin attempts an automatic on-chain refund

For an eligible failed off-ramp, the refund runs asynchronously from the transaction-specific deposit wallet:

  1. Honeycoin sends the same source token on the same chain.
  2. The destination is the request's refundAddress when supplied. Otherwise, Honeycoin uses the from address detected on the original deposit.
  3. The automatic attempt uses the original senderAmount returned as expectedAmount.
  4. The smart-account refund path uses Honeycoin-sponsored network gas, so gas is not deducted from the attempted refund amount.
  5. After a successful on-chain refund, Honeycoin adds refundTransactionHash to the original off-ramp transaction. The off-ramp's chargeStatus remains failed because the fiat payout did not succeed.

Amount mismatches are not a safe recovery workflow. Because the automatic attempt uses the original expectedAmount, an underpayment may not leave enough tokens in the deposit wallet and an overpayment may leave excess funds. Always instruct users to send the exact amount. Contact Honeycoin support if the received amount differed from expectedAmount.

Track Refund Completion

The transaction_updated webhook with status: "failed" confirms that the off-ramp payout failed. It does not confirm that the on-chain refund succeeded, and it may arrive before the refund is submitted.

Use the Get Transaction endpoint with the original transactionId or externalReference:

GET /api/b2b/transactions/zQQvoWnhYED3yXYEo7y7

When the refund completes, the original off-ramp includes refundTransactionHash:

{
  "success": true,
  "data": {
    "transactionId": "zQQvoWnhYED3yXYEo7y7",
    "chargeStatus": "failed",
    "status": "FAILED",
    "type": "offramp",
    "senderAmount": 50,
    "senderCurrency": "USDC",
    "chain": "BASE",
    "txId": "0xc3acfde7f212fcb902045119ee41299249ca153fc5c2730b0e8adba749846b53",
    "refundTransactionHash": "0xa41f748e7e0cbd3f1831439c300511bee7a6fa4067a4f48a927df1d892c7f8aa"
  }
}

Treat the refund as confirmed only after refundTransactionHash is present and, when needed, verify that hash on the source chain. If an eligible failed off-ramp continues to have no refundTransactionHash, contact Honeycoin support with the transactionId, deposit txId, chain, token, and expected amount.

Automatic off-ramp refunds remain part of the original type: "offramp" transaction. They are different from merchant-initiated mobile money deposit refunds and do not emit the separate type: "refund" transaction webhook described in the Collections refund guide.