HubZellHubZell Docs

Gateway API

Gateway API reference

The Gateway service is the reseller integration layer for top-ups, bill payments, refunds, and order lookups. All business endpoints use POST with JSON bodies that include client credentials.

Base URL: http://localhost:3105/api/v1

Authentication

Every request must include the X-Api-Key, X-Timestamp, and X-Signature HTTP headers. Credentials are validated on every call; the JSON body never carries them.

FieldTypeRequiredDescription
Content-TypestringYesMust be `application/json`
X-Api-KeystringYesShop API key issued by HubZell
X-TimestampintegerYesUnix timestamp in seconds; must be within ±5 minutes of server time
X-SignaturestringYesHex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
IP whitelist required. The caller IP must be registered in the shop's whitelist. An empty whitelist blocks all requests until IPs are configured in the reseller console.
const crypto = require('crypto');

const SECRET_KEY = process.env.GAMZ_SECRET_KEY;
const timestamp = Math.floor(Date.now() / 1000);

const method = 'POST';
const path = '/api/v1/balance';
const body = {
  // ...endpoint-specific fields
};
const rawBody = JSON.stringify(body);

const signature = crypto
  .createHmac('sha256', SECRET_KEY)
  .update(`${method}\n${path}\n${timestamp}\n${rawBody}`)
  .digest('hex');

const res = await fetch('{{BASE_URL}}/balance', {
  method,
  headers: {
    'content-type': 'application/json',
    'x-api-key': process.env.GAMZ_API_KEY,
    'x-timestamp': String(timestamp),
    'x-signature': signature,
  },
  body: rawBody,
});
const json = await res.json();

Console setup

Two things must be configured in the reseller console's Developer page before the Gateway API will accept your requests — a missing callback URL means order results never reach you, and an empty IP whitelist blocks every request outright.

1. Add your callback (webhook) URL

  1. Log into the Reseller Console (your shop login).
  2. Open the Developer tab in the sidebar.
  3. Find the Callback URL section.
    Callback URL field on the Developer page
  4. Paste your endpoint and click Save.

On the Developer page, paste your endpoint into the Callback URL field and click Save. HubZell posts order and refund results here as a signed JSON request — see Order result callback below for the exact payload and headers.

The URL must be publicly reachable (HTTPS strongly recommended — it's carrying live order/refund results over the open internet) and respond with HTTP 200 within a few seconds. Delivery is best-effort with no retry, so poll orders/status for anything you don't want to risk missing. Changes save immediately and apply to the next callback — no propagation delay.

Always verify X-OP-Webhook-Signature before trusting the payload — anyone can POST to a public URL. Minimal Node/Express example:

const crypto = require('crypto');

const SECRET_KEY = process.env.GAMZ_SECRET_KEY;

app.post('/gamz/callback', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.header('x-op-webhook-timestamp');
  const signature = req.header('x-op-webhook-signature');
  const rawBody = req.body.toString('utf8');

  const expected = 'sha256=' + crypto
    .createHmac('sha256', SECRET_KEY)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  if (signature !== expected) return res.sendStatus(401);

  const payload = JSON.parse(rawBody);
  handleOrderResult(payload.dest_ref, payload.status);

  res.sendStatus(200);
});

2. Whitelist your IP

  1. Log into the Reseller Console (your shop login).
  2. Open the Developer tab in the sidebar.
  3. Find the IP Whitelist section.
  4. Add your server's IP address — add as many as you need, one at a time.

On the same page, click "Add my current IP" to whitelist the address you're browsing from, or type a specific IP under IP Whitelist and click Add. This step is required, not optional — an empty whitelist doesn't mean open access, it means every request is rejected with 403 IP_NOT_WHITELISTED until at least one IP is added.

IPs are added and removed one at a time, IPv4 or IPv6 (no CIDR ranges). There's no limit on how many you can whitelist, and a change takes effect on the very next request — it's checked live, not cached. Remove an entry the same way, from the same list.

If the server placing orders doesn't have a static outbound IP (common behind consumer NAT, some cloud auto-scaling groups, or residential/mobile connections), it'll need re-whitelisting whenever that IP changes. For anything beyond testing, place your integration behind a static IP or a NAT gateway with a fixed egress address.

