> 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 full documentation content, see https://platform.bctrl.ai/llms-full.txt.

# Connect with CDP

> Open a short-lived CDP connection into a browser runtime and drive it with Playwright or Puppeteer.

A browser runtime is controlled through a **connection**: a short-lived lease that points your automation framework at the live browser.

Use connections for external tools such as Playwright, Puppeteer, Chrome debuggers, or other CDP-compatible clients. Use invocations when BCTRL should run hosted AI work inside the runtime.

## Flow

1. Create a space.
2. Launch a browser runtime.
3. Create a CDP connection.
4. Pass the WebSocket URL to your browser framework.
5. Inspect the current run through events, commands, artifacts, live view, and recording.

## Launch a runtime

```ts
const space = await bctrl.spaces.create({
  name: "sales-workflow",
});

const runtime = await space.runtimes.browser.launch({
  name: "crm",
  profile: "sales-bot",
});
```

## Create a CDP connection

```ts
const connection = await runtime.connections.create({
  protocol: "cdp",
  ttlSeconds: 600,
});

const wsUrl = connection.endpoint.url;
```

The endpoint URL is returned only by the create call. Connection list responses contain metadata, not signed attach URLs.

## Drive it with Playwright

```ts
import { chromium } from "playwright";

const browser = await chromium.connectOverCDP(wsUrl);
const context = browser.contexts()[0] ?? await browser.newContext();
const page = context.pages()[0] ?? await context.newPage();

await page.goto("https://example.com");
console.log(await page.title());
```

## Drive it with Puppeteer

```ts
import puppeteer from "puppeteer";

const browser = await puppeteer.connect({
  browserWSEndpoint: wsUrl,
});

const page = await browser.newPage();
await page.goto("https://example.com");
```

## Revoke a connection

Connections expire automatically. Delete one early when you hand off control or finish the attach window.

```ts
await runtime.connections.delete(connection.id);
```

Already-open connections finish according to the underlying lease. New attaches are refused.

## Stop the runtime

```ts
await runtime.stop();
```

Stopping a runtime tears down the browser and prevents new connection attaches.

## Related

* [Connections](/sdk/core-concepts/connections)
* [Browser Runtimes](/sdk/browser-runtimes/overview)
* [Runs](/sdk/core-concepts/runs)