> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://platform.bctrl.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://platform.bctrl.ai/_mcp/server.

# Embed a Live Browser View

> Mint a live viewer URL for a running browser and drop it into your product as an iframe.

Show your users the browser while the automation runs. One API call mints a tokenized, expiring URL for the [run](/sdk/runs); an iframe does the rest. No SDK on the frontend, no auth wiring - the URL itself is the credential.

```ts TypeScript
import { Bctrl } from "@bctrl/sdk";

const bctrl = new Bctrl({ apiKey: process.env.BCTRL_API_KEY! });

const runtime = await bctrl.runtimes.create({ type: "browser", name: "live-recipe" });
const { runId } = await bctrl.runtimes.start(runtime.id);

// Kick off your automation here - your own CDP code or a hosted agent.

const live = await bctrl.runs.live(runId, {
  control: "none",        // viewers watch; they can't drive
  expiresInSeconds: 3600, // the URL stops working after an hour
});

console.log(live.url);
```

```python Python
from bctrl import Bctrl

bctrl = Bctrl()

runtime = bctrl.runtimes.create(type="browser", name="live-recipe")
started = bctrl.runtimes.start(runtime["id"])

# Kick off your automation here - your own CDP code or a hosted agent.

live = bctrl.runs.live(
    started["runId"],
    control="none",          # viewers watch; they can't drive
    expires_in_seconds=3600, # the URL stops working after an hour
)

print(live["url"])
```

Hand `live.url` to your frontend and embed it:

```html
<iframe
  src="https://...the minted url..."
  width="1280"
  height="800"
  style="border: 0"
  allow="fullscreen"
></iframe>
```

> 📸 **Content TODO:** screenshot or short GIF of the live view embedded inside a third-party-looking dashboard (e.g. a fake "Acme Ops" page with the iframe next to an order table) - this is the money shot for the recipe.

## Hand control to a human

Mint with `control: "input"` and the viewer can drive the browser through the iframe - type a 2FA code, fix a stuck form, then hand back:

```ts TypeScript
const takeover = await bctrl.runs.live(runId, {
  control: "input",
  expiresInSeconds: 600, // keep takeover leases short
});
```

```python Python
takeover = bctrl.runs.live(run_id, control="input", expires_in_seconds=600)
```

While a human controls the runtime, hosted [invocations](/sdk/invocations) on it report `runtime.controller_busy` - your automation should wait, not fight.

> 🎬 **Content TODO:** record a takeover clip - agent fills a login form, hits a 2FA prompt, a human types the code through the embed, agent continues. \~20 seconds, no narration needed.

## Treat the URL as a secret

Anyone holding the URL can watch (and with `control: "input"`, drive) until it expires. Mint one per viewer, keep `expiresInSeconds` as short as the use case allows, and mint a fresh one when it lapses - they're free.

## Next

* [Live View](/sdk/live-view) - the full live view surface
* [Show Customers a Replay](/cookbook/customer-replay) - the after-the-fact version
* [Solve CAPTCHAs](/cookbook/solve-captchas) - or let the platform handle challenges itself