Response format

Successful responses are wrapped in a uniform envelope. HTTP status codes reflect errors (401, 403, 404, 400, etc.) with the same JSON shape.

Success
{
  "status": 0,
  "code": "SUCCESS",
  "message": "success",
  "data": { ... }
}
Error
{
  "status": 1,
  "code": "INVALID_SIGNATURE",
  "message": "Invalid credentials",
  "data": null
}

Error codes

The error envelope's code field is always one of the following.

CodeHTTP statusMeaning
MISSING_CREDENTIALS401One or more of the X-Api-Key, X-Timestamp, or X-Signature headers is missing from the request.
INVALID_TIMESTAMP_FORMAT401X-Timestamp isn't a valid unix timestamp in seconds.
EXPIRED_TIMESTAMP401X-Timestamp is more than 5 minutes off from the server's clock.
INVALID_SIGNATURE401X-Signature doesn't match the expected HMAC — check your secret_key and how the signature string is built.
IP_NOT_WHITELISTED403The caller's IP isn't in your shop's IP whitelist. Add it on the reseller console's Developer page (see Console setup above) — an empty whitelist blocks every IP, it doesn't allow all of them.
IP_BLOCKED403This IP has been blocked by HubZell directly, independent of your shop's whitelist — typically abusive or scanning traffic.
SHOP_DISABLED403Your shop account has been disabled. Contact HubZell support.
KYC_NOT_APPROVED403Your shop's KYC hasn't been approved yet — order placement is disabled until it is.
INSUFFICIENT_BALANCE400Your wallet's available balance can't cover this request.
CALLBACK_URL_REQUIRED400Your shop's callback URL isn't configured yet — set one on the Developer page before placing orders.
VALIDATION_ERROR400The request body failed validation — check the response's message field for which field(s) are wrong.
DUPLICATE_REQUEST409This request has already been processed.
MAINTENANCE_MODE503The Gateway is temporarily under maintenance — retry shortly.
NOT_FOUND404The requested route doesn't exist — check the endpoint path and method.
INTERNAL_ERROR500An unexpected server error occurred. If this persists, contact HubZell support.

balance

POST{{BASE_URL}}/balance

Get wallet balance

Returns the authenticated shop wallet balance (available and held funds).

Headers

Required on every call, in addition to the request body below.

FieldTypeRequiredDescription
Content-TypestringYesMust be `application/json`
X-Api-KeystringYesShop API key issued by HubZell
X-TimestampintegerYesUnix timestamp in seconds; must be within ±5 minutes of server time
X-SignaturestringYesHex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
Example headers
Content-Type: application/json
X-Api-Key: ak_b419becab1273243
X-Timestamp: 1783321239
X-Signature: a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9

Request body

Send as application/json via POST to the endpoint above.

Example request
{}

Response

Also set on the response, in addition to the fields below.

FieldTypeRequiredDescription
X-CurrencystringNoCurrency the response's amounts are denominated in (e.g. `THB`)
FieldTypeRequiredDescription
shopstringNoShop code
currencystringNoWallet currency (e.g. THB)
availablenumberNoSpendable balance
heldnumberNoFunds reserved for in-flight orders
Example response
{
  "status": 0,
  "code": "SUCCESS",
  "message": "success",
  "data": {
    "shop": "demo",
    "currency": "THB",
    "available": 50000,
    "held": 150
  }
}
POST{{BASE_URL}}/service

List provider services

Paginated catalog of enabled provider services (games, billers, top-up products). Requires a positive wallet balance — a shop with zero or negative available balance gets a 403 INSUFFICIENT_BALANCE instead of the catalog.

Headers

Required on every call, in addition to the request body below.

FieldTypeRequiredDescription
Content-TypestringYesMust be `application/json`
X-Api-KeystringYesShop API key issued by HubZell
X-TimestampintegerYesUnix timestamp in seconds; must be within ±5 minutes of server time
X-SignaturestringYesHex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
Example headers
Content-Type: application/json
X-Api-Key: ak_b419becab1273243
X-Timestamp: 1783321239
X-Signature: a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9

Request body

Send as application/json via POST to the endpoint above.

