Skip to content

Share and embed Embedding

Embedding a Live iOS or Android App

Drop an interactive device running your app into any web page with one iframe, so visitors can tap through the real app without installing anything.

Embedding lets you drop a live, interactive device running your app into any web page with a single iframe — your marketing site, your docs, a sales demo page, wherever. Visitors can tap around your real app without installing anything.

Embedding is available on the Starter and Pro plans. If you’re on Free or Dev, upgrade to create an embed key. If your organization needs custom terms, contact support@vibeview.io.

Create an embed key

Go to Settings → Embeds and click New embed key. Each key controls one embeddable demo:

  • Name — a label to help you tell keys apart (e.g. “Docs demo”).
  • App — the app the embed streams. Every session started with this key runs this app.
  • Allowed domains — the domains permitted to embed this key. Type a domain and press Enter (or comma) to add it; pasting a list splits it into entries automatically. On the Starter plan this is required and covers one domain (a domain and its subdomains count as one); on Professional it’s optional. See Domain allowlisting below.
  • Max session duration (optional) — the longest a single visitor session may run, in seconds.
  • Max concurrent sessions (optional) — the most sessions this key can have running at once. Visitors who tap start while the key is at its limit wait in a queue and start automatically when a slot frees up; if the wait line itself is full, they’re asked to try again in a few minutes.
  • Device models (optional) — pick from a list of the device models currently available to your organization. Select none and visitors get a phone-size device; select one or more and visitors get any available device from your selection (pick a specific model to stream a different form factor, for example a tablet).
  • Let visitors choose the device — when on, visitors can pick which device to run your app on. A picker only appears when you’ve selected two or more device models; with one model (or none) selected, visitors get a default device with no picker.
  • Max sessions per visitor — how many sessions one identified visitor may have running at once, from 1 to 10 (default 1). A visitor already at the limit is handed their existing session back rather than refused or queued. It only bites on visitors your page identifies — see Identifying your visitors below.
  • Require a visitor identity — off by default. When on, sessions start only for visitors the embedding page has identified; a page that presents nothing is refused. Leave it off unless every page using this key signs its visitors.
  • Allow agent control (admins only) — off by default. When on, your own team can drive sessions started with this key programmatically, using your organization’s credentials. It changes nothing for visitors. See Allow agent control below.

Click Create, and the key appears in your list along with a ready-to-copy iframe snippet. You’re also shown the key’s visitor signing secret, once — see Identifying your visitors for what it’s for and why it isn’t shown twice.

Editing a key

Each active key in your list has an Edit action. Editing opens the same form pre-filled with the key’s current settings, so you can change its name, app, allowed domains, device options, max session duration, max concurrent sessions, max sessions per visitor, and whether a visitor identity is required, at any time. Changes apply to new sessions started after you save. Retargeting a key to a different app is allowed — handy when a successor app replaces the one you originally embedded, without re-embedding a new key on every page.

The snippet

Each active key shows its embed snippet with a Copy button:

<iframe
  src="https://vibeview.io/embed/ek_live_xxxxxxxxxxxxxxxxxxxxxxxx"
  width="380"
  height="820"
></iframe>

Paste it anywhere you can embed an iframe. Adjust width/height to fit your layout — the default size mirrors a typical phone aspect ratio.

Revoking a key from the Embeds page is permanent: the key stops working immediately, any page using it stops loading, and it can’t be re-enabled. Create a new key if you need one later.

Domain allowlisting

If you set allowed domains on a key, only pages served from those domains can start a session with it — an embed on any other site is refused with “This embed is not available on this site.”

Each entry is either an exact hostname or a wildcard:

  • example.com matches only example.com itself.
  • docs.example.com matches only that subdomain.
  • *.example.com matches every subdomain (docs.example.com, app.example.com, …) but not example.com itself — add both entries to cover a whole site.

You can paste full URLs — the scheme, path, and port are stripped automatically. Entries that are too broad (such as * or *.com) are rejected.

