Appearance
postMessage Events
The embedded sportsbook talks back to your page. As things happen inside the frame — the app finishes booting, a player places a bet, an action needs login — the iframe posts a message to the parent window so your site can react: show your login UI, open your deposit flow, refresh a balance, or re-launch an expired session.
You subscribe with a single message listener on the parent window. There is nothing to construct and nothing to send back — these are one-way UI signals from the frame to you.
Signals, not authentication
These events are UI hints, not a source of truth. Use them to drive your interface; rely on your own backend and session for anything that moves money or grants access. See Security below.
The envelope
Every message the iframe posts to window.parent has the same shape:
js
{ source: 'oddyne-sportsbook', type, payload }source— always the string'oddyne-sportsbook'. Guard on it so you ignore unrelated messages on the same window.type— which event fired (see the table below).payload— event-specific data, or{}when the event carries none.
Listening on the parent page
Add one listener and switch on e.data.type. Always confirm e.data?.source first:
js
window.addEventListener('message', (e) => {
if (e.data?.source !== 'oddyne-sportsbook') return;
switch (e.data.type) {
case 'ready': /* iframe booted */ break;
case 'betPlaced': /* e.data.payload = { ticketId, stake, currency, totalOdds, potentialWin } */ break;
case 'loginRequired': /* show your login */ break;
case 'deposit': /* open your deposit flow */ break;
case 'balanceUpdate': /* e.data.payload = { balance, currency } */ break;
case 'sessionExpired': /* re-launch / re-auth the player */ break;
}
});Events
type | payload | When it fires |
|---|---|---|
ready | {} | The sportsbook has loaded and finished booting. |
betPlaced | { ticketId, stake, currency, totalOdds, potentialWin } | A bet was placed successfully. |
loginRequired | {} | The player tried an action that needs authentication while not logged in — show your login UI. |
deposit | {} | The player requested a deposit — open your deposit flow. |
balanceUpdate | { balance, currency } | The displayed player balance changed. |
sessionExpired | {} | The player token expired — mint a fresh launch token and re-load, or prompt re-login. |
betPlaced payload
| Field | Type | Notes |
|---|---|---|
ticketId | integer | The placed ticket id. |
stake | number | The staked amount. |
currency | string | Currency of the stake. |
totalOdds | number | Combined decimal odds for the ticket. |
potentialWin | number | Potential payout at those odds. |
balanceUpdate payload
| Field | Type | Notes |
|---|---|---|
balance | number | The player's new displayed balance. |
currency | string | Currency of the balance. |
Which events you get
ready,betPlaced,loginRequired, andsessionExpiredare emitted by default — you can rely on them in any integration.depositandbalanceUpdatefire only when the matching affordance or flow is enabled in your integration. If you have no deposit affordance, you won't seedeposit; if balance display isn't wired for your setup, you won't seebalanceUpdate.
Security
For v1 the events are broadcast to the parent with targetOrigin: '*', meaning any parent window hosting the frame receives them. Do not treat event data as authenticated. Use the events as UI signals only, and rely on your own backend and session for anything sensitive — balances, entitlements, and money must be verified server-side, never taken from a postMessage payload.
Always guard the source
Other scripts and embeds on your page may post their own messages to the same window. The if (e.data?.source !== 'oddyne-sportsbook') return; guard is required — handle only messages whose source is 'oddyne-sportsbook'.
A per-operator origin restriction — scoping delivery to your registered parent origin instead of '*' — is planned; see the Roadmap.