Skip to main content
Revyl app embeds let visitors run your app on a real cloud device from any web page. The iframe shows a device frame with a poster image until the visitor clicks start; then a live, interactive session streams into the same frame. This page is a complete reference: every URL parameter, every postMessage command and event, ready-to-paste recipes for each configuration, limits, and troubleshooting. It is written so you (or your coding agent) can build any embed configuration from this page alone.

How it works

An embed moves through these states: Hard limits, always enforced server-side:
  • Sessions end after 3 minutes idle or 10 minutes total (a countdown shows in the footer).
  • Concurrent embed sessions share your org’s device concurrency. Visitors beyond your plan’s limit get the at_capacity state.
  • Up to 50 embeds per app.
  • The embed URL is the only credential. It can start a device session on the pinned 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’s build detail panel and choose Embed. Pin the embed to a specific build or let it track the latest upload, then copy the iframe snippet. Revoke it from the same panel at any time. Base snippet:
Any width/height works; the device frame letterboxes to fit. 390×740 matches a phone’s aspect ratio plus the footer.

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. Replace YOUR_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 three style properties on the iframe are required — see Transparent mode for why:
Pick the 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:
Listen for 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

With transparent=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 darkrequired 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). Declaring light dark on 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:
PNG, JPEG, GIF, or WebP up to 10 MB. An app screenshot at device aspect ratio (for example 9:19.5) looks best. Re-uploading replaces the current poster.

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 (pass launch_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.
Attaching a secret variable (for example an auth-bypass token) to a public embed means every visitor gets that app state — that’s usually the point, but make it a conscious choice.

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:
Response:
Point your iframe at https://app.revyl.ai + embed_path (all other URL parameters combine with mint as usual). The typical flow:
  1. Your user clicks “Preview” in your product.
  2. Your backend authorizes them, then calls the mint API with that user’s values.
  3. Your frontend loads the returned embed_path in the iframe (or updates an existing iframe’s src).
Semantics:
  • 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_vars last — 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 exchanges postMessage events with the page that embeds it. No SDK, no keys — the messages below are the entire API.

Commands (your page → the embed)

Send with iframe.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 on window 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.