Plan limits. On the Starter plan, every key must list at least one allowed domain, and your organization’s keys can together cover one domain — a base domain plus its subdomains and wildcard count as a single domain, so example.com, docs.example.com, and *.example.com all fit within the limit. On Professional, domains are unlimited, and you may also leave the list empty to allow embedding from anywhere.

Allowlisting checks the referring page’s domain. Some browsers and privacy settings strip the referrer entirely before it reaches VibeView — in that case, a page on an allowed domain can still be refused with “This embed is not available on this site.” because there’s no referrer to check against the list. If you rely on an allowlist and see this happen on a legitimate page, check whether your site (or a browser extension your visitors use) is suppressing the referrer. On Professional you can leave the allowlist empty if referrer suppression is not something you control.

Identifying your visitors

By default every visit to your embed is anonymous. VibeView has no idea who tapped start, so a visitor who reloads the page gets a brand-new session on a brand-new device, and the limits you set on a key apply to the key as a whole rather than to any one person.

If you already know who your visitors are — because they sign in to your product before they reach the page the demo is on — you can tell VibeView which one of them is starting the session. Two things change once you do:

  • A returning visitor lands back in the session they already have running instead of burning another device. Reload the page, close the tab by accident and reopen it, switch away and come straight back — same live session, same device, the app exactly where they left it. This is what Max sessions per visitor of 1 (the default) gives you: a visitor is handed a session back once they are at their limit, so on a key set to 2 or more a reload starts a second session until they reach it.
  • Limits can apply per person. Max sessions per visitor counts that one visitor’s sessions rather than everyone’s, so one person can’t quietly occupy your whole allowance.

Because that decision hands one visitor access to a running session, VibeView won’t take your word for who they are: your server vouches for the visitor with a short-lived signed assertion, and VibeView refuses anything it can’t verify.

Your signing secret

Every embed key has its own signing secret. It’s generated when you create the key and shown to you exactly once, in a panel right after you click Create — copy it then and store it wherever your server keeps its other secrets. It can’t be viewed again afterwards.

If you lose it, or you want to retire it, use Rotate secret on the key in Settings → Embeds. Only an organization admin can rotate, because rotation is immediate and org-wide: every page still signing with the old secret stops starting identified sessions the moment you confirm, and stays broken until you deploy the new one. Rotate when you’re ready to ship the replacement in the same breath. The new secret is shown once too, on the same terms.

Keys you created before this feature existed have no secret yet — rotate once to issue their first one.

The secret must never reach the browser. Sign on your server, in the code that renders the page. The embed key itself can’t do this job: it’s public, it sits in the iframe URL on your page, and anyone can read it. A secret pasted into browser JavaScript is published to every visitor who opens devtools, and a visitor holding it can sign themselves as anybody else.

For the assertion format itself — what to sign, with which algorithm, and how long it may live — email support@vibeview.io and we’ll send you the current specification with a sample for your language.

Handing it to the embed

The assertion goes on the end of the embed URL, after a #:

<iframe
  src="https://vibeview.io/embed/ek_live_xxxxxxxxxxxxxxxxxxxxxxxx#visitor_token=eyJhbGciOi..."
  width="380"
  height="820"
></iframe>

Build that src on the server as you render the page — the same place you signed the assertion. Everything else about the snippet stays as it is.

Two things follow from putting it after the #:

  • It stays in the address bar, which is what makes a reload work — the page reads it again and lands the visitor back in their session. Re-signing means rendering the page again; changing the address bar by hand doesn’t re-sign anything.
  • It never reaches your own web server, so it can’t turn up in your access logs or in the referrer of anything your page loads afterwards. It does reach VibeView when the session starts, so treat it as a credential on your side too: anything that records your page’s outgoing traffic in full — an APM agent, a logging proxy, an error reporter — can still capture it. VibeView scrubs it from its own logs. Sign a fresh, short-lived one every time you render the page.

Leaving it off changes nothing. A page with no assertion starts an anonymous session exactly as it always has, and you can move your pages over one at a time. The one exception is a key with Require a visitor identity turned on, which is described below.

Requiring an identity

