JavaScript widget installation
Two things go on the page: the script and a container <div>. That is the whole client-side
install. Everything else is prerequisites on your side and a token endpoint on your backend.
Prerequisites
Complete these before adding the script. The widget will load without them, but it will not be able to do anything for a logged-in user.
| Prerequisite | Detail |
|---|---|
| Key exchange | Publisher and Cheggout exchange public key certificates. Cheggout needs yours to verify your signed token requests; you need Cheggout's to verify signed responses. |
| HTTPS | All communication happens over HTTPS. This includes the pages hosting the widget. |
| Request signing | Requests are signed using SHA256withRSA. This applies to the server-side token call your backend makes, not to anything running in the browser. |
| The script itself | Cheggout provides the JavaScript to be integrated into the publisher page, along with your channelName. |
| A token endpoint | Your backend must be able to mint a user token. See User states and tokens. |
Your RSA private key stays on your servers. The widget takes a bearer token that your backend minted; it never sees a key and never constructs a signature. If you find yourself putting a private key into JavaScript, stop — see Security best practices.
Step 1 — Add the script
<script src="https://chegwidgetuat.z29.web.core.windows.net/cheggout-widget.iife.js?channelName=YOURCHANNEL"></script>
The channelName query parameter is not decorative: the widget reads it from its own script tag's
src. There is no separate init() call and no configuration object. Get the query string wrong
and the widget loads but cannot identify your channel.
Replace YOURCHANNEL with the channel identifier Cheggout issued you. It is matched
case-sensitively and is the same value your backend sends as CHANNELID in the
signed envelope.
TBD The URL above is the only published distribution point, and it is a UAT blob host. There is no production widget URL. Do not ship it to production traffic. Confirm the production URL with Cheggout before committing to a launch date. Tracked in Open items.
Where to put it
Place the tag where your template loads third-party scripts. Because the widget only acts when you
call a trigger function, load order is not critical — but window.CheggoutRewards must exist before
your first call, so either load it synchronously in <head>, or guard your calls:
if (window.CheggoutRewards) {
window.CheggoutRewards.bannerTriggerEvent(/* ... */);
}
Step 2 — Add the container
Reward banners are injected into a container with an exact id:
<div id="cheggout-banner-container" class="my-8 min-h-[90px] w-full"></div>
| Attribute | Required | Why |
|---|---|---|
id="cheggout-banner-container" | Yes, exactly | The widget queries for this id. Any other id, and the banner has nowhere to render. |
class="my-8 min-h-[90px] w-full" | No, but recommended | Tailwind utilities giving vertical margin, a minimum height of 90px, and full width. Reproduce the same intent with your own CSS if you do not use Tailwind. |
The min-height matters more than it looks. Without a reserved height, the container is zero pixels
tall until the banner renders, and injecting the banner shifts your page content down — a layout
shift the user sees and your Core Web Vitals record.
Place the container where the banner should appear: at the end of article body copy for a reading-milestone reward, or near the confirmation message on a transaction page.
Step 3 — Trigger it
The widget does not watch your page. Nothing renders until you call a trigger function.
window.CheggoutRewards.bannerTriggerEvent(
virtualId, // "pub_8f14e45fceea167a", or "guest"
authToken, // bearer token from your backend
"article_read", // event type
"web", // device type
{} // additionalParams
);
Every function, its parameters and when to call it are in Trigger events.
TypeScript
If your site is TypeScript, declare the global so calls type-check. The declaration circulated with the integration material covers the legacy function:
declare global {
interface Window {
CheggoutRewards?: {
fetchScratchCard: (category: string, virtualId: string, trackerEvent: any) => void;
};
}
}
export {};
For new integrations you will also want the current functions. Extend the same declaration:
declare global {
interface Window {
CheggoutRewards?: {
bannerTriggerEvent: (
virtualId: string,
authToken: string,
eventType: string,
deviceType: string,
additionalParams?: Record<string, unknown>
) => void;
checkPendingRewardEvent: (
virtualId: string,
authToken: string,
eventType?: string,
additionalParams?: Record<string, unknown>
) => void;
/** @deprecated Legacy. Use bannerTriggerEvent for new integrations. */
fetchScratchCard: (category: string, virtualId: string, trackerEvent: any) => void;
};
}
}
export {};
The optional ? on CheggoutRewards is deliberate — the script may not have loaded when your code
runs, and the type should force you to handle that.
TBD No official type definitions package is published. The declarations above are written from the documented signatures; confirm them against the build you receive.
A complete page
<!-- In your template head or script block -->
<script src="https://chegwidgetuat.z29.web.core.windows.net/cheggout-widget.iife.js?channelName=YOURCHANNEL"></script>
<article>
<!-- your content -->
<!-- Where the reward banner appears -->
<div id="cheggout-banner-container" class="my-8 min-h-[90px] w-full"></div>
</article>
<script>
// Your own milestone logic decides when this fires.
function onArticleFinished(articleId) {
if (localStorage.getItem('cheggout_banner_clicked_' + articleId) === 'true') {
return; // already rewarded for this article
}
const virtualId = sessionStorage.getItem('vid') || 'guest';
const authToken = window.myApp.cheggoutToken || ''; // minted server-side
window.CheggoutRewards &&
window.CheggoutRewards.bannerTriggerEvent(
virtualId, authToken, 'article_read', 'web', {}
);
}
</script>
Caveats
There is no separate stylesheet
TBD An earlier integration email referenced a separate cheggout-widget.css
stylesheet <link>. Styles are bundled inside the script. Treat a separate CSS link as
legacy — do not add it. If a build you receive genuinely needs one, Cheggout will say so at handover;
confirm rather than guessing.
The widget owns its own visuals
You place the container and control its outer box — margin, width, position in the flow. You do not restyle the banner or the overlay. If you need visual control over the card itself, the widget is the wrong surface; use the headless API.
One container, one banner region
Only one #cheggout-banner-container should exist on a page. Duplicate ids are invalid HTML, and
the widget will find whichever the browser returns first.
Demo
A working sample application is available at:
https://cheggoutsampleapp.z29.web.core.windows.net/news
Compare its DOM against yours when a banner fails to appear — a missing container id is by far the most common cause.
Next
- Trigger events — the full function reference.
- User states and tokens — guest handling and the one-hour token.
- Interaction data — the localStorage keys the widget writes.