# Attach with Playwright

> Connect Playwright, Patchright or Puppeteer to a running Browlix session through the machine's ticket-checked broker.

Browlix opens the browser; your code attaches to it over the Chrome DevTools Protocol. Run the attaching code on the machine that holds the session ([why](https://browlix.com/en/docs/where-code-runs)).

## Get a ticket

```ts tab=TypeScript
import { Browlix } from "@browlix/sdk";

const bx = new Browlix({ apiKey: process.env.BROWLIX_API_KEY! });
const a = await bx.attach(sessionId, { ttlSeconds: 300 });
// a.endpoint = "ws://127.0.0.1:<port>/cdp", a.headers = { Authorization: "Bearer <ticket>" }
```

```bash tab=curl
curl -X POST https://browlix.com/api/public/v1/sessions/$SESSION_ID/attach \
  -H "Authorization: Bearer $BROWLIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ttl_seconds":300}'
```

The reply contains `ticket`, `endpoint`, `expires_at`, `session_id` and `scope`. The session must be `ready`.

## Playwright

```ts tab=TypeScript
import { chromium } from "playwright";

const browser = await chromium.connectOverCDP(a.endpoint, { headers: a.headers });
const context = browser.contexts()[0]!; // the profile's own context
const page = context.pages()[0] ?? (await context.newPage());
await page.goto("https://example.com");
await browser.close(); // disconnects; the browser keeps running
```

```python tab=Python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(endpoint, headers={"Authorization": f"Bearer {ticket}"})
    context = browser.contexts[0]
    page = context.pages[0] if context.pages else context.new_page()
    page.goto("https://example.com")
    browser.close()
```

Use the existing context — it carries the profile's cookies, storage and settings. `browser.newContext()` would create a separate, empty context that does not belong to the profile.

## Patchright and Puppeteer

Patchright is a drop-in Playwright build: import `chromium` from `patchright` and call `connectOverCDP` exactly as above. Puppeteer connects the same way:

```bash
npm i patchright
npm i puppeteer-core
```


```ts
import puppeteer from "puppeteer-core";

const browser = await puppeteer.connect({ browserWSEndpoint: a.endpoint, headers: a.headers });
```

## Rules the broker enforces

- Loopback only: the broker listens on `127.0.0.1` of the session's machine.
- One ticket, one session, until `expires_at` (30–900 seconds). Request a new ticket for a new connection after it expires.
- The ticket stops working when the session changes hands or ends.
- Disconnecting never stops the session; call `sessions.stop` when you are done.

## Things to avoid

- Do not close the browser through CDP (`browser.close()` on a *launched* browser, `Browser.close`); end sessions with `sessions.stop` so the profile data is saved.
- Do not change the profile's language, time zone, user agent or viewport through CDP overrides; set them on the profile so the whole browser stays consistent.
