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.
Field
Type
Required
Description
Content-Type
string
Yes
Must be `application/json`
X-Api-Key
string
Yes
Shop API key issued by HubZell
X-Timestamp
integer
Yes
Unix timestamp in seconds; must be within ±5 minutes of server time
X-Signature
string
Yes
Hex 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.
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.
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:
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.
The error envelope's code field is always one of the following.
Code
HTTP status
Meaning
MISSING_CREDENTIALS
401
One or more of the X-Api-Key, X-Timestamp, or X-Signature headers is missing from the request.
INVALID_TIMESTAMP_FORMAT
401
X-Timestamp isn't a valid unix timestamp in seconds.
EXPIRED_TIMESTAMP
401
X-Timestamp is more than 5 minutes off from the server's clock.
INVALID_SIGNATURE
401
X-Signature doesn't match the expected HMAC — check your secret_key and how the signature string is built.
IP_NOT_WHITELISTED
403
The 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_BLOCKED
403
This IP has been blocked by HubZell directly, independent of your shop's whitelist — typically abusive or scanning traffic.
SHOP_DISABLED
403
Your shop account has been disabled. Contact HubZell support.
KYC_NOT_APPROVED
403
Your shop's KYC hasn't been approved yet — order placement is disabled until it is.
INSUFFICIENT_BALANCE
400
Your wallet's available balance can't cover this request.
CALLBACK_URL_REQUIRED
400
Your shop's callback URL isn't configured yet — set one on the Developer page before placing orders.
VALIDATION_ERROR
400
The request body failed validation — check the response's message field for which field(s) are wrong.
DUPLICATE_REQUEST
409
This request has already been processed.
MAINTENANCE_MODE
503
The Gateway is temporarily under maintenance — retry shortly.
NOT_FOUND
404
The requested route doesn't exist — check the endpoint path and method.
INTERNAL_ERROR
500
An 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.
Field
Type
Required
Description
Content-Type
string
Yes
Must be `application/json`
X-Api-Key
string
Yes
Shop API key issued by HubZell
X-Timestamp
integer
Yes
Unix timestamp in seconds; must be within ±5 minutes of server time
X-Signature
string
Yes
Hex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
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.
Field
Type
Required
Description
Content-Type
string
Yes
Must be `application/json`
X-Api-Key
string
Yes
Shop API key issued by HubZell
X-Timestamp
integer
Yes
Unix timestamp in seconds; must be within ±5 minutes of server time
X-Signature
string
Yes
Hex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
Send as application/json via POST to the endpoint above.
Field
Type
Required
Description
category
enum
No
Same category returned by /service (uppercase). Preferred over type — supply exactly one.
type
enum
No
Legacy alias for category (lowercase): billpay | mtopup | gtopup | cashcard
company_id
string
No
catalog_product code (same value returned by /service). Preferred over pay_to_company — supply exactly one.
pay_to_company
string
No
Legacy alias for company_id
service_id
string
No
catalog_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_amount
number
No
Legacy alias for service_id — matches a plan by reseller price instead. Supply exactly one of service_id / pay_to_amount.
user_id
string
No
Game account id — alias for pay_to_ref1 when category is GTOPUP. Supply exactly one.
pay_to_ref1
string
No
Phone number / bill account / game id. Required as-is for non-GTOPUP categories; legacy alias for user_id on GTOPUP.
server_id
string
No
Game server/zone id — alias for pay_to_ref2 when category is GTOPUP
pay_to_ref2
string
No
Reference 2 — required by some billpay services, or legacy alias for server_id on GTOPUP
pay_to_ref3
string
No
Reference 3 — required by some billpay services
pay_to_barcode1
string
No
Barcode number for barcode-based bill payments
dest_ref
string
Yes
Your unique order reference (alphanumeric, max 20 chars)
Resolved category, uppercase (echoes category if supplied, else type uppercased)
pay_to_company
string
No
The catalog_product code the order was placed against, echoed back
pay_to_ref1
string
No
The 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_ref
string
No
Your order reference, echoed back
total_amount
number
No
Amount charged to your wallet — the catalog_service's reseller price (price_reseller), resolved server-side, never a client-supplied figure
balance
string
No
Your HubZell wallet balance after this charge (auto-refunded and reflected here if the provider then rejects the order)
...
object
No
Remaining fields are the provider's own raw response, spread as-is (field set varies; typically includes code, transaction_id, bill_id, queue_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.
Field
Type
Required
Description
Content-Type
string
Yes
Must be `application/json`
X-Api-Key
string
Yes
Shop API key issued by HubZell
X-Timestamp
integer
Yes
Unix timestamp in seconds; must be within ±5 minutes of server time
X-Signature
string
Yes
Hex HMAC-SHA256 digest of `secret_key`, keyed over `\n\n\n`
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.
Field
Type
Required
Description
Content-Type
string
Yes
Always `application/json`
X-OP-Webhook-Event
string
Yes
Event name — always `order.updated`
X-OP-Webhook-Event-ID
string
Yes
Unique id for this delivery attempt, for deduplication
X-OP-Webhook-Timestamp
integer
Yes
Unix timestamp in seconds — same value as the body's `timestamp` field
X-OP-Webhook-Signature
string
Yes
`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
Your order reference, as sent when the payment or refund was placed
status
string
Yes
Result status — SUCCESS or FAILED
message
string
No
Human-readable result message from the provider, when available
transaction_id
string
No
Provider transaction id, when available
amount
number
No
Amount charged for this order
currency
string
No
Currency code for amount
timestamp
integer
Yes
Unix timestamp in seconds this callback was signed at
signature
string
Yes
Legacy MD5 hex digest of `secret_key + timestamp`, kept in the body for backward compatibility — verify `X-OP-Webhook-Signature` instead, not this field