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
- Put
moda_live_...keys in frontend code - Call
POST /v1/embed/sessionsdirectly 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
Allowed origins
Setallowed_origins to the exact origins that host the iframe.
- Include scheme and host
- Include port for local development
- Do not include paths
- Do not use wildcards
Treat embed URLs as bearer URLs
Theembed_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
Session duration
Sessions supportexpires_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: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:- Run your app on one origin, for example
http://localhost:4000. - Run Moda on another origin, for example
http://localhost:3000. - Create the embed session with
allowed_origins: ["http://localhost:4000"]. - Load the returned
embed_urlin an iframe.
postMessage origin checks are active.
Production checklist
- API key stored server-side only
- Paid-plan or beta entitlement enabled
allowed_originsare exact production and staging origins- Parent app validates
channel,version,sessionId,origin, andsourceon 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