How it works
An embed moves through these states:
Session limits:
- Sessions end after 3 minutes idle or 10 minutes of active device time (a countdown shows in the footer). Queue and startup time do not consume the active-session allowance. The startup wait is bounded at 33 minutes, covering the device queue’s 30-minute limit plus startup time.
- Concurrent embed sessions share your org’s device concurrency. Visitors
beyond your plan’s limit wait in the
startingstate until a device frees up; their session starts automatically without a retry. - Up to 50 embeds per app.
- The embed URL is the only credential. It can start a device session on its app and nothing else. Revoking the embed disables the URL immediately.
- Sessions bill to your org like any direct device session.
Create an embed
In the Revyl app, open your app and choose App settings in the sidebar (or Settings on the app’s Overview), then find Public embed under General. Choose Default branch or a branch, then Automatic, Latest build, or Exact build. Automatic uses the branch’s default build; Latest build uses its newest build, even when the branch is pinned. Visitors cannot override this saved choice, and an unavailable build does not fall back. Copy the iframe snippet, or revoke it from the same section. See Build branches. Base snippet:URL parameter reference
All parameters are optional, combine freely, and are purely presentational — none of them changes what the embed is allowed to do. Invalid values are ignored (the default applies). Unknown parameters are ignored.
Why you should always pin
theme: without it, the embed follows what the
browser reports inside the iframe — and for cross-origin iframes, browsers
replace the visitor’s OS preference with the embedding page’s own
color-scheme CSS. If your site declares a scheme (most do), the embed
inherits that; if not, it follows the visitor’s OS and may flip dark on your
light page. theme=light or theme=dark removes the guesswork.
What is never configurable: the “Powered by Revyl” footer, the session
countdown, and the visitor’s Stop button always render. Anonymous visitors
must always be able to end a session they started.
Recipes
Every combination, ready to adapt. ReplaceYOUR_EMBED_ID and the origin as
needed.
1. Default video-style embed
2. Branded poster
Custom copy and button color on top of your uploaded poster image:3. Transparent blend-in
The embed shows only the device frame and footer; your page shows through everywhere else. All threestyle properties on the iframe are required —
see Transparent mode for why:
theme that keeps the gray footer text readable against your
page background: theme=light (near-black text) for light pages,
theme=dark (light-gray text) for dark pages.
4. Fully host-controlled (your own start button and states)
controls=0 hides every built-in screen; your page renders the UI and
drives the session over postMessage:
revyl-embed-ended, revyl-embed-at_capacity, and
revyl-embed-error to render your own overlays or retry buttons — see the
JavaScript API. When a session ends, the embed returns to
the (chromeless) poster; sending revyl-embed-start again restarts in place,
so your restart button can live anywhere on the page.
5. Invisible integration — just the device on your page
Combines 3 and 4: no built-in chrome, no background, only the device frame and the mandatory footer. This is the maximum blend-in configuration:6. Site with a light/dark toggle
Pin the initial theme in the URL, then forward your toggle through postMessage so the embed follows it live:Transparent mode
Withtransparent=1 the embed’s own background disappears and the footer
loses its top border. The device frame, poster, and footer text still render.
The iframe element on your page must cooperate — all three properties:
border:0— removes the browser’s default inset iframe border.background:transparent— any background you set on the iframe element paints behind the (now transparent) embed.color-scheme:light dark— required for Safari. Browsers paint an opaque canvas behind a cross-origin iframe when their color schemes might conflict. Chrome only blocks on an actual conflict, but Safari requires the embedding element to explicitly declare scheme support; without this property, transparency silently fails in Safari (you get an opaque white box). Declaringlight darkon both sides — the embed already declares it internally — makes the frames compatible in every engine.
Upload a poster image
The poster is shown as the device’s screen before the session starts. Upload one with your org API key:Launch variables
Embeds can apply your org’s launch variables — environment values or iOS launch arguments injected when the app starts, such as a demo-account auth token — so anonymous visitors land in exactly the app state you want. Attach variables when creating the embed (passlaunch_env_var_ids on the
create call, or pick them in the embed dialog). Every session the embed
starts then applies them. Two properties hold by design:
- Values never reach the visitor. Secret values are decrypted server-side, injected at app launch, and scrubbed from logs and streams. The public embed endpoints never return variable ids, keys, or values.
- The selection is fixed by your org. Nothing a visitor (or the embedding page) sends can add, remove, or change which variables apply.
Minted session URLs (per-session values)
When each session needs different values — say your users preview their own content inside your app — mint a one-time session URL from your backend instead of hard-coding one embed per case:https://app.revyl.ai + embed_path (all other URL
parameters combine with mint as usual). The typical flow:
- Your user clicks “Preview” in your product.
- Your backend authorizes them, then calls the mint API with that user’s values.
- Your frontend loads the returned
embed_pathin the iframe (or updates an existing iframe’ssrc).
- Single-use. The token burns when the session starts (or on any invalid redemption). A visitor restarting the demo gets a session with only the embed’s stored variables — mint a fresh URL per preview.
- TTL-bound.
ttl_seconds(60–3600, default 600) caps how long an unredeemed URL stays valid. Leaked links die on their own. - Values stay server-side. The token is opaque and carries no encoded data; the browser never sees your values.
- Merge order. Embed-attached variables apply first, minted variable
ids second, inline
env_varslast — later wins per key. - Validation at mint time. Inline keys follow launch-variable key rules
(no
DYLD_/SIMCTL_CHILD_/COGNISIM_prefixes), values are capped at 8 KB, max 20 inline values, and stored-variable ids must belong to your org.
JavaScript API
The embed exchangespostMessage events with the page that embeds it. No
SDK, no keys — the messages below are the entire API.
Commands (your page → the embed)
Send withiframe.contentWindow.postMessage(message, "*"). The embed only
accepts commands from its direct parent window — sibling frames, popups,
and nested frames are ignored.
Events (the embed → your page)
Listen onwindow and filter by source, since the embed posts with
targetOrigin: "*":
There is no event for the poster or starting states — drive your loading UI
from the moment you send
revyl-embed-start until revyl-embed-started
arrives.
Troubleshooting
Embeds can be framed by any site (no ancestor allowlist) — access control is
the unguessable embed URL plus per-session tokens, not framing restrictions.