Skip to main content
Canvas Embed SDK is designed around a server-created, short-lived iframe session. The browser never receives your Moda API key.

Server-side API keys only

Do:
  • Store the Moda API key in a backend secret manager
  • Create, refresh, and revoke sessions on your backend
  • Use separate API keys for production, staging, and local development
Do not:
  • Put moda_live_... keys in frontend code
  • Call POST /v1/embed/sessions directly from the browser
  • Reuse one API key across unrelated customer environments

Entitlements

The docs and SDK can be visible without granting access. Actual feature access is enforced by Moda:
  • The API key must belong to an enabled paid plan or approved beta account
  • The key owner must have access to the target canvas
  • Edit-mode sessions require edit permission on the canvas
If the account is not enabled, session creation fails before a user can load the iframe.

Allowed origins

Set allowed_origins to the exact origins that host the iframe.
Rules:
  • Include scheme and host
  • Include port for local development
  • Do not include paths
  • Do not use wildcards
The iframe rejects messages from any origin not in this list.

Treat embed URLs as bearer URLs

The embed_url contains a short-lived boot token in the query string. The iframe exchanges it for a narrower browser credential, but the boot token can still load the session while it is valid. Treat the full URL like a temporary secret and assume generic URL tooling can capture it unless you configure that tooling otherwise:
  • Do not log it in analytics or error tools
  • Redact query strings from reverse proxy, CDN, and application request logs where possible
  • Do not store it permanently
  • Do not send it to third-party services
  • Refresh it before expiry for long sessions
  • Revoke it when the user signs out or loses access
For the beta, keep sessions short-lived and only send embed URLs directly from your backend to the browser page that will host the iframe. Do not put embed URLs in emails, tickets, durable notifications, or shared documents.

Session duration

Sessions support expires_in_seconds from 60 to 3600. Recommended defaults: For long-running editors, refresh from your backend and send session.refresh to the iframe before expiry.

Iframe attributes

Use these attributes — the embed relies on them:
You do not need a downloads permission. Export never downloads from inside the iframe — the iframe returns a base64 dataUrl via postMessage and your page triggers the download. Image upload uses the iframe’s own <input type="file"> picker, which needs no special allow token. Avoid adding a restrictive sandbox attribute unless you have tested it with your required features. If your security policy requires sandboxing, test at least:
  • Scripts
  • Same-origin behavior
  • Clipboard paste
  • File picker and image upload

Content Security Policy

Your parent application must allow the Moda iframe origin:
If your parent page calls your own backend only, no additional connect-src is needed for Moda from the parent page. The iframe performs its own API calls from the Moda origin. The embed does not gate framing with a server frame-ancestors header. Instead it enforces the allowed_origins you set at session creation at runtime, on every postMessage: the iframe ignores messages from, and refuses to post to, any origin not in that list. Your page still owns the download and clipboard UX, since exports and copy/paste resolve in the parent.

Plan for failures

Handle these cases in your parent app:

Local development

For local cross-origin testing:
  1. Run your app on one origin, for example http://localhost:4000.
  2. Run Moda on another origin, for example http://localhost:3000.
  3. Create the embed session with allowed_origins: ["http://localhost:4000"].
  4. Load the returned embed_url in an iframe.
This mirrors production behavior because postMessage origin checks are active.

Production checklist

  • API key stored server-side only
  • Paid-plan or beta entitlement enabled
  • allowed_origins are exact production and staging origins
  • Parent app validates channel, version, sessionId, origin, and source on every message
  • Parent app handles load:error, save errors, export errors, chat errors, and expiry
  • Session refresh and revoke endpoints exist in your backend
  • Embed URL query strings are excluded from logs and analytics
  • Browser-token responses are only passed to the iframe through session.refresh
  • Manual QA covers edit, autosave, export, image paste/upload, keyboard undo/redo, and chat if enabled