Available on Premium and above plans.
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 singlemessage 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’scontentWindow. 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.
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’ssurface field:
dashboard— a published dashboard.app— the Creator-mode app (workbooks, folders, in-app dashboards). AI chat run inside the app reportsapp.chat— the standalone analytics chat.
Security
- Always validate
event.originagainst your tenant’s origin in yourmessagelistener, and checkdata.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 thesession on your backend with the
Generate Session API — see
Signed embedding for the full flow.