postMessage protocol between your page and the Moda iframe.
The beta protocol version is
2026-05-27. Always send and validate the channel, version, and sessionId fields.There are two version numbers, and they are not the same. They live in different layers and are set in different places:
They version independently and will diverge further over time. The iframe silently drops any message whose
version doesn’t match the protocol version, so a transposed value fails with no error — send 2026-05-27 in the envelope, and 2026-05-01 in the API header. See Session API for the header.Message envelope
Every command and event uses the same envelope:requestId to correlate command responses.
Minimal helper
moda-embed-client.ts
iframe you pass in needs the right capability attributes — clipboard paste and the file picker for image upload depend on them:
a.click() on the returned dataUrl), so the iframe needs no downloads permission. See Security and Production for the full rationale and CSP guidance.
Handshake and lifecycle
There are two distinct milestones, signalled by two different events:- Session ready — the iframe posts
readyonce, as soon as it has loaded its session. Payload is minimal:{ canvasId, mode, externalUser? }. After this you can send session-scoped commands (save,chat.send,setReadonly). - Canvas interactive — the canvas finishes loading and rendering separately. This is signalled by
canvas:changedwith{ status: 'loaded', page, pageCount, category, pages }(and an initialpage:changed). Gate page/export/edit commands (focusPage,export,insertText,insertImage) on this, not onready.
ready does not fire a second time with page info — page state arrives via canvas:changed/page:changed. (You can get a ready carrying page state, but only as a reply to host.hello — see below.)
Use host.hello to win the load race
The iframe’s first ready can fire before your page has attached its message listener (a classic iframe-load race). host.hello is the robust fix: the iframe replies with ready every time it receives one, and that reply includes the current page state if the canvas is already interactive. Poll it until you get a ready, then stop:
Commands
Save
canvas:savingcanvas:savedcanvas:save_error
Export
flatten: true to rasterize every
page instead — useful when a canvas uses a custom font whose glyphs must be reproduced
exactly, at the cost of selectable text and a larger file:
export:completed returns the file inline as a base64 dataUrl — the host triggers the download, the iframe never does:
Focus a page
page:changedwith{ page, pageCount, category, pages }
Toggle readonly
Insert text
Insert image
UseFile or Blob when the user picks or pastes a local image. The iframe uploads it using its scoped embed browser credential.
Embed uploads currently accept PNG, JPEG, GIF, and WebP files up to 25 MB. SVG uploads are not accepted in embedded sessions.
url when your app already has an HTTPS image URL that Moda can render.
Chat modes
Forui.chat: "inside", the iframe renders Moda’s hosted chat panel. Your app does not need to send chat commands, but it can still listen for chat:* events for analytics, logging, or mirrored UI.
For ui.chat: "external", your app owns the chat UI and sends prompts to the Moda agent.
chat:message — the one that matters
Each chat:message is a complete, self-contained message appended to the conversation — not an incremental delta. The text lives in text, and the author in sender. Do not accumulate or diff these; render each as its own bubble keyed by id.
chat:progress and chat:tool_call, never as chat:message deltas.
All chat events
Refresh session
The iframe proactively emitssession:refresh_requested ahead of expiry (by default 60
seconds before the browser token lapses), so you can refresh in response instead of
running your own timer:
session:expired (or session:revoked)
so you can surface a re-authentication prompt.
Events
Keyboard and paste behavior
When the iframe has focus, Moda handles normal editor shortcuts:- Undo/redo
- Copy, cut, paste
- Pasted image files and screenshots
- Delete, duplicate, group, ungroup, arrange
- Arrow nudging
- Tool shortcuts like
V,T,R,O,L, andP
save, export, or chat.send.