Cross-border Transfers
Create a CNY transfer, review its quote, collect payment and track the beneficiary payout.
A cross-border transfer has two payment stages: collect the source currency, then pay the beneficiary in CNY. Create the request first, review its quote, and explicitly initiate its charge. Confirm payout_successful before treating the transfer as delivered.
Start with the step-by-step flow overview to see how sender registration, customer confirmation, collection and beneficiary payout fit together. This page provides the request fields and examples for implementing that flow.
Before you begin
Create an active cross-border sender and obtain the bearer token for your environment. The production base URL is https://api-v2.honeycoin.app/api/b2b/cross-border/requests.
| Setting | Supported flow |
|---|---|
requestType | transfer |
chargeCurrency | KES or NGN, subject to collection availability for your account |
receiverCurrency | CNY |
New production request's chargeDetails.method | momo |
| CNY beneficiary | bank or alipay, with beneficiary.payoutDetails.country: CN |
| Alipay sender | individual only |
The accepted currency list does not guarantee a collection method is available for every currency. Select a matching country, currency and mobile money operator enabled for your account. momoOperatorId is required on these requests, including KES. For new bank-charge test requests, follow the separate sandbox guide.
1. Create a request and quote
Call Create a Cross-border Request: POST {crossBorderBaseUrl}.
senderId, purpose, amount, chargeCurrency, receiverCurrency, externalReference, requestType, chargeDetails, and beneficiary are required. amount is a positive number in the source currency, not the amount the beneficiary receives in CNY.
chargeDetails requires method, firstName, lastName, country, email, phoneNumber, and momoOperatorId for mobile money. Use the country calling code followed by the subscriber number, without spaces or a local leading zero. The saved sender identity does not replace these collection fields.
Payment purpose
purpose | subPurpose |
|---|---|
salary | Optional; omit when not needed |
family_support | Optional; omit when not needed |
goods_payment | Required; clothes_bags_shoees, daily_supplies_and_cosmetics, or electronics_and_home_appliances |
service_payment | Required; interpretation_service |
Send these values exactly, including the spelling clothes_bags_shoees.
Beneficiary identity and payout details
Supply the recipient's identity and account together in the required beneficiary object. The beneficiary is included in each request; there is no separate beneficiary-creation call in this flow.
| Field | Requirement |
|---|---|
beneficiary.type | Required; individual or business, describing the recipient |
beneficiary.idType | Required; passport, national-id, or business-registration-number |
beneficiary.idCountry | Required; two-letter uppercase country that issued the recipient's identity or registration document |
beneficiary.idNumber | Required for an individual recipient, 2–200 characters; forbidden for a business |
beneficiary.registrationNumber | Required for a business recipient, 2–200 characters; forbidden for an individual |
beneficiary.payoutDetails | Required; contains method, country, and payoutMethod |
Place the account holder name in beneficiary.payoutDetails.payoutMethod.accountName. Use country: CN for the payout destination; idCountry describes the document's issuing country. Sender and beneficiary types identify different parties, so supply each party's own identity details.
For an existing integration, move the former top-level payoutDetails into beneficiary.payoutDetails and add the beneficiary identity fields. Sending only the former top-level object does not satisfy the required beneficiary field. Business senders also now use registrationNumber instead of idNumber.
Alipay recipient
Replace senderId with the individual sender ID you created. Supply the recipient's identity in beneficiary and their actual Alipay account identifier and account holder name in beneficiary.payoutDetails.payoutMethod. Use a unique externalReference for the request within your account.
{
"senderId": "sender_example_001",
"purpose": "family_support",
"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"
},
"beneficiary": {
"type": "individual",
"idType": "national-id",
"idCountry": "CN",
"idNumber": "EXAMPLE-CN-ID-001",
"payoutDetails": {
"method": "alipay",
"country": "CN",
"payoutMethod": {
"accountName": "Example Recipient",
"accountNumber": "[email protected]"
}
}
}
}Bank recipient
Fetch a bank code for China (CN) from the bank directory. Supply that code along with the beneficiary's bank account number and account holder name in beneficiary.payoutDetails.payoutMethod. This example pays a business beneficiary, so it includes registrationNumber and omits idNumber. Replace BANK_CODE_FROM_DIRECTORY below; it is not a real bank code.
{
"senderId": "sender_example_001",
"purpose": "goods_payment",
"amount": 1000,
"chargeCurrency": "KES",
"receiverCurrency": "CNY",
"externalReference": "cny-bank-transfer-001",
"requestType": "transfer",
"chargeDetails": {
"method": "momo",
"firstName": "Amina",
"lastName": "Otieno",
"country": "KE",
"email": "[email protected]",
"phoneNumber": "254712345678",
"momoOperatorId": "mpesa"
},
"subPurpose": "electronics_and_home_appliances",
"beneficiary": {
"type": "business",
"idType": "business-registration-number",
"idCountry": "CN",
"registrationNumber": "EXAMPLE-CN-REG-001",
"payoutDetails": {
"method": "bank",
"country": "CN",
"payoutMethod": {
"accountName": "Example Supplier",
"accountNumber": "1234567890",
"code": "BANK_CODE_FROM_DIRECTORY"
}
}
}
}Review the quote
A successful create response returns data.id and data.status: pending_charge, with the saved recipient information in data.beneficiary. Store this request ID separately from the sender ID and your external reference. No money is collected by the create call.
{
"success": true,
"data": {
"senderId": "sender_example_001",
"purpose": "family_support",
"receiverCurrency": "CNY",
"externalReference": "cny-transfer-001",
"requestType": "transfer",
"chargeDetails": {
"method": "momo",
"firstName": "Amina",
"lastName": "Otieno",
"country": "KE",
"email": "[email protected]",
"phoneNumber": "254712345678",
"momoOperatorId": "mpesa"
},
"id": "request_example_001",
"status": "pending_charge",
"subPurpose": null,
"quote": {
"senderAmount": 1000,
"senderCurrency": "KES",
"receiverAmount": 50,
"receiverCurrency": "CNY",
"exchangeRate": 0.05,
"expiresAt": null
},
"senderCurrency": "KES",
"createdAt": 1789113600000,
"beneficiary": {
"type": "individual",
"idType": "national-id",
"idCountry": "CN",
"idNumber": "EXAMPLE-CN-ID-001",
"payoutDetails": {
"method": "alipay",
"country": "CN",
"payoutMethod": {
"accountName": "Example Recipient",
"accountNumber": "[email protected]"
},
"currency": "CNY"
}
}
}
}The amounts and exchange rate above are illustrative. Display the actual returned quote.senderAmount, quote.senderCurrency, quote.receiverAmount, and quote.receiverCurrency for customer confirmation. Use quote.receiverAmount directly; multiplying by the displayed exchange rate may not reproduce the payable amount. quote.expiresAt is currently null; do not assume a fixed expiry window or promise an indefinite rate lock.
2. Initiate the collection
After the customer confirms the quote, call Initiate a Cross-border Charge. Send no body; the request already holds the amount and collection details.
curl --request POST \
--url 'https://api-v2.honeycoin.app/api/b2b/cross-border/requests/charges/REQUEST_ID' \
--header 'Authorization: Bearer YOUR_BEARER_TOKEN'{
"success": true,
"message": "Charge initiated successfully.",
"data": {
"transactionId": "charge_example_001"
}
}Save data.transactionId against the request ID. The charge uses the saved quote.senderAmount and quote.senderCurrency; you do not supply a new amount or external reference on this call. An accepted charge does not mean payment has succeeded. Follow the collection prompts and process transaction updates.
An existing bank-funded request may return virtualAccount, or status: PENDING with virtualAccount: null. Retrieve the charge transaction for the account details when they become available. This response behavior does not make bank available when creating new production requests.
3. Track collection and payout separately
Use GET {crossBorderBaseUrl}/transactions/{transactionId} to read the collection's data.chargeStatus. A successful crossborder-deposit confirms that the source payment succeeded.
Use GET {crossBorderBaseUrl}/{requestId} or the crossborder_updated webhook to track the overall request. After collection, HoneyCoin reviews and processes the CNY payout. There is no customer payout-initiation call in this flow. Wait for payout_successful before marking the beneficiary payment complete.
See Status, webhooks and retries for event examples, ID mapping, safe retries, pagination and closing unpaid requests.
Updated about 14 hours ago
