Webhooks / Callbacks

Receive asynchronous updates when your order completes.

Instead of polling the order-status endpoint, we strongly recommend providing a Callback URL in your Reseller Settings.

When an order reaches a terminal state (success or failed), our background worker will send an HTTP POST request to your callback URL.

Payload Format

We will send a JSON payload to your server:

200Webhook Payload
json
{
"ref_id": "R-12345",
"order_code": "AGN-D7E8F9A0B1C2",
"status": "success",
"product_code": "MLBBPH-86",
"product_name": "Mobile Legends 86 Diamonds",
"price": 25000,
"serial_number": "ALG-A1B2C3D4EF",
"message": "Order completed successfully",
"failure_code": null,
"failure_reason": null
}

Failed Order Payload

A failed order now carries the reason. status and message are unchanged; failure_code and failure_reason were added beside them, so existing integrations keep working untouched.

200Failed Webhook Payload
json
{
"ref_id": "R-12346",
"order_code": "AGN-B4C5D6E7F8A9",
"status": "failed",
"product_code": "MLBBPH-86",
"product_name": "Mobile Legends 86 Diamonds",
"price": 25000,
"serial_number": null,
"message": "Order failed",
"failure_code": "PRODUCT_UNAVAILABLE",
"failure_reason": "The order was rejected: product not found"
}

Both fields can be null on a failure

Some upstream failures reach us as a bare “failed” with no explanation. We send null rather than inventing a reason. failure_code is an open set; handle the codes you know and display failure_reason for the rest. The full list is on the Order Status page.

order_code: new, additive

order_code is a short, human-quotable rendering of our internal order id (AGN- + 12 characters) that your support team can read out loud. It was ADDED beside the existing keys; ref_id, status, product_code, product_name, price, serial_number and message all keep their exact names, types and values. Match callbacks on ref_id as you always have; never treat order_code as an idempotency key.

Signature note

Every new field is inside the signed JSON body, so your existing HMAC verification covers them with no change. Just make sure your parser does not reject unknown keys.

Serial Number Format

The serial_number is always prefixed with ALG-. For voucher products the real redeemable code is appended after a /:

200Voucher Webhook Payload
json
{
"ref_id": "R-99001",
"order_code": "AGN-F1A2B3C4D5E6",
"status": "success",
"product_code": "ALGAN-STEAMPHP-500",
"product_name": "Steam Voucher PHP 500",
"price": 135000,
"serial_number": "ALG-A1B2C3D4EF/STEAMPH-9XQ2-K7MP-44TZ",
"message": "Order completed successfully",
"failure_code": null,
"failure_reason": null
}

Delivering voucher codes

Split serial_number on the last /: serialNumber.slice(serialNumber.lastIndexOf('/') + 1). Everything after the last / is the actual code your customer redeems (e.g. STEAMPH-9XQ2-K7MP-44TZ). A voucher serial can be three segments deep (INV-XXXXXXXXXX/ALG-ref/CODE), so splitting on the first / would hand your customer our internal reference glued to their code. For instant/direct products there is no /; the value is a branded reference token only, since the top-up is delivered straight to the target account.

Security

To ensure the webhook is genuinely from Algan, we include an X-Signature and X-Timestamp header in the request. The signature is generated using the exact same HMAC-SHA256 logic used for your requests. Make sure your code is robust enough to accept either signature or body signed by Algan, or accept both. Also, whitelist our IP Address too at: 208.77.246.15

Verification Steps

  1. Verify the timestamp is within 5 minutes of your server’s current time.
  2. Hash the received raw JSON payload and the timestamp using your API Secret.
  3. Compare your generated hash with the X-Signature header. If they match, the request is authentic.

Retry Policy

If your server does not answer with a 2xx HTTP status code, or times out after 5 seconds, we retry the callback automatically. The times below are measured from the first attempt, because the waits run back to back:

Retry Schedule

ParameterRequiredDescription
Attempt 1NoImmediate: sent right after the order reaches a terminal state (T+0s).
Attempt 2No5 seconds after the first attempt (T+5s).
Attempt 3No15 seconds after the first attempt (T+15s), a 10 second wait after attempt 2.
Attempt 4 (Final)No30 seconds after the first attempt (T+30s), a 15 second wait after attempt 3.

Sizing your reconciliation window

Worst case, the last attempt is fired at T+30s and can still be in flight for its own 5 second timeout, so allow at least 35 seconds from the terminal state before you treat a callback as missing. If your endpoint is slow to answer, every attempt shifts later by however long it holds the connection open.

Permanently Failed

If all 4 attempts fail, the callback is marked as permanently failed. Our worker will not retry again automatically.

You have two options:

  1. Resend from Dashboard: Go to your Reseller Dashboard → Orders → click the failed order → “Resend Callback”.
  2. Poll manually: Use the GET /api/v1/order-status?ref_id=... endpoint to fetch the current order status.
PreviousValidate IDNextRate Limits