Embedding guide

Put a live, interactive preview of an Android app inside your own product - an app builder, a marketing page, a support tool - with one API call and one iframe.

How embedding works

your backend ──(Bearer key)──> POST /api/v1/sessions/   ──> { embed_url, ... }
your frontend ─────────────────> <iframe src=embed_url>
the user's browser ────────────> streams the device, sends taps (token-authed)

The embed_url is a capability link: it carries a session token that is the only credential needed, and it stops working the moment the session ends. Your users never log into Emutize.

Step 1 - create a session from your backend

Keep your API key server-side. Never ship it to the browser.

const r = await fetch("https://app.emutize.com/api/v1/sessions/", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.EMUTIZE_KEY,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ app: "MyApp" })   // library name or numeric id
});
const session = await r.json();
// { session_id, embed_url, token, agent_url, agent_session_id, backend, app }

Step 2 - embed it

Plain iframe (zero dependencies)

<iframe src="EMBED_URL_FROM_STEP_1"
        style="width:420px;height:760px;border:0;border-radius:16px"
        allow="clipboard-write"></iframe>

Or the SDK (events + remote control)

<script src="https://app.emutize.com/static/devices/emutize.js"></script>
<div id="preview"></div>
<script>
  const dev = Emutize.embed("#preview", {
    embedUrl: EMBED_URL_FROM_STEP_1,
    width: "420px", height: "760px"
  });
  dev.on("ready", () => console.log("device is live"));
  dev.on("ended", (e) => showRelaunchButton(e.reason));
  // remote control, e.g. from your builder's UI:
  // dev.key(3) = Home, dev.key(4) = Back
  // dev.input({ type: "tap", x: 540, y: 1200 })
</script>

Step 3 - allow your site to frame Emutize

Embeds send a Content-Security-Policy: frame-ancestors header. The Emutize operator sets the allowlist (EMBED_FRAME_ANCESTORS) to the origins that may iframe sessions, e.g. 'self' https://builder.example.com. If your iframe renders blank, your origin isn't on it.

Lifecycle in an embed

EventWhat you seeWhat to do
Session createdDevice boots, app auto-launches (~10-20s)Show a loading state until ready
readyFirst frame renderedReveal the preview
15 min idle / DELETESession dies; embed shows "Session ended", ended firesOffer a relaunch button that hits your backend again

The app-builder pattern

The flow Emutize was built for: your builder compiles the user's project to an APK, uploads it, starts a session, and shows the live result next to the editor.

// after each successful build:
// 1. upload/refresh the APK in the Emutize library (dashboard or API)
// 2. POST /api/v1/sessions/ { app }
// 3. Emutize.embed(editorPreviewPane, { embedUrl })
// 4. on "ended" -> show "Preview expired - rebuild or relaunch"
Each session is a fresh device - users always preview the latest build with no stale state from the previous run.

Security checklist

DoWhy
Keep the Bearer key server-sideIt can create and kill sessions
Treat embed_url as a secret-ish linkAnyone holding it controls that one session until it dies
Set frame-ancestors to real origins in productionStops other sites from framing your previews
DELETE sessions you no longer needFrees device slots before the idle reaper would