Widget lifecycle and error handling
This article is for developers who embed a Drimify experience with the HTML integration code (widget) and need to react to what happens inside it: show a loader, detect errors, or track which screen the participant is on.
For setup and the full list of callbacks, see Advanced integration of the HTML integration code (widget) via the API.
Event order when the widget loads
When you call widget.load(), events always arrive in this order:
onReadyfires once, as soon as the experience has loaded its configuration (texts, theme, layout). This happens before the first screen is requested.onRouteChangefires each time a screen is displayed, starting with the first one.
What this means in practice:
onReadydoesn't mean the first screen is visible. The firstonRouteChangemarks that.onErrorcan still fire afteronReady, for example if the first screen fails to load.- If the experience can't start at all,
onReadynever fires. You receiveonErrorinstead (see below).
onReady for "the experience has started". Use the first onRouteChange for "the participant can see content".Screen names (onRouteChange)
Every onRouteChange event has this shape:
{
type: "routechange",
data: {
screen: "screen_<name>-<APP_ID>[-<suffix>]",
errorCode: "ERR_xx" // only on some unavailability screens
}
}
Examples of regular screens:
screen_start-12345screen_end-12345- Loyalty Hub:
screen_loyalty-hub-<tab>-12345,screen_loyalty-hub-reward-12345-<reward_id>
Match screens by their prefix (for example event.data.screen.startsWith('screen_end-')) rather than the full value: the suffix can change.
Screens that mean the participant can't play
These screens are delivered as a normal onRouteChange, not as an error. The experience is working, but it shows the participant a message instead of the game.
Screen name starts with | Meaning |
|
|---|---|---|
| The experience isn't available at this time |
|
| The participation quota has been reached |
|
| The experience has no question to display |
|
| The game isn't available in the selected language | — |
| The participant has already played (limit per IP address or per session) | — |
| Access control: this | — |
| Access control: this | — |
| Blocked by country, IP address or URL parameter | — |
| JWT authentication failed | — |
| A form or login was refused (blocked email domain, restricted list, already used, invalid format) | — |
| Loyalty Hub: the participant is sent back to the hub (entry refused, or the normal return after a play) | — |
Errors (onError)
onError is used when the experience can't start or can't recover. It fires once. The error code is in event.data.message.
Code | Meaning |
|---|---|
| The participation quota was already reached when the page loaded |
| Security error: the reserved |
| The experience isn't published |
| The experience needs a premium option that isn't included in the plan |
| A |
| The |
| The SSO (SAML or OAuth) login failed |
| The experience isn't available in the requested language |
| The link doesn't exist (unknown or deleted experience) |
| The SSO configuration is invalid |
| A Combo™ or Dynamic Path™ has no game assigned |
onError can also fire without an ERR_xx code, for example when a screen fails to load because of a network error after the experience has started.
widget.onError = function (event) {
var code = event.data && event.data.message;
switch (code) {
case 'ERR_09':
case 'ERR_10':
// session_uid missing or refused
break;
case 'ERR_13':
case 'ERR_17':
// SSO problem
break;
default:
// any other fatal error
break;
}
};
widget.onRouteChange = function (event) {
if (!event.data) {
return;
}
if (event.data.errorCode) {
// ERR_02, ERR_03, ERR_04, ERR_16, ERR_18 or ERR_19
}
if (event.data.screen.startsWith('screen_restriction-')) {
// access refused
}
};
Session unique ID (session_uid)
- A
session_uidhas no expiry date. An unknownsession_uidis treated as a new participant, not as an error. - If access control is based on
session_uid: - a value that isn't on the list gives
ERR_10throughonError, or thescreen_restriction-<id>-sessionscreen; - a value that has already been used gives the
screen_restriction-<id>-session_uniquescreen throughonRouteChange.
- a value that isn't on the list gives
- If participation is limited to once per session: a reused
session_uidgives thescreen_alreadyplayed-<id>-sessionscreen throughonRouteChange. - When SSO is enabled, the participant is identified by their SSO account and any
session_uidpassed in the URL is ignored.
Loading state and timeout
We don't publish guaranteed load times: they depend on the experience, its media, and the participant's device and network. To build a loading state:
- Start a timer when you call
widget.load(). - Treat the first
onRouteChangeas "the experience is visible" andonErroras a definitive failure. Either one clears the timer. - Keep a timeout as a safety net. It covers the case where neither event arrives, for example when the frame itself can't load (network failure, content blocked by the browser). Measure typical load times in your own environment and set the value from those, with a generous margin for slow mobile connections.
var timer = setTimeout(showFallback, TIMEOUT_MS);
widget.onRouteChange = function (event) {
clearTimeout(timer);
hideLoader();
};
widget.onError = function (event) {
clearTimeout(timer);
showError(event.data && event.data.message);
};
widget.load();
Updated on: 27/10/2026
Thank you!
