Skip to main content

Webhooks

Configure and enable the webhook URL and secret in the integration's Webhooks tab in the dashboard. Use an endpoint on your server that accepts POST requests with a JSON body.

Shop API sends final results only:

EventStatusMeaning
order.completedcompletedPurchase completed successfully
order.refundedrefundedPurchase failed and the charged amount was returned

No separate webhook is sent for processing, review, recipient checks or insufficient balance. Telegram notifications are configured separately.

Request body​

{
"event": "order.completed",
"timestamp": "2026-09-26T12:00:05.000Z",
"data": {
"order_id": 125,
"partner_order_id": "order-456",
"api_user_id": "customer-789",
"module": "codes",
"status": "completed",
"amount_usd": "0.9800",
"currency": "USD",
"codes": [
{"content": "EXAMPLE-CODE"}
]
}
}
FieldDescription
eventorder.completed or order.refunded
timestampWebhook creation time in UTC, ISO 8601; not the original order creation time
data.order_idAdaptGroup order ID
data.partner_order_idYour order ID or null
data.api_user_idYour customer's string ID
data.modulestars, tg_premium, topups or codes
data.statusFinal order status
data.amount_usdCharged/refunded amount as a decimal string
data.currencyAlways USD
data.codesIssued codes only for a completed codes order; otherwise []

An issued code can contain content, number and/or pin. Only present values are included.

Refund example:

{
"event": "order.refunded",
"timestamp": "2026-09-26T12:00:05.000Z",
"data": {
"order_id": 125,
"partner_order_id": "order-456",
"api_user_id": "customer-789",
"module": "codes",
"status": "refunded",
"amount_usd": "0.9800",
"currency": "USD",
"codes": []
}
}

Verify the signature​

The X-Webhook-Signature header contains the lowercase hexadecimal HMAC-SHA256 digest of the exact request body bytes, signed with your webhook secret.

The secret itself is not sent in the header or body. It is separate from the API key.

Verify the raw body before parsing it. Re-serializing JSON changes its bytes and can invalidate the signature.

const crypto = require('node:crypto');

function verifySignature(rawBody, secret, signature) {
if (typeof signature !== 'string' || !/^[a-f0-9]{64}$/.test(signature)) {
return false;
}

const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest();

return crypto.timingSafeEqual(expected, Buffer.from(signature, 'hex'));
}

rawBody must be the original Buffer received by your HTTP server, before a JSON parser changes it.

Acknowledge and handle repeats​

  1. Verify the signature.
  2. Match data.order_id to your order.
  3. Save and apply the final result once.
  4. Return HTTP 200 or 201 after you accept the event.

A response body is not required. Other statuses, including 204, do not acknowledge delivery. The sender waits up to 10 seconds for the HTTP request.

An event may be delivered repeatedly. Use the integration, data.order_id and event to recognize a result already processed. Do not issue the goods or credit a refund again. This handling of webhook repeats does not make repeated /orders/create requests the same purchase.

Delivery attempts​

Once the event has been accepted by the delivery service, unsuccessful delivery is retried:

AttemptDelay after the preceding failed attempt
1Immediately
21 minute
32 minutes
45 minutes
510 minutes
615 minutes
730 minutes
81 hour
92 hours
102 hours

A webhook is not a guarantee that every result will reach your server. If it does not arrive, retrieve the saved result with Order status. Replaying a purchase is not a delivery-recovery mechanism.

© 2026 AdaptGroup LLC. All rights reserved.
30 N Gould St Ste R, Sheridan, WY 82801, USA
Back to top