Scratch cards on the web
The Cheggout Rewards Widget is a single browser script that renders reward banners and a scratch card overlay on your website. You include one script tag, add one container element, and call one or two functions when a user does something rewardable. The widget handles the card UI, the scratch interaction, the API calls and the claim flow.
It is built with React and Vite and ships as a self-contained IIFE bundle — all components, assets, styles and business logic in one file. You do not need React yourself.
What it gives you
| Component | Behaviour |
|---|---|
| Reward banner | Injected into a container you designate, when a user completes a milestone |
| Scratch card overlay | A modal scratch experience launched from the banner |
| Reward claimed state | The revealed reward, rendered by the widget |
| Interaction data | Written to localStorage so you can avoid re-triggering |
The only documented script URL is a UAT host. There is no published production URL.
https://chegwidgetuat.z29.web.core.windows.net/cheggout-widget.iife.js?channelName={channelName}
TBD Do not ship this URL to production. Request your production widget URL from Cheggout before go-live, and treat the widget's API surface as subject to change until then. Tracked in Open Items.
Prerequisites
| Requirement | Notes |
|---|---|
| Public key certificates exchanged | Both directions, as for every Cheggout integration |
| HTTPS everywhere | Required |
Requests signed with SHA256withRSA | For the server-side token call. See Signed envelope |
| The widget script, provided by Cheggout | Per channel |
A virtualId per logged-in user | Yours, stable and immutable |
| A campaign configured for your channel | Otherwise nothing renders |
Installation
1. Include the script
<script src="https://chegwidgetuat.z29.web.core.windows.net/cheggout-widget.iife.js?channelName=YOURCHANNEL"></script>
The widget reads channelName from its own script tag's query string. There is no separate
initialisation call for the channel, so the query parameter is not optional.
2. Add the banner container
The container id is fixed and the widget looks for it exactly:
<div id="cheggout-banner-container" class="my-8 min-h-[90px] w-full"></div>
Place it where a reward banner should appear — typically below article content or on a home feed. The minimum height reserves space so the injected banner does not shift your layout.
An earlier integration email referenced a cheggout-widget.css file. Styles are bundled inside the
script. Treat a separate CSS <link> as legacy and unnecessary. TBD If your
onboarding pack still lists it, confirm before adding it.
Trigger events
The widget exposes its API on window.CheggoutRewards. There are two functions you should be
calling, and one legacy function you will see in older code.
Inline reward banners
Call this when a user completes a predefined milestone — 100% reading progress, a scroll action, or any other qualifying event you have agreed with Cheggout. It injects an interactive reward banner into the container.
window.CheggoutRewards.bannerTriggerEvent(
virtualId,
authToken,
eventType,
deviceType,
additionalParams
);
| Parameter | Type | Required | Description |
|---|---|---|---|
virtualId | string | Yes | Your unique user id, for example sessionStorage.getItem("vid") |
authToken | string | For logged-in users | Bearer token generated for the user session |
eventType | string | Yes | Event identifier, for example "article_read" |
deviceType | string | Yes | "web", "mobile" |
additionalParams | object | No | For example { bannerURL: "<url>" } |
If additionalParams supplies a bannerURL, that image is displayed. Otherwise the widget renders
its default static banner — in which case pass an empty object.
window.CheggoutRewards.bannerTriggerEvent(
sessionStorage.getItem("vid"),
authToken,
"article_read",
"web",
{}
);
Check pending rewards
Call this on landing and home pages, immediately after login, to find out whether the user has unclaimed rewards waiting.
window.CheggoutRewards.checkPendingRewardEvent(
virtualId,
authToken,
eventType,
additionalParams
);
eventType and additionalParams are both optional here. This is the call that turns an
unscratched card into a reason to come back — without it, a user who earned a card and left has no
prompt on return.
Legacy article trigger
You will encounter this in older integrations:
window.CheggoutRewards.fetchScratchCard("NULL", virtualId, "CAMPAIGN_ARTICLEREAD");
It was used with static event codes such as CAMPAIGN_ARTICLEREAD (triggered after five events).
TBD Treat fetchScratchCard as legacy. It takes no auth token, and its first
argument is not used by current builds. CAMPAIGN_ADREAD availability is unconfirmed. New
integrations should use bannerTriggerEvent and checkPendingRewardEvent. An articleTriggerEvent
variant also exists in current builds but is not formally specified — do not build against it until
it is.
If you are working in TypeScript against an older integration, this is the declaration that was circulated:
declare global {
interface Window {
CheggoutRewards?: {
fetchScratchCard: (category: string, virtualId: string, trackerEvent: any) => void;
};
}
}
Guest and logged-in users
The widget distinguishes the two by the value of virtualId.
Guest users. Pass virtualId = "guest". The user is treated as non-logged-in. authToken is
not required. On interaction, redirect the user to your own login page.
After login. authToken is required. Re-trigger the banner flow with the updated
virtualId and the full parameter set: virtualId, authToken, eventType, deviceType,
additionalParams.
The re-trigger is not optional. Without it, a user who logs in mid-flow is left looking at a banner that belongs to a guest identity.
The token API
authToken comes from a signed server-side call. It is a user-scoped token.
| Environment | URL |
|---|---|
| Production | https://couponapi.cheggout.com/api/GetAuthToken |
| UAT | https://bankapi-uat.cheggout.com/api/GetAuthToken |
Signed envelope request. The BDATA payload:
{
"virtualId": "pub_8f14e45fceea167a",
"channelName": "YOURCHANNEL"
}
Decoded response:
{ "token": "<user token>", "expireDate": "<expiry timestamp>" }
The token is valid for one hour. When it expires, obtain a new one by calling the same API from your server. The token is then used as the bearer token for the fetch-reward-info API and other user-detail APIs.
That one-hour figure is documented for the widget token specifically. Other token lifetimes vary by channel — see Tokens.
The signing key must never reach client-side JavaScript. Expose a small endpoint on your own backend that returns a token for the currently authenticated session, and call that from the page.
TBD The host for this endpoint is inconsistent across documents. The widget
documentation names couponapi.cheggout.com for production, while the Rewards Center product line
documents GetAuthToken on rewardscenter.cheggout.com. Confirm your host during onboarding rather
than assuming. Tracked in Open Items.
Full contract: GetAuthToken.
Interaction data in localStorage
The widget writes interaction state to localStorage. It is the documented mechanism for tracking
reward-related actions and, importantly, for preventing bannerTriggerEvent from firing more than
once for the same article.
| Key | Type | Meaning |
|---|---|---|
cheggout_banner_clicked_<ARTICLE_ID> | Boolean | The user already clicked the reward banner for that article |
| Card state | JSON array | Per-card isSeen and claimed flags |
[
{ "rewardId": 121783, "isSeen": false, "claimed": false },
{ "rewardId": 121782, "isSeen": false, "claimed": false }
]
Read cheggout_banner_clicked_<ARTICLE_ID> before calling bannerTriggerEvent on a page the user
may revisit.
TBD There is no documented JavaScript callback or event emitter for banner
clicks or card claims. localStorage is the only documented signal, which means you cannot react
synchronously to a claim. A formal callback API is not available. If your analytics need
claim-level events, raise it with Cheggout.
Reward listing page
Cheggout hosts a page that lists a user's cards — unscratched first, then scratched.
https://scratchcardpwasg.z29.web.core.windows.net/my-rewards?token={tokenvalue}
Replace tokenvalue with the token from the auth token API.
TBD The canonical launch URL is not confirmed. An earlier email documented a different form:
https://scratchcardpwasg.z29.web.core.windows.net/?channelName={channelName}&vid={virtualId}
The two forms conflict and neither has been confirmed. Treat the token-based form as the
recommended shape and the channelName/vid form as legacy or publisher-specific. The host is
also a stage blob host; a branded production URL is not published. Confirm both before go-live.
Tracked in Open Items.
A working page
<!-- 1. Reserve the banner slot -->
<div id="cheggout-banner-container" class="my-8 min-h-[90px] w-full"></div>
<!-- 2. Load the widget for your channel -->
<script src="https://chegwidgetuat.z29.web.core.windows.net/cheggout-widget.iife.js?channelName=YOURCHANNEL"></script>
<script>
const ARTICLE_ID = "12345";
// 3. On login pages and home pages, check for unclaimed rewards.
async function onHomeLoad(virtualId) {
const authToken = await fetch("/api/cheggout/token").then(r => r.text());
window.CheggoutRewards.checkPendingRewardEvent(virtualId, authToken, "article_read", {});
}
// 4. When the reader finishes the article, offer a card — once.
async function onArticleComplete(virtualId) {
if (localStorage.getItem("cheggout_banner_clicked_" + ARTICLE_ID) === "true") return;
const isGuest = !virtualId || virtualId === "guest";
const authToken = isGuest ? "" : await fetch("/api/cheggout/token").then(r => r.text());
window.CheggoutRewards.bannerTriggerEvent(
isGuest ? "guest" : virtualId,
authToken,
"article_read",
"web",
{}
);
}
</script>
/api/cheggout/token is your endpoint. It authenticates the session, calls GetAuthToken with
your signing key, caches the result for its one-hour lifetime, and returns only the token.
Demo
A sample application demonstrating the scratch card functionality:
https://cheggoutsampleapp.z29.web.core.windows.net/news
Checklist
- Script tag carries the correct
channelNamein its query string. -
#cheggout-banner-containerexists on every page that should show a banner. - Tokens are minted server-side, never in the browser.
- Guest users pass
virtualId = "guest"and no token. - The banner flow is re-triggered after login with the real identity.
-
checkPendingRewardEventruns on home and landing pages after login. -
cheggout_banner_clicked_<ARTICLE_ID>is checked before re-triggering. - A production widget URL has been obtained from Cheggout.
Next
- Trigger events — which
eventTypevalues apply on web. - Lifecycle — what happens behind the overlay.
- Tokens — lifetimes and refresh behaviour.