Payout API
Use the Payout API to send payouts to Malaysian bank accounts from your BLOX prefund balance. This guide gives the integration steps in sequence, from account configuration to reconciliation.
Prerequisites
Before you start the integration, get these items:
| Item | How to get it |
|---|---|
| Payout API access | Ask BLOX to enable Payout on your sandbox account. |
| API key and signer key id | Ask BLOX for API access. |
| Ed25519 key pair | Generate the key pair yourself. Register only the public key with BLOX. |
| Public HTTPS webhook URL | Register a PAYOUT webhook in Devtools → Webhooks. |
| Prefund balance | Create a top-up in Payouts → Top up. |
Use the sandbox base URL during the integration:
https://api.sandbox.blox.myThe production base URL is https://api.blox.my. All read requests must contain blox-api-key. All write requests must also contain an HTTP message signature. The created= timestamp of a payout signature must be in the range of ±300 seconds of the BLOX time.
How it works
A payout follows one of these status sequences:
INITIATED ─────► SETTLED ─────► RETURNED
│
└──────────► REVERSED| Status | Meaning |
|---|---|
INITIATED | BLOX accepted the payout and debited your prefund balance. The bank did not confirm the payout yet. |
SETTLED | The bank confirmed that the beneficiary received the money. |
REVERSED | The bank rejected the payout before settlement. netAmount and the fee return to your prefund balance. |
RETURNED | The bank returned the payout after settlement. netAmount returns to your prefund balance. BLOX keeps the fee. |
All amounts are in sen. Requests use integers, and responses use strings. Read fee and netAmount from the response. Do not calculate them yourself.
Each payout must have a feeMode:
DEDUCT_FROM_AMOUNT: The beneficiary receivesamount - fee.CHARGE_TO_PREFUND: The beneficiary receives the fullamount. BLOX debits the fee separately.
In the two modes, BLOX debits netAmount + fee from your prefund balance.
BLOX sets the fee for each account with three values: a percentage, a fixed amount, and a formula. MAX uses the larger of the two charges (max(amount × rate, fixed)). SUM adds the two charges. The standard fee is MAX at 1.00% with a minimum of RM1.50. Ask BLOX for the fee of your account.
BLOX rounds the percentage charge half-up to the nearest sen before it applies the formula. The fee in the response is the correct value.
Step-by-step guide
Step 1: Configure your application
Generate an Ed25519 key pair. Send only the public key to BLOX:
openssl genpkey -algorithm Ed25519 -out blox_private_key.pem
openssl pkey -in blox_private_key.pem -pubout -out blox_public_key.pemConfigure your sandbox credentials. The examples that follow use the Node.js signBlox or Python sign_blox helper. Copy the helper from Request Signing.
Keep the private key on your server. Do not send it to BLOX. Do not put it in frontend code.
Step 2: Register your webhook and fund the prefund
In the dashboard, do these steps:
- Register a public HTTPS endpoint of type
PAYOUTin Devtools → Webhooks. - Create a sandbox top-up in Payouts → Top up.
- Wait for
payout.prefund_completed. A sandbox top-up usually completes in about 10 seconds.
Verify the signature of each webhook event. Process each eventId one time only. For the payloads, refer to Webhook Events.
Step 3: Check the available balance
Read available before you create a payout. Do not subtract inFlight from available. BLOX already subtracted it.
For all response fields, refer to GET /v1/payout/prefund/balance.
Step 4: Choose the beneficiary
Get the list of active banks, and use the bankCode from the response. Then use one of these two methods:
- For a new beneficiary that you pay one time, send
beneficiaryinline. - For a beneficiary that you pay again, register the beneficiary first. Then send its
beneficiaryId.
For all fields, refer to Active Banks and Beneficiary endpoints.
Step 5: Create the payout
Generate one UUID v4 Idempotency-Key, and store it before you send the request. After a timeout or a 5xx response, send the request again with the same key.
Do not use a new key for a retry. A new key can create a second payout.
If BLOX creates the payout, the status is INITIATED. A 202 response tells you that BLOX did not complete the first request yet. Send the request again with the same idempotency key. For validation, response fields, and retry rules, refer to POST /v1/payouts.
Step 6: Handle the final outcome
Use the payout.updated webhook event as the primary source of the payout status. Do these steps:
- Store the
payoutId. - Process each webhook
eventIdone time only. - Process all three final statuses:
SETTLED,REVERSED, andRETURNED.
Use the read endpoint only to find the status after you did not receive a webhook event:
Step 7 (optional): Pay a beneficiary from a deposit
Ask BLOX to enable payouts from deposits. Then give a beneficiary its own on-chain address. When you send tokens to that address, BLOX converts the tokens and pays that beneficiary. You do not send POST /v1/payouts, and BLOX does not use your prefund balance.
For the endpoints, deposit records, and webhook events, refer to Onchain Trigger.
Do these steps one time for each beneficiary:
- Send
POST /v1/payouts/beneficiaries/{beneficiaryId}/address. The response contains the address. A beneficiary has only one address. If you send the request again, you get the same address. - For each address that you will send tokens from, send
POST /v1/payouts/beneficiaries/{beneficiaryId}/address/whitelist.
You must do step 2. A trigger address does not accept deposits until you allow a sender. BLOX refuses deposits from all other addresses with sender_not_allowed. The tokens of a refused deposit stay in your wallet.
BLOX reports these transfers with payout.deposit.updated and payout.withdrawal.updated. BLOX does not send payout.created or payout.updated for them, and GET /v1/payouts does not show them. One deposit causes a maximum of one transfer.
If BLOX refuses a deposit for a reason that you can correct, correct the problem. Then send POST /v1/payouts/deposits/{id}/redrive. If you sent tokens and did not get a webhook event, send POST /v1/payouts/deposits/check-missed with the transaction hash.
Before you change to production, do these steps:
- Test the settled, reversed, returned, and pending results. Refer to Sandbox Testing.
- Change the base URL and the credentials to the production values.
- Add funds to your production prefund balance.
- Ask BLOX to confirm your fee and your account limits.
- Set alerts for
RETURNEDandpayout.prefund_reversed.