Skip to content

postMessage reference

The embed SDK implements this protocol for you. Read on if you integrate without the SDK, or want to know exactly what crosses the frame boundary. TypeScript definitions ship in @bookmaker/embed-sdk/protocol.

Every message is a plain object:

// iframe -> your page (events)
{ source: 'bookmaker-sportsbook', v: 1, type: 'balance_changed', payload: { balance: 49500, currency: 'USD' } }
// your page -> iframe (commands)
{ source: 'bookmaker-host', v: 1, type: 'navigate', payload: { path: '/live' } }

payload is always an object ({} when there is nothing to say). Amounts are integers in minor units.

Direction Rule
iframe → page Events are posted only to the parent’s origin, after it has been checked against the tenant’s embedOrigins (or is the sportsbook’s own origin). Never '*'. Nothing is posted before that check, not even ready.
page → iframe Commands are accepted only from the parent window (event.source === window.parent) at that verified origin, with source: 'bookmaker-host'. Anything else is ignored silently.
Your listener Accept a message only if event.source is the sportsbook iframe’s contentWindow, event.origin is exactly the sportsbook origin, source is 'bookmaker-sportsbook' and v is 1.
Your commands iframe.contentWindow.postMessage(message, '<sportsbook origin>'), with the exact origin as targetOrigin.

How the iframe knows its parent: location.ancestorOrigins where the browser provides it (Chromium, Safari); otherwise the origin launch parameter the SDK adds, or the origin in document.referrer. A page that lies about its origin gains nothing: the browser only delivers events to a parent that really has the claimed origin, and frame-ancestors already refuses to render the sportsbook in a page that is not allowed.

Sent by the iframe. ready is always first; events that happen before it (for example a refused launch token) are queued and sent right after it.

Event Payload When
ready { version: 1, tenant, locale, loggedIn: boolean, path } The sportsbook verified your origin and listens for commands. Once per page load.
resize { height } The document height in CSS pixels changed. The SDK sizes the iframe from it in auto mode.
route { path, title } The player navigated. title is localised, e.g. Flamengo x Palmeiras, In-Play. An event page reports again once its title is known.
login_required { reason: 'place_bet' | 'my_bets' | 'login' } An anonymous player pressed Log in to bet, opened My Bets, or pressed Log in. Open your login, then send set_launch_token.
session_expired { reason } The session ended on the server: idle_timeout, expired, mfa_stale, logout (ended elsewhere), account_blocked or self_exclusion; or a launch token from the URL was refused (launch_token_used, launch_token_expired, launch_token_invalid, launch_failed). The player is anonymous until you send a new launch token.
balance_changed { balance, currency } The wallet balance is known or changed: after login, after every debit and credit the platform makes, and in answer to refresh_balance.
bet_placed { betId, stake, potentialPayout, currency } A bet was accepted. One event per bet when a slip places several.
bet_settled { betId, status, payout, currency } A bet reached a final status while the iframe was open: won, lost, void, cashed_out or cancelled. Sent again if a resettlement changes it.
deposit_requested {} The player pressed Deposit. Open your cashier, then send refresh_balance.
error { code, message } See error codes.

Sent by your page. Commands that arrive before ready are not processed: wait for it (the SDK queues them for you).

Command Payload Effect
set_launch_token { launchToken } Exchanges the token and logs the player in, or replaces an expired session, without a reload. The bet slip is kept.
navigate { path } Goes to a deep link: /, /live, /live/{sportId}, /sport/{sportId}, /competition/{id}, /event/{eventId}, /my-bets.
set_locale { locale } en or pt-BR (case-insensitive). Re-renders in place.
refresh_balance {} Re-reads the balance from your wallet and answers with balance_changed.
logout {} Ends the sportsbook session on the server and in the iframe. No session_expired follows (unless the session had already ended).
code Meaning
launch_token_invalid Unknown, malformed or for another tenant.
launch_token_used Already exchanged: launch tokens are single use.
launch_token_expired Older than 120 seconds.
launch_failed The token could not be exchanged (network or server error).
invalid_command Unknown command type, or a missing or invalid payload field.
invalid_path Not a sportsbook deep link (from navigate or the path parameter).
unsupported_locale Not en or pt-BR (from set_locale or the locale parameter).
unsupported_version The command’s v is not a version this sportsbook speaks.
command_failed A command could not be completed, e.g. the language pack for set_locale failed to load.
  • v is the protocol’s major version. It only changes for breaking changes, and a new major version comes with a new SDK URL (/embed/v2.js); v1 keeps working.
  • Within v1, new events, new commands and new optional payload fields can appear. Ignore event types and fields you do not know. The SDK does.
  • Payload fields are never removed or retyped within v1.
  • Changes are listed in the changelog.