Human handoff
Transfer control, let a person act, then reconnect automation.
Automation → Request handoff → Human control → Return → Reconnect automationRequest control
With an initialized client and a ready session:
const handoff = await client.sessions.handoffs.create(sessionId, {
reason: "Complete the website's sign-in step",
});| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
reason | string | No | Empty | Up to 256 characters |
idempotencyKey | string | No in SDK | Generated | Request option; 1–128 characters |
Wait until the handoff is active and the session reports human control, then issue the control grant:
const control = await client.sessions.connections.create(sessionId, {
type: "live",
mode: "control",
handoffId: handoff.id,
});Deliver control.url only to the authorized person. Old control channels are revoked.
Return control
await client.sessions.handoffs.complete(sessionId, handoff.id);
await client.sessions.handoffs.waitUntilCompleted(sessionId, handoff.id);returning is still in progress. Leaving the viewer does not complete a handoff.
Resume automation
Record a checkpoint while ready and controlled by automation:
import { handoffCheckpoint } from "tabsail";
const after = handoffCheckpoint(await client.sessions.get(sessionId));
const resumed = await client.sessions.waitForHandoff(sessionId, {
after,
timeoutMs: 300_000,
});
const grant = await client.sessions.connections.create(resumed.id, { type: "cdp" });Take this checkpoint before the human step starts. Reconnect and acquire context, page, and locator objects again. Inspect the person's changes before continuing.
Handoff waiting does not replay your task or stop the browser on timeout. Human work still uses lifetime, concurrency, and credits.
See the complete human-assisted example.