Onramp API
Accept MYR from a customer and settle MYRC to an EVM or Solana address. This guide follows the integration sequence from account setup to final checkout reconciliation.
Prerequisites
Before you integrate, prepare:
| Requirement | How to get it |
|---|---|
| Onramp API access | Ask BLOX to enable Onramp on your sandbox account |
| Access per checkout type | Each type is enabled separately — holding one never grants another |
| Checkout prefund balance | Only if you use CHARGE_TO_PREFUND; fund it under Checkout → Top up prefund |
| API key and signer key id | Ask BLOX for API access |
| Ed25519 key pair | Generate it yourself; register only the public key with BLOX |
| Destination address | Use an EVM or Solana address you control |
| Public HTTPS webhook URL | Register a CHECKOUT webhook under Devtools → Webhooks |
Use the sandbox base URL while integrating:
https://api.sandbox.blox.myProduction uses https://api.blox.my. Read requests require blox-api-key; create requests also require an HTTP message signature and a persisted Idempotency-Key.
Choose a checkout type
All three collect MYR from the customer and settle MYRC to addressTo. They differ in how the customer pays and who collects the bank details. Every one is hosted by BLOX at checkout.blox.my — the customer is always sent to a checkoutUrl.
type | Use it when | Fee |
|---|---|---|
BLOX_ACCOUNT | The customer has a Blox account and pays with their own MYRC | Never charged; feeMode is rejected |
FPX_HOSTED | You want the customer to pick their bank on the BLOX page | feeMode required |
FPX_DIRECT | Your interface already collects the buyer name and FPX bank | feeMode required |
Each type is a separate access grant. Being enabled for one does not enable another: the payer flows differ, and granting FPX_HOSTED because you hold FPX_DIRECT would put your customers in front of a page you never chose to use.
How it works
The browser redirect is for customer navigation, not payment confirmation. Fulfill only after a webhook or read response reports COMPLETED.
Lifecycle
CREATED ──► PENDING ──► PROCESSING ──► COMPLETED
│ │ │
│ │ ├──────────► FAILED
│ │ └──────────► REJECTED
├────────────┴───────────────────────► CANCELLED
└────────────────────────────────────► FAILED (expired after 20 min)| Status | Meaning | Final |
|---|---|---|
CREATED | Link generated; the customer has not started | No |
PENDING | Customer is in progress, or an FPX bill was created | No |
PROCESSING | Payment initiated; confirmation is pending | No |
COMPLETED | MYRC transferred to addressTo | Yes |
FAILED | Payment failed, or the link expired after 20 minutes | Yes |
CANCELLED | Customer cancelled the checkout | Yes |
REJECTED | Payment provider refused the payment | Yes |
checkout.updated is sent for COMPLETED and FAILED. Reconcile CANCELLED and REJECTED through GET /v1/checkout/{id}.
Amounts are in sen: integers in create requests and strings in checkout responses. The accepted range is 1000–100000000 sen (RM10–RM1,000,000).
Fees
type | Fee | feeMode |
|---|---|---|
BLOX_ACCOUNT | Always "0"; netAmount equals amount | Rejected — there is no MYR leg to charge against, and silently ignoring the field is how you end up believing a fee applies |
FPX_HOSTED | Charged only when BLOX configured a checkout fee for the account | Required |
FPX_DIRECT | Charged only when BLOX configured a checkout fee for the account | Required |
For both FPX types:
DEDUCT_FROM_AMOUNT: settlement isamount - fee.CHARGE_TO_PREFUND: settlement is the fullamountand the fee comes from your checkout prefund — a separate balance from your payout prefund, which cannot cover it.
Read fee and netAmount from the checkout instead of calculating settlement yourself.
Step-by-step guide
Step 1: Configure access
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.pemKeep the private key on your backend. Register a CHECKOUT webhook and copy the signing helper for your language from Request Signing.
Step 2: Resolve the settlement token
Call GET /v1/wallet/networks, choose the network matching the format of addressTo, and select its MYRC tokenId.
Do not hard-code the token ID across environments. Resolve it again when switching from sandbox to production.
Step 3: Create the checkout
Generate and persist one UUID v4 Idempotency-Key before sending. Put your order identifier in title, then choose one path:
Blox-account checkout
Send type: "BLOX_ACCOUNT" plus addressTo, amount, tokenId, redirectUrl, title, and optional description to POST /v1/checkout.
The customer signs in to their Blox account on the BLOX page and pays with their own MYRC. Do not send feeMode — it is rejected.
Hosted FPX checkout
Send type: "FPX_HOSTED" plus the same fields and feeMode. Do not send buyerName, bank, or bankType — the customer supplies those on the BLOX page, which is also when the FPX bill is created, because the bank is part of what the bill is.
This is the FPX flow to reach for unless you have a reason to collect bank details yourself: BLOX renders the bank picker, and you send the customer the returned checkoutUrl.
Direct FPX checkout
First fetch the active bank list from GET /v1/checkout/banks. Then send type: "FPX_DIRECT" plus buyerName, bank, bankType, and feeMode to POST /v1/checkout.
BLOX creates the FPX bill immediately, using the bank details your application collected.
Every type returns the same shape:
{
"checkoutId": "8f14e45f-ceea-467f-a830-5e3e3c7e2b8a",
"checkoutUrl": "https://checkout.blox.my/8f14e45f-ceea-467f-a830-5e3e3c7e2b8a?s=checkout_signature",
"expiresAt": "2026-02-03T15:20:00.000Z"
}The path inside checkoutUrl differs by type. Never construct it yourself — use the string BLOX returns, including its query string.
Step 4: Send the customer to checkout
Store checkoutId against your order and open the complete checkoutUrl, including its query string. Create the checkout only when the customer is ready; the link expires after 20 minutes.
Step 5: Handle the final outcome
Use Webhook Events as the primary status signal. Process each eventId once and make fulfillment idempotent on checkoutId.
Use GET /v1/checkout/{id} only for reconciliation or to confirm CANCELLED and REJECTED. Credit the customer against netAmount, not amount.
Production readiness
Before switching to production:
- Complete each applicable flow in Sandbox Testing.
- Confirm every create call sends
type, and that nothing still callsPOST /v1/checkout/directorGET /v1/checkout/direct/banks. - Change the base URL and credentials.
- Resolve the production
tokenIdagain. - Register and verify the production webhook.
- Confirm access for each checkout type you use, plus fees and limits, with BLOX.
- If you use
CHARGE_TO_PREFUND, fund the production checkout prefund and alert onINSUFFICIENT_BALANCE— it fails link creation, not payment. - Review Onramp Errors and alert on unresolved final outcomes.