Skip to content
LogoLogo

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:

RequirementHow to get it
Onramp API accessAsk BLOX to enable Onramp on your sandbox account
Access per checkout typeEach type is enabled separately — holding one never grants another
Checkout prefund balanceOnly if you use CHARGE_TO_PREFUND; fund it under Checkout → Top up prefund
API key and signer key idAsk BLOX for API access
Ed25519 key pairGenerate it yourself; register only the public key with BLOX
Destination addressUse an EVM or Solana address you control
Public HTTPS webhook URLRegister a CHECKOUT webhook under Devtools → Webhooks

Use the sandbox base URL while integrating:

https://api.sandbox.blox.my

Production 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.

typeUse it whenFee
BLOX_ACCOUNTThe customer has a Blox account and pays with their own MYRCNever charged; feeMode is rejected
FPX_HOSTEDYou want the customer to pick their bank on the BLOX pagefeeMode required
FPX_DIRECTYour interface already collects the buyer name and FPX bankfeeMode 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

Loading diagram...

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)
StatusMeaningFinal
CREATEDLink generated; the customer has not startedNo
PENDINGCustomer is in progress, or an FPX bill was createdNo
PROCESSINGPayment initiated; confirmation is pendingNo
COMPLETEDMYRC transferred to addressToYes
FAILEDPayment failed, or the link expired after 20 minutesYes
CANCELLEDCustomer cancelled the checkoutYes
REJECTEDPayment provider refused the paymentYes

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 1000100000000 sen (RM10–RM1,000,000).

Fees

typeFeefeeMode
BLOX_ACCOUNTAlways "0"; netAmount equals amountRejected — there is no MYR leg to charge against, and silently ignoring the field is how you end up believing a fee applies
FPX_HOSTEDCharged only when BLOX configured a checkout fee for the accountRequired
FPX_DIRECTCharged only when BLOX configured a checkout fee for the accountRequired

For both FPX types:

  • DEDUCT_FROM_AMOUNT: settlement is amount - fee.
  • CHARGE_TO_PREFUND: settlement is the full amount and 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.pem

Keep 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:

  1. Complete each applicable flow in Sandbox Testing.
  2. Confirm every create call sends type, and that nothing still calls POST /v1/checkout/direct or GET /v1/checkout/direct/banks.
  3. Change the base URL and credentials.
  4. Resolve the production tokenId again.
  5. Register and verify the production webhook.
  6. Confirm access for each checkout type you use, plus fees and limits, with BLOX.
  7. If you use CHARGE_TO_PREFUND, fund the production checkout prefund and alert on INSUFFICIENT_BALANCE — it fails link creation, not payment.
  8. Review Onramp Errors and alert on unresolved final outcomes.