Coupon status webhook
This contract runs in the opposite direction: you host the endpoint, and Cheggout calls it when a coupon that was already delivered becomes invalid before its expected validity.
TBD Availability is not confirmed. The contract is published, but whether the webhook is actively delivered for your channel — and the operational details below — must be confirmed with Cheggout before you rely on it. Tracked in Open Items.
Why it exists
A coupon can stop being honourable after you have handed it to a customer — a brand withdraws the campaign, stock turns out to be short, or a technical issue is rolled back. The webhook lets you mark it expired in your "My Coupons" view and decide whether to refund the coins, rather than letting a customer discover the failure at the till.
The endpoint you build
POST https://<your-domain>/cheggout/webhook/coupon-status
The path shown is the documented convention. Confirm the exact URL with Cheggout during onboarding and register it with them.
Authentication
The documented control is IP whitelisting of Cheggout's outbound addresses.
TBD There is no request signature, and the published contract describes IP whitelisting as recommended rather than required. Cheggout's outbound IP list is also not published.
Without a signature, an unauthenticated caller who knows your URL can assert that a coupon was invalidated. Mitigate on your side:
- Restrict the endpoint to Cheggout's IP ranges at your edge, once you have them.
- Treat the call as a trigger to verify, not as authority. Confirm against
getUserCouponsbefore taking an irreversible action such as a refund. - Never expose the endpoint publicly without at least IP restriction.
Ask Cheggout for the outbound IP list and for signature support before going live.
Request from Cheggout
| Field | Type | Description |
|---|---|---|
channelName | string | Your channel identifier |
offerId | string | The offer whose coupons are affected |
eventType | string | "COUPON_INVALIDATED" |
expiredDate | string | When the invalidation takes effect, ISO 8601 with offset |
reason | string | Machine-readable reason — see below |
reasonMessage | string | Human-readable explanation |
referenceId | string | Cheggout's reference for this event |
reason values
| Value | Meaning | Typical handling |
|---|---|---|
BRAND_CALLED_OUT | The brand withdrew the offer | Mark expired, consider a refund |
OUT_OF_STOCK | Stock could not be honoured | Mark expired, refund |
TECHNICAL_GLITCH | Issued in error, rolled back | Mark expired, refund |
FRAUD_DETECTED | Fraud controls triggered | Mark expired, review the account |
OFFER_TERMINATED | Campaign ended early | Mark expired, consider a refund |
Whether to refund coins is a business decision, not a platform one — Cheggout does not hold your coin balance.
{
"channelName": "YOURCHANNEL",
"offerId": "517",
"eventType": "COUPON_INVALIDATED",
"expiredDate": "2026-08-02T12:30:00+05:30",
"reason": "OUT_OF_STOCK",
"reasonMessage": "Brand inventory could not be fulfilled.",
"referenceId": "REPLACE_EVENT_REFERENCE"
}
Your response
{ "status": "OK" }
Return this once you have accepted the event. Acknowledge quickly and process asynchronously — long-running work inside the handler risks a timeout on Cheggout's side.
Significant gaps
These are limitations of the current contract that will shape your implementation.
There is no virtualId, no coupon_code and no extTransactionId in the body. You cannot tell
which user's coupon was invalidated — only that an offer is affected.
In practice this means the event applies at offer granularity. If you must act on individual
coupons, you have to look up every coupon you delivered for that offerId yourself, and decide
whether all of them are affected. Confirm the intended semantics with Cheggout before implementing
refunds off the back of it. TBD
Also unspecified:
| Gap | Impact |
|---|---|
| No retry or backoff policy | You do not know whether a failed delivery will be re-attempted. Make your handler resilient and reconcile independently. |
| No signature | See the authentication warning above. |
referenceId semantics | Whether it is safe as a de-duplication key is unconfirmed — de-duplicate on it defensively anyway. |
No eventType values beyond invalidation | Assume more may be added; ignore unknown types rather than failing. |
Implementation guidance
A robust handler:
- Verifies the source against Cheggout's IP range.
- Acknowledges immediately with
{"status":"OK"}and queues the work. - De-duplicates on
referenceIdso a repeat delivery is harmless. - Ignores unknown
eventTypevalues rather than erroring. - Verifies before refunding — cross-check with
getUserCouponsrather than acting on the notification alone. - Logs every event with its
referenceIdfor reconciliation.
If you don't implement it
The webhook is optional. Without it you will not learn about invalidations proactively, and a
customer may present a coupon that is no longer honoured. A periodic reconciliation job over
getUserCoupons is a partial substitute.