Automate Features
Execute complex browser automation tasks using natural language with the Tabstack TypeScript SDK.
The Automate operator executes complex browser automation tasks using natural language instructions. Unlike Extract and Generate which work with static content, Automate can interact with pages, fill forms, click buttons, and perform multi-step workflows.
Overview
Section titled “Overview”The automate method is accessed through the agent client on your Tabstack instance:
import Tabstack from '@tabstack/sdk';
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY!});
// Access agent automate methodconst stream = await client.agent.automate({ task: 'Your task description', url: 'https://example.com'});
for await (const event of stream) { // Handle streaming events console.log(event.event, event.data);}const Tabstack = require('@tabstack/sdk').default;
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY});
// Access agent automate methodconst stream = await client.agent.automate({ task: 'Your task description', url: 'https://example.com'});
for await (const event of stream) { // Handle streaming events console.log(event.event, event.data);}Key Features
Section titled “Key Features”- Natural Language Tasks: Describe what you want to accomplish in plain English
- Real-Time Streaming: Get live updates as the automation progresses
- Multi-Step Workflows: Execute complex sequences of actions
- Form Handling: Fill and submit forms automatically
- Data Extraction: Extract data during automation
- Guardrails: Set safety constraints to control automation behavior
Execute Automation
Section titled “Execute Automation”The automate method returns an async iterable that streams events as the automation runs.
Basic Usage
Section titled “Basic Usage”const stream = await client.agent.automate({ task: 'Find the top 3 trending repositories on GitHub and extract their names and star counts', url: 'https://github.com/trending', guardrails: 'browse and extract only'});
for await (const event of stream) { console.log(`Event: ${event.event}`);
if (event.event === 'task:completed') { const result = event.data?.finalAnswer; console.log('Automation completed:', result); }}const stream = await client.agent.automate({ task: 'Find the top 3 trending repositories on GitHub and extract their names and star counts', url: 'https://github.com/trending', guardrails: 'browse and extract only'});
for await (const event of stream) { console.log(`Event: ${event.event}`);
if (event.event === 'task:completed') { const result = event.data?.finalAnswer; console.log('Automation completed:', result); }}Understanding the Stream
Section titled “Understanding the Stream”The automate method returns an async iterable to stream events:
// The for await...of loop handles the streaming automaticallyconst stream = await client.agent.automate({ task: 'Find products', url: 'https://example.com'});
for await (const event of stream) { // Each event has: // - event: string (event type) // - data: object (event-specific data)
console.log(`${event.event}:`, event.data);}// The for await...of loop handles the streaming automaticallyconst stream = await client.agent.automate({ task: 'Find products', url: 'https://example.com'});
for await (const event of stream) { // Each event has: // - event: string (event type) // - data: object (event-specific data)
console.log(`${event.event}:`, event.data);}Event Types
Section titled “Event Types”The automation streams different types of events as it progresses:
Task Events
Section titled “Task Events”| Event Type | Description | Key Data Fields |
|---|---|---|
start | Automation is starting | - |
task:setup | Task is being initialized | task |
task:started | Task execution began | task |
task:completed | Task finished successfully | finalAnswer, status |
task:aborted | Task was aborted | reason |
task:validated | Task result validated | validation |
task:validation_error | Validation failed | error |
Agent Events
Section titled “Agent Events”| Event Type | Description | Key Data Fields |
|---|---|---|
agent:processing | Agent is processing | status |
agent:status | Status update | message |
agent:step | Completed a step | step, description |
agent:action | Performing an action | action, target |
agent:reasoned | Agent’s reasoning | thought, plan |
agent:extracted | Data was extracted | extractedData |
agent:waiting | Waiting for page load/action | reason |
Browser Events
Section titled “Browser Events”| Event Type | Description | Key Data Fields |
|---|---|---|
browser:navigated | Page navigation occurred | url |
browser:action_started | Browser action starting | action |
browser:action_completed | Browser action finished | action, result |
browser:screenshot_captured | Screenshot taken | screenshotId |
Stream Control Events
Section titled “Stream Control Events”| Event Type | Description |
|---|---|
complete | Automation stream finished |
done | Final event (always sent) |
error | An error occurred |
Real-World Examples
Section titled “Real-World Examples”Example 1: Web Scraping with Navigation
Section titled “Example 1: Web Scraping with Navigation”console.log('Starting GitHub trending scraper...\n');
const stream = await client.agent.automate({ task: 'Navigate to GitHub trending, find the top 5 repositories, and for each extract: name, description, primary language, and star count', url: 'https://github.com/trending', guardrails: 'browse and extract only', maxIterations: 50});
for await (const event of stream) { switch (event.event) { case 'agent:status': console.log(`Status: ${event.data?.message}`); break;
case 'agent:action': console.log(`Action: ${event.data?.action}`); break;
case 'browser:navigated': console.log(`Navigated to: ${event.data?.url}`); break;
case 'agent:extracted': const extracted = event.data?.extractedData; console.log('Extracted data:', JSON.stringify(extracted, null, 2)); break;
case 'task:completed': const result = event.data?.finalAnswer; console.log('\nAutomation completed!'); console.log('Final result:', result); break;
case 'error': console.error('Error:', event.data?.error); break; }}console.log('Starting GitHub trending scraper...\n');
const stream = await client.agent.automate({ task: 'Navigate to GitHub trending, find the top 5 repositories, and for each extract: name, description, primary language, and star count', url: 'https://github.com/trending', guardrails: 'browse and extract only', maxIterations: 50});
for await (const event of stream) { switch (event.event) { case 'agent:status': console.log(`Status: ${event.data?.message}`); break;
case 'agent:action': console.log(`Action: ${event.data?.action}`); break;
case 'browser:navigated': console.log(`Navigated to: ${event.data?.url}`); break;
case 'agent:extracted': const extracted = event.data?.extractedData; console.log('Extracted data:', JSON.stringify(extracted, null, 2)); break;
case 'task:completed': const result = event.data?.finalAnswer; console.log('\nAutomation completed!'); console.log('Final result:', result); break;
case 'error': console.error('Error:', event.data?.error); break; }}Example 2: Form Filling
Section titled “Example 2: Form Filling”console.log('Filling contact form...\n');
const formData = { name: 'Alex Johnson', company: 'Example Corp', message: 'I am interested in learning more about your product offerings.'};
const stream = await client.agent.automate({ task: 'Fill out the contact form with the provided data and submit it', url: 'https://company.example.com/contact', data: formData, guardrails: 'do not navigate away from the domain', maxIterations: 30});
for await (const event of stream) { switch (event.event) { case 'agent:action': const action = event.data?.action; const target = event.data?.target; console.log(`Action: ${action} on ${target || 'page'}`); break;
case 'agent:status': console.log(`Status: ${event.data?.message}`); break;
case 'task:completed': console.log('\nForm submitted successfully!'); const confirmation = event.data?.finalAnswer; console.log('Confirmation:', confirmation); break;
case 'error': console.error('Error submitting form:', event.data?.error); break; }}console.log('Filling contact form...\n');
const formData = { name: 'Alex Johnson', company: 'Example Corp', message: 'I am interested in learning more about your product offerings.'};
const stream = await client.agent.automate({ task: 'Fill out the contact form with the provided data and submit it', url: 'https://company.example.com/contact', data: formData, guardrails: 'do not navigate away from the domain', maxIterations: 30});
for await (const event of stream) { switch (event.event) { case 'agent:action': const action = event.data?.action; const target = event.data?.target; console.log(`Action: ${action} on ${target || 'page'}`); break;
case 'agent:status': console.log(`Status: ${event.data?.message}`); break;
case 'task:completed': console.log('\nForm submitted successfully!'); const confirmation = event.data?.finalAnswer; console.log('Confirmation:', confirmation); break;
case 'error': console.error('Error submitting form:', event.data?.error); break; }}Example 3: Multi-Step Workflow
Section titled “Example 3: Multi-Step Workflow”console.log('Starting product research workflow...\n');
const steps: string[] = [];const extractedData: any[] = [];
const stream = await client.agent.automate({ task: ` 1. Search for "wireless headphones" on the e-commerce site 2. Filter results by "customer rating" (4 stars and above) 3. Extract the top 3 products with: name, price, rating, and review count 4. Return the results as structured data `, url: 'https://shop.example.com', guardrails: 'browse and extract only, do not add items to cart', maxIterations: 100});
for await (const event of stream) { switch (event.event) { case 'agent:step': const step = event.data?.step; const description = event.data?.description; steps.push(description); console.log(`\nStep ${step}: ${description}`); break;
case 'agent:action': console.log(` -> ${event.data?.action}`); break;
case 'agent:extracted': const data = event.data?.extractedData; extractedData.push(data); console.log(' -> Extracted:', data); break;
case 'task:completed': console.log('\nWorkflow completed!'); console.log(`\nCompleted ${steps.length} steps`);
const finalResult = event.data?.finalAnswer; console.log('\nFinal Results:'); console.log(JSON.stringify(finalResult, null, 2)); break;
case 'error': console.error('\nWorkflow failed:', event.data?.error); break; }}console.log('Starting product research workflow...\n');
const steps = [];const extractedData = [];
const stream = await client.agent.automate({ task: ` 1. Search for "wireless headphones" on the e-commerce site 2. Filter results by "customer rating" (4 stars and above) 3. Extract the top 3 products with: name, price, rating, and review count 4. Return the results as structured data `, url: 'https://shop.example.com', guardrails: 'browse and extract only, do not add items to cart', maxIterations: 100});
for await (const event of stream) { switch (event.event) { case 'agent:step': const step = event.data?.step; const description = event.data?.description; steps.push(description); console.log(`\nStep ${step}: ${description}`); break;
case 'agent:action': console.log(` -> ${event.data?.action}`); break;
case 'agent:extracted': const data = event.data?.extractedData; extractedData.push(data); console.log(' -> Extracted:', data); break;
case 'task:completed': console.log('\nWorkflow completed!'); console.log(`\nCompleted ${steps.length} steps`);
const finalResult = event.data?.finalAnswer; console.log('\nFinal Results:'); console.log(JSON.stringify(finalResult, null, 2)); break;
case 'error': console.error('\nWorkflow failed:', event.data?.error); break; }}Example 4: Progress Tracking with UI
Section titled “Example 4: Progress Tracking with UI”Build a simple progress tracker:
interface ProgressState { status: string; currentStep: number; totalSteps: number; lastAction: string; isComplete: boolean; error?: string;}
const progress: ProgressState = { status: 'Starting...', currentStep: 0, totalSteps: 0, lastAction: '', isComplete: false};
function displayProgress(progress: ProgressState) { console.clear(); console.log('=== Automation Progress ===\n'); console.log(`Status: ${progress.status}`); console.log(`Step: ${progress.currentStep}/${progress.totalSteps || '?'}`); console.log(`Last Action: ${progress.lastAction}`);
if (progress.isComplete) { console.log('\nComplete!'); } else if (progress.error) { console.log(`\nError: ${progress.error}`); }}
try { const stream = await client.agent.automate({ task: 'Find and extract the top 5 blog posts', url: 'https://blog.example.com' });
for await (const event of stream) { switch (event.event) { case 'agent:status': progress.status = event.data?.message || 'Processing...'; break;
case 'agent:step': progress.currentStep = event.data?.step || 0; break;
case 'agent:action': progress.lastAction = event.data?.action || ''; break;
case 'task:completed': progress.isComplete = true; progress.status = 'Completed'; break;
case 'error': progress.error = event.data?.error; break; }
displayProgress(progress); }} catch (error) { progress.error = error.message; displayProgress(progress);}const progress = { status: 'Starting...', currentStep: 0, totalSteps: 0, lastAction: '', isComplete: false, error: null};
function displayProgress(progress) { console.clear(); console.log('=== Automation Progress ===\n'); console.log(`Status: ${progress.status}`); console.log(`Step: ${progress.currentStep}/${progress.totalSteps || '?'}`); console.log(`Last Action: ${progress.lastAction}`);
if (progress.isComplete) { console.log('\nComplete!'); } else if (progress.error) { console.log(`\nError: ${progress.error}`); }}
try { const stream = await client.agent.automate({ task: 'Find and extract the top 5 blog posts', url: 'https://blog.example.com' });
for await (const event of stream) { switch (event.event) { case 'agent:status': progress.status = event.data?.message || 'Processing...'; break;
case 'agent:step': progress.currentStep = event.data?.step || 0; break;
case 'agent:action': progress.lastAction = event.data?.action || ''; break;
case 'task:completed': progress.isComplete = true; progress.status = 'Completed'; break;
case 'error': progress.error = event.data?.error; break; }
displayProgress(progress); }} catch (error) { progress.error = error.message; displayProgress(progress);}Working with Event Data
Section titled “Working with Event Data”Each event includes an event property and a data object:
const stream = await client.agent.automate({ task: 'Find products', url: 'https://example.com'});
for await (const event of stream) { // Access event type console.log('Event type:', event.event);
// Access data fields directly using optional chaining const message = event.data?.message; const step = event.data?.step ?? 0;
// Access nested data if (event.data?.extractedData) { const data = event.data.extractedData; // Process extracted data }
// Log all event data console.log('Event data:', event.data);}const stream = await client.agent.automate({ task: 'Find products', url: 'https://example.com'});
for await (const event of stream) { // Access event type console.log('Event type:', event.event);
// Access data fields directly using optional chaining const message = event.data?.message; const step = event.data?.step ?? 0;
// Access nested data if (event.data?.extractedData) { const data = event.data.extractedData; // Process extracted data }
// Log all event data console.log('Event data:', event.data);}Options Reference
Section titled “Options Reference”AutomateOptions
Section titled “AutomateOptions”| Option | Type | Default | Description |
|---|---|---|---|
task | string | - | The task description in natural language |
url | string | - | Starting URL for the automation |
data | Record<string, unknown> | - | Context data (e.g., form fields to fill) |
geoTarget | { country: string } | - | Geotargeting parameters for region-specific browsing (e.g., { country: 'US' }) |
guardrails | string | - | Safety constraints for automation behavior |
maxIterations | number | 50 | Maximum iterations (range: 1-100) |
maxValidationAttempts | number | 3 | Maximum validation retry attempts (range: 1-10) |
Guardrails
Section titled “Guardrails”Guardrails are natural language constraints that control automation behavior:
// Examples of guardrails:
// Browse only, no modificationsguardrails: 'browse and extract only'
// Stay on specific domainguardrails: 'do not navigate away from the domain'
// No purchasesguardrails: 'do not add items to cart or make purchases'
// Read-only operationsguardrails: 'read-only operations, do not submit forms or click buttons that modify data'
// Specific constraintsguardrails: 'only search and extract data, do not click on external links'Best Practices
Section titled “Best Practices”1. Be Specific with Instructions
Section titled “1. Be Specific with Instructions”// Vague'Get some products'
// Specific'Find the top 5 best-selling products in the Electronics category and extract their names, prices, and average ratings'2. Set Appropriate Iteration Limits
Section titled “2. Set Appropriate Iteration Limits”// Simple task - lower limitmaxIterations: 30
// Complex multi-step workflow - higher limitmaxIterations: 1003. Always Use Guardrails
Section titled “3. Always Use Guardrails”Protect against unintended actions:
// Good: Clear guardrails{ guardrails: 'browse and extract only, do not submit forms or make purchases'}
// Risky: No guardrails{ // Could potentially trigger unintended actions}4. Handle All Event Types
Section titled “4. Handle All Event Types”Don’t just wait for completion, handle progress and errors:
const stream = await client.agent.automate({ task: 'Find products', url: 'https://example.com'});
for await (const event of stream) { switch (event.event) { case 'agent:status': // Show progress break; case 'task:completed': // Handle success break; case 'error': // Handle errors break; case 'task:aborted': // Handle aborted tasks break; }}5. Implement Timeouts
Section titled “5. Implement Timeouts”Add your own timeout logic for long-running automations:
const timeout = 300000; // 5 minutesconst controller = new AbortController();const timeoutId = setTimeout(() => controller.abort(), timeout);
try { const stream = await client.agent.automate({ task: 'Complex task', url: 'https://example.com' });
for await (const event of stream) { if (controller.signal.aborted) { console.log('Automation timed out'); break; } // Handle events }} finally { clearTimeout(timeoutId);}6. Log Important Events
Section titled “6. Log Important Events”Keep track of the automation flow:
const log: string[] = [];
const stream = await client.agent.automate({ task: 'Find products', url: 'https://example.com'});
for await (const event of stream) { const logEntry = `[${new Date().toISOString()}] ${event.event}: ${JSON.stringify(event.data)}`; log.push(logEntry);
if (event.event === 'task:completed' || event.event === 'error') { // Save log to file or database await saveLog(log); }}Error Handling
Section titled “Error Handling”Always wrap automation in try-catch blocks:
try { const stream = await client.agent.automate({ task: 'Find products', url: 'https://example.com' });
for await (const event of stream) { if (event.event === 'error') { const error = event.data?.error; console.error('Automation error:', error); // Handle gracefully break; }
if (event.event === 'task:completed') { // Success } }} catch (error) { console.error('Fatal error:', error.message); // Cleanup and notify}try { const stream = await client.agent.automate({ task: 'Find products', url: 'https://example.com' });
for await (const event of stream) { if (event.event === 'error') { const error = event.data?.error; console.error('Automation error:', error); // Handle gracefully break; }
if (event.event === 'task:completed') { // Success } }} catch (error) { console.error('Fatal error:', error.message); // Cleanup and notify}Next Steps
Section titled “Next Steps”- Error Handling: Build robust applications with comprehensive error handling
- Generate Features: Discover AI-powered content transformation
- REST API Reference: See the underlying REST API endpoint