Skip to content
LogoLogo

Payout API

Send Malaysian bank payouts from a prefunded BLOX balance. This guide follows the production integration sequence from account setup to reconciliation.

Prerequisites

Before you integrate, prepare:

RequirementHow to get it
Payout API accessAsk BLOX to enable Payout on your sandbox account
API key and signer key idAsk BLOX for API access
Ed25519 key pairGenerate it yourself; register only the public key with BLOX
Public HTTPS webhook URLRegister a PAYOUT webhook under Devtools → Webhooks
Prefund balanceCreate a top-up under Payouts → Top up

Use the sandbox base URL while integrating:

https://api.sandbox.blox.my

Production uses https://api.blox.my. All read requests require blox-api-key; all write requests also require an HTTP message signature. Payout signatures accept a created= timestamp within ±300 seconds.

How it works

Loading diagram...

A payout moves through one of these paths:

INITIATED ─────► SETTLED ─────► RETURNED

    └──────────► REVERSED
StatusMeaning
INITIATEDAccepted, debited from prefund, and queued for the bank
SETTLEDThe bank confirmed delivery
REVERSEDRejected before settlement; netAmount and the fee return
RETURNEDReturned after settlement; netAmount returns and the fee is kept

Amounts are always in sen: integers in requests and strings in responses. Read fee and netAmount from the response instead of calculating them yourself.

Every payout requires a feeMode:

  • DEDUCT_FROM_AMOUNT: the beneficiary receives amount - fee.
  • CHARGE_TO_PREFUND: the beneficiary receives the full amount; the fee is debited separately.

In both cases, the prefund debit is netAmount + fee.

BLOX configures the fee per account as a percentage, a fixed amount, and one formula. MAX uses the larger charge (max(amount × rate, fixed)); SUM adds them. The standard arrangement is MAX at 1.00% with a RM1.50 floor, but confirm your configuration with BLOX. The percentage is rounded half-up to the nearest sen before the formula is applied; the response remains authoritative.

Step-by-step guide

Step 1: Configure your application

Generate an Ed25519 key pair and 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.pem

Set your sandbox credentials. The examples below assume you have copied the Node.js signBlox or Python sign_blox helper from Request Signing.

Keep the private key on your server. Never send it to BLOX or expose it in frontend code.

Step 2: Register your webhook and fund the prefund

In the dashboard:

  1. Register a public HTTPS endpoint of type PAYOUT under Devtools → Webhooks.
  2. Create a sandbox top-up under Payouts → Top up.
  3. Wait for payout.prefund_completed; sandbox top-ups normally complete in about 10 seconds.

Verify webhook signatures and process each eventId once. See Webhook Events for the payloads.

Step 3: Check the available balance

Read available before creating a payout. Do not subtract inFlight; it is already excluded.

See GET /v1/payout/prefund/balance for every response field.

Step 4: Choose the beneficiary

Fetch the active banks and use the returned bankCode. Then choose one payout path:

  • Send beneficiary inline for a new or one-off recipient.
  • Register the recipient first and send its beneficiaryId when you want to reuse it.

See Active Banks and Beneficiary endpoints for the complete fields.

Step 5: Create the payout

Generate and persist one UUID v4 Idempotency-Key before sending. Reuse the same key after a timeout or 5xx; a new key can create another transfer.

A successful create returns INITIATED. A 202 means the first request is still processing; retry with the same idempotency key. See POST /v1/payouts for validation, response fields, and retry rules.

Step 6: Handle the final outcome

Use payout.updated webhooks as the primary status signal. Store the payoutId, process webhook eventId values idempotently, and handle all three final statuses: SETTLED, REVERSED, and RETURNED.

Use the read endpoint only to reconcile a missed webhook:

Step 7 (optional): Pay a beneficiary from a deposit

Ask BLOX to enable paying a beneficiary from a deposit, then give a beneficiary its own on-chain address. Tokens sent to that address are converted and paid to that beneficiary — no POST /v1/payouts, and no prefund balance involved.

Set it up once per beneficiary:

  1. POST /v1/payouts/beneficiaries/{beneficiaryId}/address returns the address. A beneficiary has one address for its lifetime, so calling again returns the same one.
  2. POST /v1/payouts/beneficiaries/{beneficiaryId}/address/whitelist for each address you will send from.

The second step is not optional. A trigger address accepts nothing until you allow a sender. Deposits from any other address are refused with sender_not_allowed, and the tokens simply stay in your wallet.

Transfers made this way are reported on payout.deposit.updated and payout.withdrawal.updated, not payout.created or payout.updated, and they do not appear in GET /v1/payouts. One deposit produces at most one transfer.

If a deposit is refused for a reason you can fix, fix it and call POST /v1/payouts/deposits/{id}/redrive. If you sent tokens and heard nothing at all, POST /v1/payouts/deposits/check-missed with the transaction hash is the way back.

Before switching to production, test settlement, reversal, return, and pending outcomes using Sandbox Testing. Then change the base URL and credentials, fund the production prefund, confirm your fee and account limits with BLOX, and alert on RETURNED and payout.prefund_reversed.