FieldTypeRequiredDescription
pageintegerNoPage number (default 1, minimum 1)
limitintegerNoItems per page (default 20, max 100)
categoryenumNoFilter by category: MTOPUP | CASHCARD | GTOPUP | BILLPAY
company_idstringNoFilter by company id, e.g. MLBB
Example request
{
  "category": "GTOPUP",
  "page": 1,
  "limit": 20
}

Response

Also set on the response, in addition to the fields below.

FieldTypeRequiredDescription
X-CurrencystringNoCurrency the response's amounts are denominated in (e.g. `THB`)
FieldTypeRequiredDescription
itemsarrayNoList of provider services
items[].categoryenumNoMTOPUP | CASHCARD | GTOPUP | BILLPAY
items[].company_idstringNoReseller-facing product code — pass this exact value as company_id on /orders/payment, /orders/refund, and /orders/check-id
items[].company_namestringNoDisplay name
items[].imagestring | nullNoAbsolute URL of the service artwork
items[].feenumberNoService fee
items[].minimum_amountnumber | nullNoMinimum top-up amount
items[].maximum_amountnumber | nullNoMaximum top-up amount
items[].refundablebooleanNoWhether refunds are supported
items[].servicearrayNoFixed-price plans, reseller price already marked up (empty for variable-amount services e.g. BILLPAY)
items[].service[].codestringNocatalog_service code — pass this exact value as service_id on /orders/payment to select this plan directly
items[].service[].pricenumberNoReseller price for this plan (matches pay_to_amount, if you use that legacy field instead of service_id)
items[].service[].namestringNoPlan label, e.g. "100 Diamonds"
meta.pageintegerNoCurrent page
meta.limitintegerNoPage size
meta.totalintegerNoTotal matching items
meta.total_pagesintegerNoTotal pages
Example response
{
  "status": 0,
  "code": "SUCCESS",
  "message": "success",
  "data": {
    "items": [
      {
        "category": "GTOPUP",
        "company_id": "HUBZELLFLA71B",
        "company_name": "Mobile Legends: Bang Bang",
        "image": "https://hubzell.example.com/images/mlbb.png",
        "fee": 0,
        "minimum_amount": 10,
        "maximum_amount": 10000,
        "refundable": false,
        "service": [
          {
            "code": "HUBZELL6AM05HI3O",
            "price": 6.18,
            "name": "100 Diamonds"
          }
        ]
      }
    ],
    "meta": {
      "page": 1,
      "limit": 20,
      "total": 1,
      "total_pages": 1
    }
  }
}

orders

POST{{BASE_URL}}/orders/payment

Place payment / top-up

Charges the shop wallet and submits a top-up or bill payment to the upstream provider.

Headers

Required on every call, in addition to the request body below.

FieldTypeRequiredDescription
Content-TypestringYesMust be `application/json`
X-Api-KeystringYesShop API key issued by HubZell
X-TimestampintegerYesUnix timestamp in seconds; must be within ±5 minutes of server time
X-SignaturestringYesHex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
Example headers
Content-Type: application/json
X-Api-Key: ak_b419becab1273243
X-Timestamp: 1783321239
X-Signature: a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9

Request body

Send as application/json via POST to the endpoint above.

FieldTypeRequiredDescription
categoryenumNoSame category returned by /service (uppercase). Preferred over type — supply exactly one.
typeenumNoLegacy alias for category (lowercase): billpay | mtopup | gtopup | cashcard
company_idstringNocatalog_product code (same value returned by /service). Preferred over pay_to_company — supply exactly one.
pay_to_companystringNoLegacy alias for company_id
service_idstringNocatalog_service code (same value returned by /service under items[].service[]) — selects the exact price tier directly, the server resolves the amount. Preferred over pay_to_amount — supply exactly one.
pay_to_amountnumberNoLegacy alias for service_id — matches a plan by reseller price instead. Supply exactly one of service_id / pay_to_amount.
user_idstringNoGame account id — alias for pay_to_ref1 when category is GTOPUP. Supply exactly one.
pay_to_ref1stringNoPhone number / bill account / game id. Required as-is for non-GTOPUP categories; legacy alias for user_id on GTOPUP.
server_idstringNoGame server/zone id — alias for pay_to_ref2 when category is GTOPUP
pay_to_ref2stringNoReference 2 — required by some billpay services, or legacy alias for server_id on GTOPUP
pay_to_ref3stringNoReference 3 — required by some billpay services
pay_to_barcode1stringNoBarcode number for barcode-based bill payments
dest_refstringYesYour unique order reference (alphanumeric, max 20 chars)
Example request
{
  "category": "GTOPUP",
  "company_id": "HUBZELLFLA71B",
  "service_id": "HUBZELL6AM05HI3O",
  "user_id": "312678959",
  "server_id": "3611",
  "dest_ref": "ORD20260708001"
}

