Skip to main content
Embedded Cube surfaces communicate with your host page over the browser postMessage API, in both directions:
  • Events (cube:event:*) travel embed → host. Subscribe to them to learn how a viewer interacts with the embed — when it loads, what they view, download, drill into, search for with AI, and any errors they hit. Feed them into your own analytics / event bus.
  • Actions (cube:action:*) travel host → embed. Send them to drive the embed from your app — switch the color scheme, set a filter, navigate, or refresh the data.
No SDK is required — it’s plain window.postMessage and a message listener.

Message envelope

Every message — in either direction — is a single object with the same shape:

Listening to events

Attach a single message listener to window. Always validate event.origin against your tenant’s origin and check data.source === "cube-embed" before trusting a message.

Event catalog

Every event payload is also delivered with the envelope’s surface field, so you can always tell which surface (dashboard, app, or chat) it came from — including AI queries, which report app when run inside the embedded app and chat on the standalone chat surface.

cube:event:ready

Emitted once per session, as soon as the embed authenticates and mounts. The handshake — the first event you receive, and the moment to record an “embed opened”.

cube:event:view

Emitted when a surface is viewed — on the initial load and again whenever the viewer navigates within the embed.

cube:event:navigate

Emitted when the viewer navigates within the embed (a route change). Use it to mirror the embed’s location in your own router or analytics.

cube:event:dashboard-loaded

Emitted when a dashboard has finished rendering all of its widgets — the “fully painted” signal (distinct from ready, which fires at mount, before data loads). For an app, it is emitted once the app has loaded, or once it shows an error. The payload does not say whether the app loaded or failed. For an app, the payload carries publicId, and widgetCount is 0 because an app has no widgets.

cube:event:download

Emitted when a viewer exports something — a widget’s data as CSV, or a widget as a PNG or PDF image. Reports that an export happened and its shape — never the exported rows themselves.
A dashboard’s download actions — per-widget (CSV, PNG, PDF, and a table’s print version) and whole-dashboard (PNG, PDF, print version) — only appear when the embed URL includes allowExport=true (see Dashboards → Allow chart and dashboard export). The event fires when a viewer uses one of them. It reports that the viewer asked for a file, not that the browser saved it: a sandboxed iframe without allow-downloads drops the file, and the event still fires.

cube:event:drilldown

Emitted when a viewer drills into a measure (clicks a chart mark or table cell to see its detail rows).

cube:event:ai-query

Emitted around an AI / natural-language query — capturing what the viewer asked and the lifecycle stage. Fires wherever AI chat is used: the standalone chat surface, the dashboard agent, and the embedded app.

cube:event:session-expiring

Emitted once per signed embedding session, ~30 minutes before it stops working. This is the moment to mint a replacement session and push it in with cube:action:set-session — see Keeping a signed session alive below.
Only fires for signed embedding sessions — a private-embedding iframe has no expiring session to renew. It’s a best-effort timer inside the iframe: a hidden or suspended tab can throttle it, so it may arrive late (immediately on wake) or after cube:event:session-expired.

cube:event:session-expired

Emitted when a signed session has actually lapsed. The embed can’t recover on its own — there’s no refresh token or re-exchange endpoint — so nothing happens until you push a fresh session in with cube:action:set-session.

cube:event:error

Emitted when the embed surfaces an error (a render error, a query failure, an auth/session problem). fatal distinguishes an error that took the whole surface down from a recoverable one.

Sending actions

Send actions into the embed by posting a message to the iframe’s contentWindow. Always target your tenant’s origin (not "*") so the message can’t leak to another document if the iframe navigates away.

Action catalog

cube:action:set-color-scheme

Switch the embed’s color scheme at runtime.

cube:action:set-theme

Apply a brand theme (colors, fonts) to the embed at runtime. The payload is an embedTheme object — the same shape the Generate Session API accepts. Common fields are primaryColor, borderRadius, and font; see App customization for the full list.

cube:action:set-locale

Switch the embed’s UI language. Accepts a full code (es-ES), a short code (es), or a regional variant. See Localization for the list of supported languages and the other ways to set the language.

cube:action:set-timezone

Switch the time zone the embed’s queries run in — which day a row falls into, and what today means. Takes precedence over the ?timezone= URL parameter and the account default. See Time zones for the other ways to set it.

cube:action:set-filter

Push a filter into a dashboard or an app. The filterUrlParameter is the same f_<semantic_view>.<dimension>=<JSON> form used to pre-set filters via URL, so you can capture a viewer’s filters and restore them later. Each action replaces all filter parameters in the embed URL with the ones it sends. On a dashboard, a control whose filter a later action leaves out keeps its current value; to clear a control, send its filter with {"value":[]}. The same action filters an app. Cube applies the filters to every report that the app reads, and a report that cannot take a filter fails to load. Unlike a dashboard, an app keeps only the filters from the latest action, so a filter that the action leaves out is removed. Send an empty filterUrlParameter to remove them all.

cube:action:navigate

Navigate the embed to an in-embed path.

cube:action:refresh

Re-run the embed’s queries and refresh its data. An app reads its reports again. No payload.

cube:action:set-session

Hand the embed a fresh signed embedding session id, replacing the one it’s running on without reloading the iframe. Sent before the current session lapses, the swap is invisible — nothing unmounts, so the viewer keeps their place, filters, and any unsaved editing state. Sent after it has already lapsed, it still recovers the embed without a reload, but the surface has been torn down by then and transient state is gone.
A rejected id (unknown, already redeemed, or expired) doesn’t tear down a working embed. It’s reported via cube:event:error with context: "session-renewal" and name: "EmbedSessionExchangeError", so you can filter for it and retry.

Keeping a signed session alive

A signed embed’s token is usable for about 23 hours (see Session lifecycle), unless revoked sooner, and can’t refresh itself — left alone, a tab open that long drops to a “Session expired” message. Renew it in place instead. If you’re using @cube-dev/embed-sdk, call autoRenewSession on the connection instead of hand-rolling the listener below — it drives renewal from all three triggers (both events plus the tab returning to the foreground, which catches a renewal that timers missed while the tab was hidden), and collapses concurrent triggers into one in-flight renewal:
mintSession is your own backend endpoint, calling Generate Session — an API key that must never reach the browser. Each id is single-use and expires 5 minutes after minting, so mint a fresh one on every call rather than caching one ahead of time. Without the SDK, drive the same renewal by hand from the raw events and action:

Ending a session on logout

Letting the token expire isn’t the same as logging the viewer out — for that, revoke the embed session from your own logout handler. Every renewal mints a new session id, so track each one you’ve minted for that viewer and revoke all of them; an earlier one may still be a live, unexpired token. Revoking doesn’t tear down a running iframe by itself — remove it as you normally would on logout.

Surfaces

Events come from one of three customer-facing surfaces, reported in the envelope’s surface field:

Security

  • Always validate event.origin against your tenant’s origin in your message listener, and check data.source === "cube-embed". Never act on a message that fails either check.
  • Target your tenant’s origin when sending actions (iframe.contentWindow.postMessage(msg, CUBE_ORIGIN)), not "*", so an action can’t be delivered to an unexpected document.

Complete example

A minimal host page that loads a signed dashboard embed, logs every event, and exposes buttons to drive it. Generate the session on your backend with the Generate Session API — see Signed embedding for the full flow.