Iframe Protocol
The exact postMessage contract between the Upside parent shell and an embedded game iframe.
Iframe Protocol
Upside embeds approved games in an iframe and exchanges a small set of browser messages with that iframe. The protocol is intentionally narrow: parent-to-game messages provide player context, and game-to-parent messages request UI refreshes or parent-shell feedback.
Treat postMessage data as untrusted until event.origin matches an expected Upside origin. Never accept a player token from *, from localhost, or from another arbitrary site.
Parent To Game
Initial Context
When the game iframe is available, Upside sends a context object to the iframe's origin.
tstype UpsideContextMessage = { balance: string; token: string; locale: string;};
| Field | Required | Description |
|---|---|---|
balance | Yes | Estimated WIN balance in atomic units. WIN uses 18 decimals. |
token | Yes | Player JWT. Forward it to your backend as Authorization: Bearer {token}. |
locale | Yes | Current locale. Upside also appends it to the iframe URL as ?locale=.... |
Locale Change
tstype LocaleChangeMessage = { type: "localeChange"; locale: string;};
Use this to update copy, number formatting, and any localized game assets without forcing a full iframe reload.
Game To Parent
Ready
tswindow.parent.postMessage({ type: "ready" }, "https://upside.win");
Send this after your app has mounted and registered its message listener. Upside responds by sending the latest context snapshot.
Win Modal
tswindow.parent.postMessage( { type: "showWinModal", wins: "2000000000000000000", }, "https://upside.win",);
Opens the standard Upside win modal. The optional htmlContent field can customize modal body content.
Loss Modal
tswindow.parent.postMessage( { type: "showLossModal", loss: "1000000000000000000", }, "https://upside.win",);
Opens the standard Upside loss modal. Use this after the backend has settled the session with processPayout.
Custom Modal
tswindow.parent.postMessage( { type: "showCustomModal", htmlContent: "<p>Round complete</p>", }, "https://upside.win",);
htmlContent must be a string. Keep it generated from trusted server-rendered templates or safe static content.
Toast
tswindow.parent.postMessage( { type: "showToast", message: "Game saved", description: "You can resume this session later.", variant: "success", duration: 4000, }, "https://upside.win",);
Supported variant values are success, error, warning, and info. Unknown variants fall back to the default toast.
Refresh Requests
tswindow.parent.postMessage({ type: "refetchBalance" }, "https://upside.win");window.parent.postMessage({ type: "refreshMatchHistory" }, "https://upside.win");
Call these after settlement, not before. If a backend payout fails, show an error instead of asking the parent to refresh stale data.
Message Matrix
| Direction | Message | Required Fields | Typical Timing |
|---|---|---|---|
| Game -> parent | ready | type | App mounted and listener registered |
| Parent -> game | context snapshot | balance, token, locale | Initial load, balance/token updates |
| Parent -> game | localeChange | type, locale | Player changes locale |
| Game -> parent | showWinModal | type, wins | Backend settled a winning session |
| Game -> parent | showLossModal | type, loss | Backend settled a losing session |
| Game -> parent | showCustomModal | type, htmlContent | Trusted custom result or explainer |
| Game -> parent | showToast | type, message | Non-blocking status feedback |
| Game -> parent | refetchBalance | type | After placeBet or processPayout success |
| Game -> parent | refreshMatchHistory | type | After processPayout success |
Safe Handler
tsconst UPSIDE_ORIGINS = new Set(["https://upside.win", "https://www.upside.win"]);window.addEventListener("message", event => { if (!UPSIDE_ORIGINS.has(event.origin)) return; const data = event.data ?? {}; if (typeof data.token === "string") { // Store the token only in memory and send it only to your backend. } if (data.type === "localeChange" && typeof data.locale === "string") { // Update translations and number formatting. }});
Common Mistakes
Do not use * when a message includes or requests authenticated player state. Send messages to https://upside.win and accept messages only from known Upside origins.
The player-facing result should follow backend settlement. If processPayout fails, show an error toast and retry or reconcile the session.
Client events can be modified. Treat the iframe as a display and input surface; final outcome generation and payout calculation belong on the backend.
htmlContent is powerful and should be reserved for trusted templates. Never pass player-submitted HTML directly into parent-shell modals.