Host ↔ iframe messaging
The iframe and your page communicate with window.postMessage. Every message is
an object with a source and a type. The iframe only posts to the origin you
pass as the parent URL param, and only accepts messages whose source
identifies your host handshake.
Setup
const FRAME = document.querySelector('#lootbox-solutions-iframe');
window.addEventListener('message', (e) => { if (e.origin !== 'https://{operator}.app.lootboxsolutions.com') return; // verify sender if (e.data?.source !== 'lootbox-solutions') return; handle(e.data);});
// to send a command into the iframe:function send(msg) { FRAME.contentWindow.postMessage({ source: 'host', ...msg }, 'https://{operator}.app.lootboxsolutions.com');}iframe → host
Messages the iframe emits (source: 'lootbox-solutions'):
type | Payload | Meaning |
|---|---|---|
ready | {} | Game app mounted and listening. Safe to send commands. |
wallet:balance-changed | { balanceMinor, balanceFetchedAt, currency } | Balance moved; refresh your own balance UI. |
play:auth-required | { boxId, priceMinor, currency } | A guest tried to act; sign them in, then upgrade in place with authenticate (or re-launch). |
session:authenticated | { playerExternalId } | A guest session was upgraded in place after an authenticate command — now logged in, no reload. |
play:insufficient-balance | { boxId, priceMinor, balanceMinor, currency } | Player lacks funds; offer a top-up. |
navigation:changed | { path, query } | The player navigated; mirror it into your address bar / persist for refresh. |
box:card-clicked | { boxId } | A box card was tapped in the widget. Navigate the parent to that box’s page — don’t navigate inside the iframe. |
box:card-purchase | { boxId } | ”Buy now” on a card. Navigate the parent to the box page and launch with autoOpen so the box is bought + played on arrival. |
resize | { height } | Content height changed; resize the iframe if you size to content. |
host → iframe
Commands your page sends in (source: 'host'):
type | Payload | Effect |
|---|---|---|
authenticate | { launchToken } | Upgrade a guest (or re-authenticate) in place — the game app exchanges the token and switches to the authenticated session without reloading the iframe. See Convert without a reload. |
theme | { appearance: 'light' | 'dark' } | Mirror your light/dark mode into the game app, no reload. |
navigate | { path } | Move the game app to a page (e.g. a box, inventory). |
locale | { locale } | Change language on the fly. |
currency | { currency } | Change display currency on the fly. |
Example: keep the URL in sync
function handle(msg) { switch (msg.type) { case 'navigation:changed': // reflect iframe location in the host URL so refresh/share works history.replaceState(null, '', `#/boxes/${msg.query.boxId ?? ''}`); sessionStorage.setItem('gc:where', JSON.stringify(msg)); break; case 'wallet:balance-changed': renderBalance(msg.balanceMinor, msg.currency); break; case 'play:auth-required': promptSignIn().then(() => relaunchAt(msg.boxId)); break; }}Security notes
- Always check
e.originagainst your operator host ande.data.source. - Pass
parent=<your-exact-origin>so the game app scopes its outbound messages to you; messages are never broadcast to*. - Your origin must also be in the allowed embedding origins, or the browser won’t load the frame at all.