Require a visitor identity is a per-key setting, off by default. With it on, a session can only start when the page presents an assertion that verifies — no assertion is refused just as firmly as a bad one.

Turn it on once every page embedding that key identifies its visitors. Until then it costs you nothing to leave off, but leave it off and your per-visitor limit is only advisory: a visitor who edits the URL in devtools to drop the assertion is anonymous again, and anonymous visitors have no per-person limit to reach. It’s the setting that turns Max sessions per visitor from a courtesy into a limit.

A key that has no signing secret cannot verify anything, so turning the setting on is refused with a message saying so — otherwise the key would be left unable to start a session for anyone at all. Keys created before visitor identity existed are the only ones in that state; an admin rotating the key’s secret once clears it, and the setting saves normally afterwards.

When something is wrong

Whatever the problem, it stops before a device is handed out, so a refused visitor never costs you streaming minutes. What they see depends on which thing went wrong:

  • “This preview link has expired — reload the page” — the assertion is past its expiry, or your server’s clock is running ahead of real time (a minute of drift either way is tolerated; more than that isn’t). Reloading is genuinely the fix for the first: your server signs a fresh one as it renders. If visitors hit this on a page they’ve had open a while, consider re-rendering the iframe with a freshly signed assertion when the page regains focus, rather than signing only at first render.
  • “This visitor identity could not be verified” — the assertion didn’t check out. In practice that’s signing with the wrong (usually a rotated-away) secret, signing for a different embed key than the one in the URL, or an assertion that doesn’t match the format. Reloading won’t help until you fix the signing side.
  • “This demo is not configured to accept visitor identities” — the key has no signing secret. Rotate the key’s secret once to issue one.
  • “This demo requires a visitor identity”Require a visitor identity is on and the page presented nothing at all. Check that the assertion is actually on the URL your server rendered, after the #.
  • “You already have a session running — it will free up shortly” — the visitor is at their Max sessions per visitor limit, and none of the sessions they hold can be handed back. In practice this only happens right after you retarget a key to a different app: their existing session is running the old app, so it can’t be resumed, but it still counts until it ends.

A value that isn’t even shaped like an assertion — empty, truncated, or carrying characters an assertion never contains — is ignored rather than treated as a failure, and the visitor gets an ordinary anonymous session (or, on a key that requires an identity, the message above). A bug in your page’s URL building can’t leave a visitor staring at a broken demo; it does log a warning to the browser console, which is the quickest way to spot one.

What a resumed session does and doesn’t do

  • A resume allocates nothing. No new device, no place in the queue, no second session against your plan’s concurrent limit. That’s why a visitor at their Max sessions per visitor limit is handed their session back rather than being turned away or queued — queueing them would be asking them to wait for a device that only frees when they close the very session they’re trying to reach.
  • The clock keeps running. If the key has a Max session duration, it’s measured from when the session first started, not from the resume. Someone who reloads at minute nine of a ten-minute demo gets one more minute, not ten — otherwise the limit would be a suggestion.
  • Two different people never share a session. A resume matches on the visitor you signed for, so one visitor never reaches another’s session, and a visitor’s session on one embed key is invisible to every other key.
  • Coming straight back works; coming back later doesn’t. An identified session survives a refresh, a tab closed by accident, and a second tab opened alongside the first — that’s the point of it. It is not a bookmark: a session nobody is connected to is cleaned up within a couple of minutes, so a visitor who wanders off and returns later starts a fresh one. An anonymous session is unchanged: closing the page ends it right away, because nobody could ever reach that session again.
  • Two tabs are one session. With the default Max sessions per visitor of 1, a second tab on the same page is handed the same running session rather than a second device — and closing one tab does not take the other down with it.
  • Ending is still ending. Once a session has finished — the visitor closed it, a limit was reached, or it timed out — it isn’t resumed. The next visit starts a fresh one.

Allow agent control

Allow agent control is a per-key setting that is off by default. When it’s on, someone in your organization holding a Developer or Admin credential can drive a session started from this key — reading the screen, tapping, typing, scrolling — with the commands described in AI Agent Control. They target the visitor’s existing session by its id: no second session is started and no extra device is used, and the visitor watches every action happen live on the device in front of them. Useful for guiding someone through your demo, or for checking that an embedded demo still works end to end.

