> 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.

# Connect with WebDriver

> Attach Selenium to a running browser Runtime through its run-scoped WebDriver endpoint.

The WebDriver connection is a **Run-scoped browser connection**. When a browser
Runtime starts, BCTRL returns a `webDriverUrl` for the same browser as its CDP
connection. Pass that URL to Selenium Remote WebDriver.

The Runtime owns the browser process and configuration. Selenium attaches to
the existing browser; it does not launch a second browser. The URL is valid
only while its Run is active, so treat it as a credential.

## Get the WebDriver URL

An ephemeral Runtime starts during creation:

```ts
const runtime = await bctrl.runtimes.create({
  name: "checkout-job",
});

if (!runtime.connection) throw new Error("Runtime did not start");
const { webDriverUrl, runId } = runtime.connection;
```

For a reusable profile-backed Runtime, start it when you need a session:

```ts
const runtime = await bctrl.runtimes.create({
  name: "customer-portal",
  profile: true,
});

const started = await bctrl.runtimes.start(runtime.id);
const { webDriverUrl, runId } = { ...started.connection, runId: started.runId };
```

The connection fields are:

| Field                     | Type                   | Always present | Meaning                                                  |
| ------------------------- | ---------------------- | -------------- | -------------------------------------------------------- |
| `connection.webDriverUrl` | `string`               | Yes            | Run-scoped Selenium endpoint for the active browser Run. |
| `connection.cdpUrl`       | `string`               | Yes            | CDP endpoint for the same browser.                       |
| `runId`                   | `string`               | Yes            | Run that owns both connections.                          |
| `connection.recording`    | `{ enabled: boolean }` | Yes            | Whether Run recording is enabled.                        |

Both endpoints remain stable for the lifetime of the Run and stop working when
the Run ends. Do not log, persist, or expose them to page code.

## JavaScript

Install `selenium-webdriver`, then use `webDriverUrl` as the remote server:

```ts
import { Builder, By } from "selenium-webdriver";

const driver = await new Builder()
  .usingServer(webDriverUrl)
  .forBrowser("chrome")
  .build();

try {
  await driver.get("https://example.com");
  console.log(await driver.findElement(By.css("h1")).getText());
} finally {
  await driver.quit();
}
```

`driver.quit()` closes the Selenium session. Stop the BCTRL Runtime separately
when the automation is complete.

## Python

```python
from selenium import webdriver

options = webdriver.ChromeOptions()
driver = webdriver.Remote(
    command_executor=webdriver_url,
    options=options,
)

try:
    driver.get("https://example.com")
    print(driver.title)
finally:
    driver.quit()
```

## Coordinate controllers

Selenium, the external CDP client, and hosted Agents can connect to the same
browser Run. BCTRL does not serialize page-level actions between controllers.
Coordinate navigation, clicks, and page ownership in your application so two
controllers do not overwrite each other’s work.

The external CDP endpoint allows one external CDP controller at a time. CDP
and WebDriver have separate connection endpoints; Views, recordings, traces,
and events do not consume the external CDP slot.

## Runtime-owned configuration

Configure browser arguments, browser version, profile, extensions, proxy,
certificate policy, and other launch settings on the BCTRL Runtime. WebDriver
capabilities do not replace Runtime launch configuration.

Remote file upload through Selenium is not supported. Use the Runtime file
Tools to stage a durable File into the Runtime workspace, then upload it from
the workspace path:

```ts
await bctrl.tools.call("runtime.files.stage", {
  fileId,
  path: "input.csv",
}, { runtimeId: runtime.id });
```

See [Stage Runtime file](/sdk/tools/runtime/runtime-files-stage) and
[Collect Runtime file](/sdk/tools/runtime/runtime-files-collect) for the file
transfer methods.

## End the session

Stopping the Runtime ends the active Run and invalidates the WebDriver URL:

```ts
const stopped = await bctrl.runtimes.stop(runtime.id);
console.log(stopped.runId, stopped.status, stopped.stopped);
```

Use `runId` with [Runs](/sdk/runs) to inspect trace spans, raw events,
recordings, usage, and files from the Selenium session.

## Next

* [Connect with CDP](/sdk/connect-cdp) — attach Playwright or Puppeteer
* [Runtimes](/sdk/runtimes) — configure browser identity and launch options
* [Runs](/sdk/runs) — inspect what happened during the session