embed_url to your frontend. The URL contains a short-lived boot token; the iframe exchanges it for a narrower browser credential before calling canvas, asset, export, or chat endpoints.
Create an embed session
Moda-Version: 2026-05-01 is the REST API version. It is not the same as the browser protocol version: 2026-05-27 you send in postMessage envelopes — see the Browser SDK version note. They version independently.Request
Response
403.
Request fields
Modes
UI options
chat controls where the Moda agent chat appears:
"hidden"disables agent chat for the embed session."inside"renders a Moda-hosted chat panel inside the iframe."external"enables the agent runtime but leaves the chat UI to your application through postMessage commands and events.
Recommended presets
Origin validation
allowed_origins must contain origins only: scheme, host, and optional port. Do not include paths.
Refresh a session
Refresh rotates the session boot token for an existing session and returns a fresh browser token for the iframe. Call this from your backend before expiry, then pass the new values to the iframe withsession.refresh.
Response
session:refreshed with { expiresAt }. The iframe also proactively warns you before expiry by posting session:refresh_requested (by default 60 seconds before expiry), and posts session:expired if the token lapses — refresh in response to the warning (see Refresh long-running sessions).
Revoke a session
Call revoke when the user closes the embedded editor, signs out, or loses access in your product.Error codes
Every public API error — including session create/refresh/revoke and the internal iframe endpoints — returns the same envelope:code categories:
Expired, revoked, and invalid-token failures surface as
401 / authentication everywhere except the refresh endpoint — on the boot-token exchange (GET /v1/embed/sessions/{session_id}?token=…) as well as on the browser token; today they are distinguished only by the human-readable message ("Embed session expired", "Embed session revoked", and an invalid-token string that differs by lane — "Invalid embed browser token" on the browser credential, "Invalid embed session token" on the boot exchange). Branch on status for recovery (re-mint on 401, surface a permission error on 403), and treat message as non-stable display text. Finer-grained machine codes are a known beta limitation.The refresh endpoint is the exception, and the distinction there is load-bearing: 409 means the session is dead but its record survives, 410 means stop asking about this id entirely, and 429 is the only one of the three worth retrying. A generic “retry on 4xx” policy will loop forever on 404 and 410.Read error.retryable first. It is the server’s explicit contract for that code — false means retrying the same request cannot succeed, true means the condition is transient — and it is correct for codes this table has never heard of. Do not derive retryability from the status when the field is present.It is not on every response, and whether it appears is a property of the code, not of how the error is raised: the field is sent only for codes whose registry entry declares it, and omitted entirely otherwise (null keys are stripped from the envelope, so it is absent rather than null). On this lane embed_session_gone and canvas_crdt_state_corrupt send false and rate_limited sends true, while not_found, conflict, authentication, permission, upstream_error and collab_active send nothing — including the typed collab_active, which is why “typed” is not the test. Expect the fallback below to be the usual path here rather than the exception. When retryable is absent the code’s retryability is context-dependent; branch on status and code then, remembering the mapping is not 1:1 and the table above is the current set rather than a closed one. 409 alone carries conflict, collab_active and canvas_crdt_state_corrupt, and they disagree about retrying. For an unrecognised 4xx code, not retrying is the safe default; do not extend that to 5xx, where transient is the norm and retry_after_ms may carry a hint.load:error event carrying the same code and status.
Internal iframe endpoints
The iframe exchanges the boot URL token for a short-lived embed browser credential, then uses that credential for internal browser-hit endpoints:GET /v1/embed/sessions/{session_id}/canvasPATCH /v1/embed/sessions/{session_id}/canvasPOST /v1/embed/sessions/{session_id}/imagesGET /v1/embed/sessions/{session_id}/images/{file_id}
Authorization: Bearer <embed_browser_token>.
You normally do not call these directly. Use the iframe and browser SDK commands instead.