Only an organization admin can change the setting. Other members see it on the key’s form but can’t edit it. Keys with it on show an Agent control pill beside Active in your key list, so you can tell at a glance which demos are drivable. Changes take effect immediately, including on sessions that are already running — switching it back off stops an in-progress agent at its next command.

Revoking a key does the same thing, and so does disabling one: a key that isn’t active can’t be driven, whatever this setting says. Any of the three is an immediate stop, not just a block on starting new sessions.

The setting grants nothing to the people viewing your embed. Driving a session takes an organization credential the embedded page never has, so no visitor and no page you paste the snippet into can do it, and turning the setting on doesn’t change anything a visitor can tap, see, or reach. The confinement described below stays in place either way: what your team can do on an embed session is a restricted set of commands that can’t leave your app — no home button, no deep links.

What visitors experience

Visitors see a Tap to Start button. Once tapped:

  • If a device is free, the session connects and your app loads on it automatically.
  • If every device is busy, the visitor is queued and sees their position in line; the session starts the moment a device frees up.
  • The embed stays covered until the configured app is in the foreground. If the app exits or loses the foreground, VibeView covers the embed while restoring the app.
  • While the session is live, the embed keeps the visitor focused on your app — system navigation that would exit your app (like a home button) is blocked, so visitors stay on the experience you set up for them.
  • If the key has a max session duration, the visitor sees a countdown in the final moments before the session ends.
  • When the session ends (visitor closes it, or a cap is reached), the visitor sees Demo ended with a Replay? button to start a new session.
  • If the visitor closes the page or navigates away, the session ends automatically a moment later.

Usage and limits

Embed sessions are ordinary VibeView sessions from a billing standpoint: the streaming time they use counts toward your plan’s included streaming minutes, and each running embed session counts toward your plan’s concurrent session limit. Once you’re past your included minutes, streaming continues based on your organization’s overage settings (see Billing and Session defaults).

The max concurrent sessions and max session duration fields on an embed key are optional limits you can set in addition to your plan’s own limits — they cap what that one key can do, on top of whatever your plan already allows. Reaching the concurrent-session limit doesn’t turn visitors away: they wait in a queue and start automatically when a slot frees up.

Page events (postMessage)

The embedded page sends postMessage events to the parent page so you can react to what’s happening in the demo — for example, to show your own “loading” UI or track analytics.

Every event has the same envelope:

{ "source": "vibeview-embed", "version": 1, "type": "session:started", ...extraFields }

type is one of:

TypeMeaning
readyThe embedded page has loaded and is showing the start screen.
session:queuedThe visitor is waiting in queue. Includes position, always 1 or higher, and no session id. Sent at most once per session: a visitor returning to a session that’s still being set up gets it only once that session turns out to be waiting for a device, and session:started follows when it’s ready.
session:startedA session is live. Includes sessionId.
session:endedThe session ended (visitor stopped it, or a limit was reached).
errorSomething went wrong starting or running the session. Includes reason.

Example listener:

// Select the iframe whose events this page should handle.
const iframe = document.querySelector('iframe[src^="https://vibeview.io/embed/"]');

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://vibeview.io') return;
  if (!iframe || event.source !== iframe.contentWindow) return;
  if (event.data?.source !== 'vibeview-embed' || event.data.version !== 1) return;
  console.log('VibeView embed event:', event.data.type, event.data);
});

You can also send commands from the parent page into the embed:

iframe.contentWindow.postMessage({ source: 'vibeview-embed-host', type: 'start' }, 'https://vibeview.io');
iframe.contentWindow.postMessage({ source: 'vibeview-embed-host', type: 'stop' }, 'https://vibeview.io');

start begins a session as if the visitor tapped the button themselves (useful if you want your own call-to-action to trigger it). stop ends the current session early.

Last updated 14 Sep 2026 Something wrong on this page? Tell us

Search the docs