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
| Event | What you see | What to do |
|---|---|---|
| Session created | Device boots, app auto-launches (~10-20s) | Show a loading state until ready |
ready | First frame rendered | Reveal the preview |
| 15 min idle / DELETE | Session dies; embed shows "Session ended", ended fires | Offer 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"
Security checklist
| Do | Why |
|---|---|
| Keep the Bearer key server-side | It can create and kill sessions |
Treat embed_url as a secret-ish link | Anyone holding it controls that one session until it dies |
Set frame-ancestors to real origins in production | Stops other sites from framing your previews |
| DELETE sessions you no longer need | Frees device slots before the idle reaper would |