Skip to content
Get started

Quickstart: Your First Interactive Automation

Build a working interactive automation in under 5 minutes. Start a browser task, receive a mid-task form request from the agent, and supply values so the task can continue to completion.

You’ll build a working interactive automation in under 5 minutes. By the end, you’ll have code that starts a browser task, receives a mid-task form request from the agent, and supplies values so the task can continue to completion.

Terminal window
npm install @tabstack/sdk

Set your API key as an environment variable:

Terminal window
export TABSTACK_API_KEY="your-key-here"

You’ll automate a newsletter signup. The agent navigates to a signup page, hits the email field, and pauses to ask you for the address. Your code responds with the value, and the agent submits the form.

This is the core pattern for any interactive workflow: the agent does what it can autonomously, asks for what it needs, and continues when you respond.

Create interactive.ts:

import Tabstack from '@tabstack/sdk'
const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY })
function resolveField(field: { ref: string; label: string }): string {
const values: Record<string, string> = {
email: process.env.NEWSLETTER_EMAIL ?? '',
name: process.env.NEWSLETTER_NAME ?? ''
}
return values[field.ref] ?? values[field.label.toLowerCase()] ?? ''
}
try {
const stream = await client.agent.automate({
task: 'Sign up for the newsletter using the email address I provide.',
url: 'https://example.com/newsletter',
interactive: true
})
for await (const event of stream) {
if (event.event === 'interactive:form_data:request') {
const { requestId, fields } = event.data
const fieldValues = fields.map((f) => ({
ref: f.ref,
value: resolveField(f)
}))
await client.agent.automateInput(requestId, { fields: fieldValues })
}
if (event.event === 'complete') {
console.log('Done:', event.data.finalAnswer)
}
if (event.event === 'error') {
throw new Error(event.data.error.message)
}
}
} catch (err) {
if (err instanceof Tabstack.APIError) {
if (err.status === 410) {
console.error('Input request expired. Re-run the task and respond faster.')
} else {
console.error(`API error ${err.status}: ${err.message}`)
}
}
throw err
}

When you set interactive: true, the agent streams events back as it works. When it reaches a form it needs input for, it pauses and emits an interactive:form_data:request event instead of guessing or failing.

That event carries a requestId and a list of fields. Each field has a ref (a stable identifier), a label, a fieldType, and a required flag. You call automateInput with the requestId and your values. The agent receives them and picks up where it left off.

The stream continues until you get a complete event carrying the final result.

The resolveField helper (or resolve_field in Python) is the key integration point: it maps field identifiers to the values your code has available. In production, this is where you look up user data, prompt a UI, or call a secrets store. The example uses environment variables to keep things runnable.

One timing constraint: input requests expire after 2 minutes. If your code doesn’t call automateInput in time, the task fails with a 410 Gone error. In production, handle this by restarting the task.