Cross-border Transfers
Create a transfer with saved identities, satisfy documents, then collect funds and track payout.
Create the request, resolve any supporting documents, and obtain a quote before initiating the customer's payment. A successful collection funds the transfer; only a successful beneficiary payout confirms delivery.
The production base URL is https://api-v2.honeycoin.app/api/b2b/cross-border/requests. Use a bearer token for that environment. Sandbox uses /api/sandbox/b2b/cross-border/requests on the same host.
China processing hours
The following processing schedule applies to production transfers to China:
| Payout method | Processing schedule |
|---|---|
| Alipay | Automated processing, 24 hours a day, five days a week (24 × 5). |
| Bank | Requires compliance review, available from 14:00 to 22:00 Beijing time (UTC+8) on working days. |
These are processing hours, not a guaranteed time to completion. Track the request status and confirm payout_successful before treating the beneficiary payment as complete.
1. Register sender and beneficiary
Create or reuse an active sender with POST /senders and an active beneficiary with POST /beneficiaries. Save their IDs separately. Both must belong to your account and environment.
2. Create the transfer request
Call POST / with senderId, beneficiaryId, purpose, subPurpose, amount, chargeCurrency, receiverCurrency, externalReference, requestType, and chargeDetails. Send IDs rather than an inline beneficiary or top-level payoutDetails.
| Setting | Supported values |
|---|---|
requestType | transfer |
chargeCurrency | KES, NGN, subject to account collection availability |
receiverCurrency | CNY |
| Collection methods (production and sandbox) | momo, bank, subject to account collection availability |
| Payout method | Saved beneficiary's bank or alipay; Alipay requires individual sender and beneficiary |
For mobile money, supply method, firstName, lastName, country, email, phoneNumber, and momoOperatorId, including for KES. Match country, currency, and operator to a route enabled for your account. Use a numeric calling code without +, spaces, or a local leading zero in chargeDetails.phoneNumber.
For bank collection, send method: bank, firstName, lastName, country, and email. Omit phoneNumber and momoOperatorId. For example, replace the request's chargeDetails with the following object and select a compatible source currency such as NGN for Nigeria, subject to your account's collection availability:
{
"method": "bank",
"firstName": "Amina",
"lastName": "Otieno",
"country": "NG",
"email": "[email protected]"
}Payment purpose and subpurpose
Both purpose and subPurpose are required for every new request in production and sandbox. Ask the customer to select both before creating the request. Use the display titles and exact codes in Purposes and Sub-purposes. Every purpose accepts the same sub-purpose list.
Example for a saved bank beneficiary:
{
"senderId": "sender_example_001",
"beneficiaryId": "beneficiary_example_001",
"purpose": "goods_payment",
"subPurpose": "electronics_and_home_appliances",
"amount": 1000,
"chargeCurrency": "KES",
"receiverCurrency": "CNY",
"externalReference": "cny-transfer-001",
"requestType": "transfer",
"chargeDetails": {
"method": "momo",
"firstName": "Amina",
"lastName": "Otieno",
"country": "KE",
"email": "[email protected]",
"phoneNumber": "254712345678",
"momoOperatorId": "mpesa"
}
}Store the returned data.id as the request ID. The response includes beneficiaryId, senderAmount, senderCurrency, receiverCurrency, requiredDocuments, documents, and status, with quote: null. Nonempty requirements start at documents_required; an explicit empty list starts at pending_charge. This call creates the request without obtaining a quote or collecting funds.
amount is in the source currency and is returned as senderAmount. Request creation uses chargeCurrency; quote creation uses senderCurrency for the same source currency.
3. Satisfy supporting documents
If the response requires documents, upload and explicitly submit them. Every required group must be satisfied; any one type within each anyOf is sufficient. On rejection, replace the affected file and resubmit. Wait for pending_charge before collection.
4. Obtain a quote and ask the customer to confirm
Once the request is pending_charge, call Create a Cross-border Quote: POST /quotes. Use the request's senderAmount, senderCurrency, and receiverCurrency:
{
"senderAmount": 1000,
"senderCurrency": "KES",
"receiverCurrency": "CNY"
}Save the returned data.id as quoteId. Display the returned source amount, receiver amount, and fee to the customer. receiverAmount already includes the fee deduction; do not calculate it from the displayed exchange rate or deduct the fee again.
Use expiresAt, a Unix timestamp in milliseconds, to determine whether the quote is still valid. Obtain a fresh quote if it expires before charge initiation and ask the customer to confirm the new amounts. See Cross-border Quotes for fields, expiry, and retry rules.
5. Initiate collection with the confirmed quote
Call POST /charges/{requestId} with a JSON body containing the confirmed quoteId. The request must be pending_charge, or an eligible charge_failed retry. Document states block charging. The quote must belong to the same account and environment, be unexpired and unused, and match the request's amount and currencies.
curl --request POST 'https://api-v2.honeycoin.app/api/b2b/cross-border/requests/charges/REQUEST_ID' \
--header 'Authorization: Bearer YOUR_BEARER_TOKEN' \
--header 'Content-Type: application/json' \
--data '{"quoteId":"quote_example_001"}'The response returns data.transactionId. Save it against the request. The used quote is saved on the request as data.quote. Initiation is not payment success; follow the payment prompts and track the transaction. Quotes are single-use. Before retrying a failed or uncertain call, reconcile the request, its transactions, and the quote; obtain a fresh quote for an eligible new attempt. Do not charge again if there is a pending or successful deposit.
6. Track collection and beneficiary payout
Use GET /transactions/{transactionId} for the charge's chargeStatus. A successful crossborder-deposit confirms collection only. Use GET /{requestId} or crossborder_updated for the overall transfer.
After collection, HoneyCoin processes the production payout; there is no customer payout-initiation endpoint. Sandbox automatically initiates a simulated payout and closes the request after payout success. Track payout_successful and the payout transaction for confirmation; closed alone is not proof of delivery.
See status, webhooks and retries and the sandbox scenarios.
Updated 5 days ago
