Skip to main content

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

ComponentBehaviour
Reward bannerInjected into a container you designate, when a user completes a milestone
Scratch card overlayA modal scratch experience launched from the banner
Reward claimed stateThe revealed reward, rendered by the widget
Interaction dataWritten to localStorage so you can avoid re-triggering
The published script is a UAT artefact

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

RequirementNotes
Public key certificates exchangedBoth directions, as for every Cheggout integration
HTTPS everywhereRequired
Requests signed with SHA256withRSAFor the server-side token call. See Signed envelope
The widget script, provided by CheggoutPer channel
A virtualId per logged-in userYours, stable and immutable
A campaign configured for your channelOtherwise 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.

A separate stylesheet is not required

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
);
ParameterTypeRequiredDescription
virtualIdstringYesYour unique user id, for example sessionStorage.getItem("vid")
authTokenstringFor logged-in usersBearer token generated for the user session
eventTypestringYesEvent identifier, for example "article_read"
deviceTypestringYes"web", "mobile"
additionalParamsobjectNoFor 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.

Fire on 100% reading progress
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.

EnvironmentURL
Productionhttps://couponapi.cheggout.com/api/GetAuthToken
UAThttps://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.

Never mint tokens from the browser

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.

KeyTypeMeaning
cheggout_banner_clicked_<ARTICLE_ID>BooleanThe user already clicked the reward banner for that article
Card stateJSON arrayPer-card isSeen and claimed flags
Sample card state
[
{ "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 channelName in its query string.
  • #cheggout-banner-container exists 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.
  • checkPendingRewardEvent runs 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 eventType values apply on web.
  • Lifecycle — what happens behind the overlay.
  • Tokens — lifetimes and refresh behaviour.