Response

FieldTypeRequiredDescription
typestringNoResolved category, uppercase (echoes category if supplied, else type uppercased)
pay_to_companystringNoThe catalog_product code the order was placed against, echoed back
pay_to_ref1stringNoThe resolved target sent to the provider (user_id, or the exact string the provider's own format requires — see the ref format on /service if the provider rejects it)
dest_refstringNoYour order reference, echoed back
total_amountnumberNoAmount charged to your wallet — the catalog_service's reseller price (price_reseller), resolved server-side, never a client-supplied figure
balancestringNoYour HubZell wallet balance after this charge (auto-refunded and reflected here if the provider then rejects the order)
...objectNoRemaining fields are the provider's own raw response, spread as-is (field set varies; typically includes code, transaction_id, bill_id, queue_id)
Example response
{
  "status": 0,
  "code": "SUCCESS",
  "message": "success",
  "data": {
    "bill_id": "BILL0000123",
    "transaction_id": "987654321",
    "queue_id": "Q00045",
    "type": "GTOPUP",
    "pay_to_company": "HUBZELLFLA71B",
    "pay_to_ref1": "312678959 3611",
    "dest_ref": "ORD20260708001",
    "total_amount": 27.04,
    "balance": "49900.00"
  }
}
POST{{BASE_URL}}/orders/refund

Refund order

Requests a refund from the provider for a previously placed order. Wallet is credited on success.

Headers

Required on every call, in addition to the request body below.

FieldTypeRequiredDescription
Content-TypestringYesMust be `application/json`
X-Api-KeystringYesShop API key issued by HubZell
X-TimestampintegerYesUnix timestamp in seconds; must be within ±5 minutes of server time
X-SignaturestringYesHex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
Example headers
Content-Type: application/json
X-Api-Key: ak_b419becab1273243
X-Timestamp: 1783321239
X-Signature: a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9

Request body

Send as application/json via POST to the endpoint above.

FieldTypeRequiredDescription
categoryenumNoSame category returned by /service (uppercase). Informational only.
typeenumNoLegacy alias for category (informational only): billpay | mtopup | gtopup | cashcard
transaction_idstringYesProvider transaction id returned when the order was placed
dest_refstringYesOrder reference used when the order was placed (alphanumeric, max 20 chars)
company_idstringNocatalog_product code of the original order. Informational only — the real provider-side code is resolved from the stored order, not from this field.
pay_to_companystringNoLegacy alias for company_id (informational only)
service_idstringNocatalog_service code of the original order. Informational only.
new_msisdnstringNoNew phone number for services that transfer credit to a new number
Example request
{
  "category": "GTOPUP",
  "company_id": "HUBZELLFLA71B",
  "service_id": "HUBZELL6AM05HI3O",
  "transaction_id": "987654321",
  "dest_ref": "ORD20260708001"
}

Response

FieldTypeRequiredDescription
dest_refstringNoYour order reference, echoed back
balancestringNoYour HubZell wallet balance after this refund credit
resultstringNoProvider's raw refund response, e.g. "SUCCEED
Example response
{
  "status": 0,
  "code": "SUCCESS",
  "message": "success",
  "data": {
    "dest_ref": "ORD20260708001",
    "balance": "50000.00",
    "result": "SUCCEED|RID=1234"
  }
}
POST{{BASE_URL}}/orders/status

Get order status

Looks up a single order by your dest_ref reference.

Headers

Required on every call, in addition to the request body below.

FieldTypeRequiredDescription
Content-TypestringYesMust be `application/json`
X-Api-KeystringYesShop API key issued by HubZell
X-TimestampintegerYesUnix timestamp in seconds; must be within ±5 minutes of server time
X-SignaturestringYesHex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
Example headers
Content-Type: application/json
X-Api-Key: ak_b419becab1273243
X-Timestamp: 1783321239
X-Signature: a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9

Request body

Send as application/json via POST to the endpoint above.

FieldTypeRequiredDescription
dest_refstringYesOrder reference used when the order was placed
Example request
{
  "dest_ref": "ORD20260708001"
}

Response

FieldTypeRequiredDescription
dest_refstringNoYour order reference
categorystringNoMTOPUP
company_idstringNocatalog_product code the order was placed against
user_idstringNoPhone / account / game id used for the order
server_idstring | nullNoGame server/zone id, when the order carried one separately (null otherwise — some games require it pre-combined into user_id instead)
statusenumNoPENDING | SENT | SUCCESS | FAILED | REFUNDED | CANCELLED
amountnumberNoOrder amount
currencystringNoCurrency code
transaction_idstring | nullNoProvider transaction id
messagestring | nullNoResult message from provider
failure_reasonstring | nullNoFailure reason when status is FAILED
created_atstringNoISO 8601 creation timestamp
completed_atstring | nullNoISO 8601 completion timestamp
Example response
{
  "status": 0,
  "code": "SUCCESS",
  "message": "success",
  "data": {
    "dest_ref": "ORD20260708001",
    "category": "GTOPUP",
    "company_id": "HUBZELLFLA71B",
    "user_id": "312678959",
    "server_id": "3611",
    "status": "SUCCESS",
    "amount": 100,
    "currency": "THB",
    "transaction_id": "987654321",
    "message": null,
    "failure_reason": null,
    "created_at": "2026-07-08T02:30:00.000Z",
    "completed_at": "2026-07-08T02:30:05.000Z"
  }
}
POST{{BASE_URL}}/orders/history

Order history

Paginated list of orders for the authenticated shop with optional filters.

Headers

Required on every call, in addition to the request body below.

FieldTypeRequiredDescription
Content-TypestringYesMust be `application/json`
X-Api-KeystringYesShop API key issued by HubZell
X-TimestampintegerYesUnix timestamp in seconds; must be within ±5 minutes of server time
X-SignaturestringYesHex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
Example headers
Content-Type: application/json
X-Api-Key: ak_b419becab1273243
X-Timestamp: 1783321239
X-Signature: a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9

Request body

Send as application/json via POST to the endpoint above.

FieldTypeRequiredDescription
pageintegerNoPage number (default 1, minimum 1)
limitintegerNoItems per page (default 20, max 100)
statusenumNoFilter by status: PENDING | SENT | SUCCESS | FAILED | REFUNDED | CANCELLED
dest_refstringNoFilter by your order reference
date_fromstringNoOrders created at or after (ISO 8601 date)
date_tostringNoOrders created at or before (ISO 8601 date)
Example request
{
  "status": "SUCCESS",
  "page": 1,
  "limit": 20,
  "date_from": "2026-07-01",
  "date_to": "2026-07-31"
}

Response

FieldTypeRequiredDescription
itemsarrayNoOrder records (same shape as order status response)
metaobjectNoPagination metadata (page, limit, total, total_pages)
Example response
{
  "status": 0,
  "code": "SUCCESS",
  "message": "success",
  "data": {
    "items": [
      {
        "dest_ref": "ORD20260708001",
        "category": "GTOPUP",
        "company_id": "HUBZELLFLA71B",
        "user_id": "312678959",
        "server_id": "3611",
        "status": "SUCCESS",
        "amount": 100,
        "currency": "THB",
        "transaction_id": "987654321",
        "message": null,
        "failure_reason": null,
        "created_at": "2026-07-08T02:30:00.000Z",
        "completed_at": "2026-07-08T02:30:05.000Z"
      }
    ],
    "meta": {
      "page": 1,
      "limit": 20,
      "total": 1,
      "total_pages": 1
    }
  }
}
POST{{BASE_URL}}/orders/check-id

Check player ID

Validates a player/account id directly against the provider before placing an order — checks whether the account exists, without charging the wallet or creating an order record. Routed automatically to whichever provider owns company_id; returns 501 if that provider doesn't support id checking.

Headers

Required on every call, in addition to the request body below.

FieldTypeRequiredDescription
Content-TypestringYesMust be `application/json`
X-Api-KeystringYesShop API key issued by HubZell
X-TimestampintegerYesUnix timestamp in seconds; must be within ±5 minutes of server time
X-SignaturestringYesHex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
Example headers
Content-Type: application/json
X-Api-Key: ak_b419becab1273243
X-Timestamp: 1783321239
X-Signature: a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9

Request body

Send as application/json via POST to the endpoint above.

FieldTypeRequiredDescription
company_idstringYesSame value as company_id from /service — identifies both the game/product and (via lookup) the provider to check against
user_idstringYesPlayer / account / user id to validate
server_idstringNoServer / zone id — required by some games, not others
Example request
{
  "company_id": "HUBZELLFLA71B",
  "user_id": "312678959",
  "server_id": "3611"
}

Response

FieldTypeRequiredDescription
validbooleanNoWhether the id resolved to a real account
account_namestring | nullNoIn-game display name for the resolved account, when the provider returns one
error_codestring | nullNoProvider error code when not valid (e.g. INVALID_PLAYER_ID)
error_messagestring | nullNoHuman-readable reason when not valid
Example response
{
  "status": 0,
  "code": "SUCCESS",
  "message": "success",
  "data": {
    "valid": true,
    "account_name": "PlayerOne",
    "error_code": null,
    "error_message": null
  }
}

webhooks

POST{shop callback_url}

Order result callback

When the upstream provider confirms an async payment or refund, HubZell relays the result to your shop's configured callback_url, as a JSON POST body. Only final statuses are delivered, best-effort with no retry — poll orders/status if you never receive one. The body carries a legacy `signature` field (MD5 of your secret_key + timestamp, kept for backward compatibility) — verify the stronger `X-OP-Webhook-Signature` header instead before trusting the payload.

Headers

Sent by HubZell on every callback, in addition to the JSON body below.

FieldTypeRequiredDescription
Content-TypestringYesAlways `application/json`
X-OP-Webhook-EventstringYesEvent name — always `order.updated`
X-OP-Webhook-Event-IDstringYesUnique id for this delivery attempt, for deduplication
X-OP-Webhook-TimestampintegerYesUnix timestamp in seconds — same value as the body's `timestamp` field
X-OP-Webhook-SignaturestringYes`sha256=` followed by the hex HMAC-SHA256 digest of `secret_key`, keyed over `.` — a stronger signature than the body's `signature` field, verify either before trusting the callback
Example headers
Content-Type: application/json
X-OP-Webhook-Event: order.updated
X-OP-Webhook-Event-ID: 8f14e45f-ceea-467d-8a3f-2a1d4e3c9b2a
X-OP-Webhook-Timestamp: 1783321500
X-OP-Webhook-Signature: sha256=3f2504e04f8964efe040cbb8e5c9c3d1a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d

Callback payload

Posted to your as application/json.

FieldTypeRequiredDescription
dest_refstringYesYour order reference, as sent when the payment or refund was placed
statusstringYesResult status — SUCCESS or FAILED
messagestringNoHuman-readable result message from the provider, when available
transaction_idstringNoProvider transaction id, when available
amountnumberNoAmount charged for this order
currencystringNoCurrency code for amount
timestampintegerYesUnix timestamp in seconds this callback was signed at
signaturestringYesLegacy MD5 hex digest of `secret_key + timestamp`, kept in the body for backward compatibility — verify `X-OP-Webhook-Signature` instead, not this field
Example request
{
  "dest_ref": "ORD20260708001",
  "status": "SUCCESS",
  "message": "Top-up successful",
  "transaction_id": "987654321",
  "amount": 0.25,
  "currency": "USD",
  "timestamp": 1783321500,
  "signature": "3f2504e04f8964efe040cbb8e5c9c3d1"
}

Your response

Return HTTP 200 to acknowledge. The response body isn't read.