Getting Started
Everything needed to get the iFrame onto your page and working: the URL you build, the code that mounts it, and the events a complete integration handles.
All of it goes through the integration module — copy that file into your project first.
What happens when the page loads
The load sequence matters: the iFrame cannot build video-streaming headers or share links until you answer its sdkInit with client-url, and it cannot restore the right route until it receives iframe-navigation.
Steps 2 and 3 are what most incomplete integrations miss. If you implement nothing else, implement the handshake and login.click.
mount() sends iframe-navigation for you, but client-url is yours to send — and only if you subscribed before mounting, which is why the example below subscribes first.
The iFrame URL
You do not create the iframe element yourself — mount() does that, and applies the attributes the iFrame needs. What you build is the URL you pass to it:
https://{iframe-host}/{lang}/?jwt={jwt-token}| Parameter | Required | Description |
|---|---|---|
iframe-host | + | The iFrame host GR8 Tech provisions for you |
lang | + | Interface language as an ISO 639-1 code. Unsupported values fall back to the tenant’s default language, set during tenant setup. See Supported Languages |
jwt | ~ | Auth token issued by your own platform. Required only for players already authorized on your side |
sessionId | - | Player session identifier, if your transaction processing is session-based |
shareKey | - | Present when the player arrived through a shared bet link — see Sharing bet links |
The jwt token comes from an endpoint you implement yourself, at any URL you like — these docs refer to it as get-jwt-token. See Players Authorization for the token format and signing keys.
There is no need to receive and post a jwt-token when the end user is not authorized on your platform. The iFrame works for anonymous players — just omit the parameter.
https://demo-iframe-brand.gr8.tech/en/?jwt=tokenvaluehttps://demo-iframe-brand.gr8.tech/en/Session-based integration
When sessionId is present, it is included in the context field of bet placement transactions sent to your wallet endpoint:
https://demo-iframe-brand.gr8.tech/en/?jwt=tokenvalue&sessionId=player-session-123For how sessionId appears in transactions, see Perform Transaction — Context Object.
The complete integration
<div id="sportsbook"></div>import integration from "services/integration";
const IFRAME_HOST = "https://demo-iframe-brand.gr8.tech";
const LANG = "en";
// Where the sportsbook lives on your site, e.g. https://example.com/sports/en/...
const PREFIX = "sports";
async function mountSportsbook() {
// 1. Configure. Call before mount().
integration.init({
languages: [LANG],
prefix: PREFIX,
});
// 2. Subscribe before mounting, so nothing is missed during load.
const unsubscriptions = [];
unsubscriptions.push(
integration.on("sdkInit", ({ value }) => {
// Required: without client-url, video streaming and share links break.
integration.emit(
{ href: window.location.href, basePath: PREFIX },
"client-url",
);
// Apply the iFrame's default only if the player has no preference stored.
const stored = localStorage.getItem("prefers-color-scheme");
integration.helpers.toggleThemeMode(stored || value?.defaultThemeMode);
}),
);
unsubscriptions.push(
integration.on("login.click", () => openLoginModal()),
);
unsubscriptions.push(
integration.on("register.click", () => openRegisterModal()),
);
unsubscriptions.push(
integration.on("auth.fail", () => {
// The token was rejected inside the iFrame — re-issue it or log out.
handleAuthFailure();
}),
);
unsubscriptions.push(
integration.on("user.balance", () => refreshPlatformBalance()),
);
unsubscriptions.push(
integration.on("deposit.click", () => routeTo("/deposit")),
);
unsubscriptions.push(
integration.on("openLink", ({ url, target }) => window.open(url, target)),
);
// 3. Forward a share key, if the player arrived through a shared bet link.
const shareKey = new URLSearchParams(window.location.search).get("shareKey");
const query = new URLSearchParams();
const jwt = await fetchJwtFromYourBackend(); // null for anonymous players
if (jwt) query.set("jwt", jwt);
if (shareKey) query.set("shareKey", shareKey);
// 4. Mount. Sends the initial iframe-navigation for you.
integration.mount(
"sportsbook",
`${IFRAME_HOST}/${LANG}/?${query.toString()}`,
);
// 5. Tear down on unmount, so listeners don't stack up on re-render.
return () => unsubscriptions.forEach((sub) => sub.off());
}
mountSportsbook();When the player logs out of your platform, or their session expires:
integration.helpers.logout();When their balance changes outside the iFrame — a deposit, a bonus, a manual adjustment:
integration.emit({}, "get-user-balance");Sharing bet links
The client-url message above does double duty: the iFrame builds video-streaming request headers from your page’s href, and builds share-bet links from it. Without client-url, neither works.
Share links default to {origin}/sports/{lang}?shareKey=.... If the sportsbook is mounted on a custom path, basePath overrides the middle segment:
basePath values
basePath is the path segment or segments between your origin and the ?shareKey query parameter in the resulting link:
| You want the link to be | basePath |
|---|---|
https://example.com?shareKey=... | '' |
https://example.com/sportsbook?shareKey=... | 'sportsbook' |
https://example.com/casino/sportsbook?shareKey=... | 'casino/sportsbook' |
Omitting basePath keeps the default format, so existing integrations are unaffected.
Handling shareKey on load
The other half is reading shareKey off your own URL and passing it into the iFrame URL — step 3 in the example above. Without it the share link opens your page correctly, but the iFrame does not know which bet to display.
Before you call it done
-
init()is called beforemount(), and subscriptions are set up before mounting -
client-urlis sent in response tosdkInit -
login.clickandregister.clickopen something -
auth.failis handled — this is what tells you a token was rejected - Logout is sent when the session ends on your platform
-
shareKeyis forwarded into the iFrame URL, if you use share links - Subscriptions are torn down on unmount, so listeners don’t stack up on re-render
- The target origin in your copy of
integration.jsis your iFrame’s origin, not'*'
Next steps
- Integration Module — the full
init/mount/on/emit/helpersreference - Navigation & Routing — keep your URL and the iFrame’s route in sync
- Theme Mode — light and dark switching
- Balance & Currency — balance updates and currency switching
- All Events — every event, both directions, with exact payloads