Automation Events
Orientation for consuming the Server-Sent Event stream from /v1/automate -- ordering, conditional events, filtering, and consumption patterns.
The /v1/automate endpoint streams Server-Sent Events (SSE). Each event has a string event name and a typed data payload; the SDKs expose the stream as a discriminated union (AutomateEvent in TypeScript, matched via event.event in Python), so switch/match on the event name narrows the payload for you.
This page covers what the API reference can’t: the order events arrive in, which ones only fire under specific conditions, what you probably want to filter out in production, and a starter consumption pattern. For the complete list of events and their payload schemas, see the API reference.
Typical event ordering
Section titled “Typical event ordering”A simple task (extract the title of a single page, no form interactivity, no extraction-heavy work) produces roughly this sequence:
cdp:endpoint_connected— browser session attached.agent:processing/agent:status— the agent builds a task plan.browser:navigated— the initial page loads.task:started— execution begins; the payload echoes the plan andsuccessCriteria.- A per-iteration loop:
agent:step— iteration begins.agent:processing— the agent is thinking.agent:reasoned— reasoning output for this step.agent:action— the action being taken (e.g.extract,click,done).agent:processing— post-action processing.
task:validated— the completion check passes. See the API reference for the payload fields.task:completed— the agent decides the task is done (fires inside the agent loop).complete— final result event:finalAnswer,stats(action count, iteration count, duration),success, and an optional structurederror.done— stream terminator (empty payload today, reserved for future metadata).
Longer tasks repeat step 5 multiple times and may interleave browser:navigated, browser:action_started/browser:action_completed, browser:screenshot_captured, and agent:waiting. The first four events and the last four are stable landmarks; everything between them is per-iteration noise you can filter down to what you actually want to display.
complete, done, and the top-level error event are full members of the typed AutomateEvent union (added in SDK 2.6.0), so an exhaustive switch narrows their payloads the same way it does for any other event.
Conditional events
Section titled “Conditional events”These only fire under specific conditions:
interactive:form_data:request/interactive:form_data:error— only wheninteractive: trueis set. See the Interactive Mode guide for the request/response cycle.agent:extracted— fires when the task performs structured extraction.task:validation_error— emitted if the completion check fails; rare on well-formed tasks.task:aborted— the task was terminated early.browser:screenshot_captured_image— fires alongsidebrowser:screenshot_capturedwhen the screenshot payload is actually attached; large events, so the image variant is separate from the lightweight capture notice.browser:reconnected— the underlying browser session was re-established (e.g. after a transient disconnect).ai:generation/ai:generation:error— LLM-call instrumentation around agent reasoning steps.cdp:endpoint_cycle— the CDP endpoint was rotated.task:metrics/task:metrics_incremental— metric emissions for tasks when metrics are enabled.
Payload shapes for each are in the API reference.
Events to filter in production
Section titled “Events to filter in production”Filtering is client-side — the server always emits these.
system:debug_compression,system:debug_message— internal diagnostics; nothing end-user-facing.browser:screenshot_captured_image— the payload carries image bytes; drop it unless you actually render screenshots.agent:processing— high frequency. If you only want state transitions (navigated, action, validated, completed), this is the first thing to filter.ai:generation— chatty during LLM-heavy steps.
A typical progress UI only keeps task:started, browser:navigated, agent:action, task:validated, task:completed, and complete.
Consumption pattern
Section titled “Consumption pattern”Switch on event.event; the SDK narrows event.data for each case. Show only the events you care about and ignore the rest:
import Tabstack from '@tabstack/sdk'
const client = new Tabstack()const stream = await client.agent.automate({ task: 'Extract the page title', url: 'https://example.com',})
for await (const event of stream) { switch (event.event) { case 'task:started': console.log('Task running:', event.data.task) break case 'agent:action': console.log('Action:', event.data.action, '(value:', event.data.value, ')') break case 'browser:navigated': console.log('Navigated:', event.data.url) break case 'complete': if (event.data.success) { console.log('Completed:', event.data.finalAnswer) console.log(` ${event.data.stats.iterations} iterations, ${event.data.stats.durationMs}ms`) } else { console.log(`Failed (${event.data.error?.code}):`, event.data.error?.message) } break case 'error': console.log(`Runner error (${event.data.error.code}):`, event.data.error.message) break }}from tabstack import Tabstack
client = Tabstack()stream = client.agent.automate( task="Extract the page title", url="https://example.com",)
for event in stream: match event.event: case "task:started": print("Task running:", event.data.task) case "agent:action": print("Action:", event.data.action, "(value:", event.data.value, ")") case "browser:navigated": print("Navigated:", event.data.url) case "complete": if event.data.success: print("Completed:", event.data.final_answer) print(f" {event.data.stats.iterations} iterations, {event.data.stats.duration_ms}ms") else: code = event.data.error.code if event.data.error else "UNKNOWN" msg = event.data.error.message if event.data.error else "" print(f"Failed ({code}):", msg) case "error": print(f"Runner error ({event.data.error.code}):", event.data.error.message)For a deeper walkthrough of consuming the stream end-to-end, see the Automate event flow guide.
Error handling
Section titled “Error handling”Failures surface in three places:
- HTTP-level exceptions raised by the SDK before or during the request (e.g. a malformed body, missing task, auth failure, rate limit). These are the SDK’s typed error classes —
BadRequestError,AuthenticationError,RateLimitError, etc. Wrap the initialautomate(...)call and thefor awaitloop withtry/except(ortry/catch) to handle them. - Agent-level aborts surface inside the
completeevent withsuccess: falseand a structurederrorpayload:{ code, message }. Thecodeis a typed enum (TASK_ABORTED,MAX_ITERATIONS,MAX_ERRORS,TASK_FAILED) you can branch on. Atask:abortedevent also fires earlier in the stream, butcompletecarries the canonical final state. - Top-level
errorevents fire only if the task runner itself crashes — distinct from the agent-level aborts above. Payload:{ success: false, error: { code, message, timestamp } }wheretimestampis an ISO-8601 string. This event is in the typedAutomateEventunion, so an exhaustiveswitchnarrows it.
When in doubt, log unknown event names during development — they are either conditional events you do not handle yet, or new variants added in a later SDK version.
See also
Section titled “See also”- API reference — automate — complete event schema.
- Automate event flow guide — deeper consumption walkthrough.
- Interactive Mode guide —
interactive:form_data:*usage.