Webhooks
Webhooks allow you to receive real-time server-side notifications about transactions and catalog changes. Unlike SDK events (which are client-side), webhooks are server-to-server callbacks that are reliable and cannot be missed due to client disconnections.
Overview
If a partner is interested in receiving real-time callbacks from Hubble, they can share a webhook URL with us. Hubble will send POST requests to this URL for supported events.
All webhooks are signed using an HMAC signature, and partners are expected to verify the request authenticity using the X-Verify header.
Transaction Webhooks
Triggered whenever a transaction occurs via the Hubble SDK. Different webhook payloads are sent based on the order status.
| Order Status | When It Fires | Voucher Data |
|---|---|---|
| COMPLETED | Gift card successfully generated | Includes voucher codes, PINs, and validity dates |
| FAILED | Order failed after payment | Empty vouchers array; coins are reversed if applicable |
| REVERSED | Manual reversal by Hubble team | Empty vouchers array; refund has been processed |
COMPLETED Status
Triggered by: Successful redemption
{
"transactionReferenceId": "01HRVY38NCAANDYPN3WVGJCDSG",
"partnerCoinTransactionId": "01HZ123EXAMPLE456789",
"partnerCoinReversalTransactionId": null,
"amount": 1000.0,
"discountAmount": 50.0,
"timestamp": "2024-01-15T14:30:22",
"userId": "01HRW71319JNKJS6D3Q85X780R",
"vouchers": [
{
"id": "01HZ46Q0P0242STC8TBV926M8C",
"brandId": "01JRSY42BBMKC2YS9TXMYSYN83",
"brandName": "Swiggy",
"cardType": "CARD_NUMBER_SECURED",
"cardNumber": "5333644476668777",
"cardPin": "123456",
"validTill": "2025-04-04",
"amount": 1000.0,
"shareImageUrl": "https://gullak-assets.s3.ap-south-1.amazonaws.com/share-wa-brand-images/Swiggy.jpg",
"brandRetailModes": ["online", "offline", "app", "website"]
}
],
"brandName": "Swiggy",
"orderStatus": "COMPLETED"
}
FAILED Status
Triggered when orders fail.
{
"transactionReferenceId": "01HRVY38NCAANDYPN3WVGJCDSG",
"partnerCoinTransactionId": "01HZ123EXAMPLE456789",
"partnerCoinReversalTransactionId": "01HZ456REVERSAL789012",
"amount": 1000.0,
"discountAmount": 50.0,
"timestamp": "2024-01-15T14:35:10",
"userId": "01HRW71319JNKJS6D3Q85X780R",
"vouchers": [],
"brandName": "Swiggy",
"orderStatus": "FAILED"
}
REVERSED Status
Triggered during manual reversals.
{
"transactionReferenceId": "01HRVY38NCAANDYPN3WVGJCDSG",
"partnerCoinTransactionId": "01HZ123EXAMPLE456789",
"partnerCoinReversalTransactionId": "01HZ456REVERSAL789012",
"amount": 1000.0,
"discountAmount": 50.0,
"timestamp": "2024-01-15T15:00:00",
"userId": "01HRW71319JNKJS6D3Q85X780R",
"vouchers": [],
"brandName": "Swiggy",
"orderStatus": "REVERSED"
}
brandRetailModescan be null and must be treated as flexible tags, not a strict enum.partnerCoinReversalTransactionIdandpartnerCoinTransactionIdwill be null always if coin module is not integrated.
Brand Update Webhooks
Used to notify partners of catalog changes such as discount updates, new brand additions, or brand deactivation.
USER_DISCOUNT_UPDATED
{
"event": "USER_DISCOUNT_UPDATED",
"details": {
"id": "01GMAVS2CHXR0XP1BZSTA9A44K",
"name": "Amazon shopping",
"discountPercentage": 5
}
}
NEW_BRAND_CREATED
{
"event": "NEW_BRAND_CREATED",
"details": {
"id": "01GMAVS2CHXR0XP1BZSTA9A44K",
"name": "Amazon shopping",
"discountPercentage": 2
}
}
BRAND_DISABLED
{
"event": "BRAND_DISABLED",
"details": {
"id": "01GMAVS2CHXR0XP1BZSTA9A44K",
"name": "Amazon shopping"
}
}
PARTNER_DISCOUNT_UPDATED
{
"event": "PARTNER_DISCOUNT_UPDATED",
"details": {
"brandId": "01HFV6NGBFGATYAKZ3AKASJVM9",
"subventionPercentage": 8,
"validFrom": "2025-12-23",
"validTo": "2026-02-28"
}
}
Webhook Authentication & Security
All webhook requests from Hubble are secured using HMAC-SHA256 signatures.
Signature Verification
Each webhook request includes an X-Verify header containing an HMAC-SHA256 signature of the request body, encoded in Base64.
Verification steps:
- Extract the
X-Verifyheader from the incoming request - Read the raw request body as a string (before any JSON parsing)
- Compute HMAC-SHA256 of the raw body using your webhook secret key, then Base64-encode the result
- Compare your computed signature with the
X-Verifyheader. Only process the request if they match exactly
Example: Node.js
const crypto = require("crypto");
function verifyWebhook(rawBody, signatureHeader, secret) {
const hmac = crypto.createHmac("sha256", secret);
hmac.update(rawBody);
const expected = hmac.digest("base64");
return expected === signatureHeader;
}
// In your webhook handler:
app.post("/hubble-webhook", (req, res) => {
const rawBody = req.rawBody; // Must be the raw string, not parsed JSON
const signature = req.headers["x-verify"];
if (!verifyWebhook(rawBody, signature, WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
// Process the webhook...
res.status(200).send("OK");
});
Example: Kotlin
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.util.Base64
private fun generateSignature(payload: String, secret: String): String {
val algorithm = "HmacSHA256"
val secretKeySpec = SecretKeySpec(secret.toByteArray(), algorithm)
val mac = Mac.getInstance(algorithm).apply { init(secretKeySpec) }
val signatureBytes = mac.doFinal(payload.toByteArray())
return Base64.getEncoder().encodeToString(signatureBytes)
}
fun verifyWebhook(requestBody: String, signatureHeader: String, secret: String): Boolean {
val expectedSignature = generateSignature(requestBody, secret)
return expectedSignature == signatureHeader
}
Important Identifiers
transactionReferenceId: Use this as the unique transaction identifier for all webhook callbacks. This ID remains consistent throughout the transaction lifecycle.userId: This corresponds to thecustomer_idthat was provided to Hubble during initialization.
Security Best Practices
- Always verify the webhook signature before processing any request
- Never log or expose your webhook secret key
- Use HTTPS for your webhook endpoint
- Implement rate limiting to prevent abuse
- Validate all incoming data before processing
IP Whitelisting (Optional)
For additional security, you can whitelist Hubble's production IP addresses:
35.200.156.19934.47.147.24434.14.138.52
- Signature validation failure: If you are parsing the JSON body before computing the signature, the whitespace or field ordering may differ from the raw body. Always compute the HMAC on the raw request string.
- Webhook endpoint returning 401/400/500: Your webhook endpoint should authenticate using the X-Verify signature, not your app's general auth middleware.
- Missing FAILED status handling: Always handle both COMPLETED and FAILED statuses.
- Webhooks not being received: Confirm your webhook URL has been registered with the Hubble team for the correct environment.
Hubble may retry webhook delivery if your endpoint does not return a 200 response. Implement idempotency on your webhook handler using the transactionReferenceId. If you have already processed a webhook for a given transactionReferenceId, return 200 without reprocessing.