My Coupons
Where users find what they bought. This screen is yours to build, and your own store should be the primary source for it.
Your store is the source of truth
You persist every coupon at delivery time. Render "My Coupons" from that store, not from a live Cheggout call:
- It is faster. No round trip on a screen users open often.
- It works offline, or when Cheggout is briefly unavailable.
- It is complete.
getUserCouponsis scoped to a single offer, so building a full list from it means one call per offer the user has ever bought.
Use the Cheggout endpoint for reconciliation and recovery, not for rendering.
What to show
For each coupon:
| Element | Source |
|---|---|
| Brand and product name | Your catalogue cache |
| Coupon code, prominently | Your store, from delivery |
| A copy action | — |
| Validity | coupon_validity from delivery |
| Redemption instructions | how_to_redeem from your catalogue cache |
| Terms | tnc from your catalogue cache |
| Redeem action | The url from delivery |
Sort unexpired coupons first, then expired. Users open this screen to use something, not to browse history.
Reconciliation
Run a scheduled job over any delivery your ledger recorded as uncertain — a timeout, an
INTERNAL_ERROR, an ambiguous retry.
The match is exact because extTransactionId is echoed on every returned coupon. That is the whole
reason to use your real reference rather than a random value.
{
"status": 200,
"message": "Fetched Successfully",
"data": {
"id": "517",
"brand_name": "Example Brand",
"product_name": "Sample Product Voucher",
"offer_validity": "2026-12-31T16:34:00.000Z",
"tnc": "One redemption per customer.",
"how_to_redeem": "Present the code at checkout.",
"url": "https://click.cheggout.com/REPLACE",
"coupons": [
{
"coupon_code": "REPLACE-COUPON-CODE",
"extTransactionId": "PUB_OFFERBUY_20260731_000045",
"purchase_time": "2026-07-31T14:15:31.000Z"
}
]
}
}
Each entry has a code, your reference and a timestamp — no per-coupon expiry, no used/unused flag,
no invalidation state. Validity comes from the offer's offer_validity. If you need redemption
state, track it yourself. TBD
Invalidation
A coupon can stop being honourable after you have given it to a customer — a brand withdraws the campaign, stock turns out to be short, or an issue is rolled back. Cheggout can notify you through a webhook you host.
Reason codes: BRAND_CALLED_OUT, OUT_OF_STOCK, TECHNICAL_GLITCH, FRAUD_DETECTED,
OFFER_TERMINATED. Whether to refund coins is your business decision — Cheggout does not hold your
coin balance.
The webhook payload identifies the offer, not the coupon or the user — there is no virtualId,
coupon_code or extTransactionId in it. You cannot tell whose coupon was invalidated, only that an
offer is affected.
It also has no signature, and no published retry policy. Verify before acting: cross-check with
getUserCoupons rather than issuing refunds off the
notification alone. TBD — see
Open items.
If you do not implement the webhook, a periodic reconciliation job over getUserCoupons is a
partial substitute — it will not tell you why something changed, but it will keep your store
aligned.
Handling an invalid coupon well
The worst outcome is a customer at a till with a code that does not work. In order of preference:
- Mark it expired in your UI as soon as you learn, with a clear explanation.
- Refund the coins if your policy allows, and tell the user you have.
- Notify proactively rather than letting them discover it.
- Log it against the
referenceIdso support can explain what happened.