Developer experience

Wrap your Playwright session. Done.

BatonPass inherits your existing Page. When the agent gets stuck, the next line of code blocks until the operator resumes from their phone. Your script never knows it was paused.

npm install @batonpass/playwright
import { wrapWithBatonPass } from '@batonpass/playwright';
import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();
const baton = wrapWithBatonPass(page, { apiKey: process.env.BATONPASS_API_KEY, agentId: 'outbound-sf-v3' });

await baton.run(async () => {
  await page.goto('https://salesforce.com/login');
  await page.fill('#username', user);
  await page.fill('#password', pass);
  await page.click('button[type=submit]');
  // 2FA modal appears here? BatonPass auto-detects, hands off to phone.
  // When operator solves, this `await` returns and the agent continues.
  await page.click('a[href*="/lightning"]');
});
1

Wrap

Pass your Playwright page through wrapWithBatonPass().

2

Detect

SDK detects CAPTCHA / 2FA / approval modal patterns automatically.

3

Resume

When the operator solves it on the phone, your script picks up the same await.

Local sandbox

The backend ships an in-memory mode. Run uvicorn app.main:app --port 8069 and the public demo tenant northwind-public is live. Mint a handoff:

curl -X POST http://localhost:8069/api/v1/handoffs \
  -H "Content-Type: application/json" \
  -d '{"interruption_type":"twofa","interruption_label":"Salesforce 2FA"}'

Returns a real ULID and a signed mobile URL. Open the URL on your phone, tap, watch the agent resume.

SDK status

  • @batonpass/playwright (Node) · alpha — wraps `Page`, detects 3 interruption families
  • batonpass-playwright (Python) · alpha — async + sync API symmetric with Node
  • Direct REST + SSE · GA — no SDK required if you can hit our HTTP API
  • Browser-Use / OpenAI Operator · roadmap Q3 2026