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.
Envelope
Section titled “Envelope”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.
Origin rules
Section titled “Origin rules”| 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.
Events
Section titled “Events”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. |
Commands
Section titled “Commands”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). |
Error codes
Section titled “Error codes”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. |
Versioning
Section titled “Versioning”vis 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);v1keeps 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.