--- title: How to Use Interactive Mode | Tabstack description: Interactive Mode allows the automation agent to pause and request information from the caller when it encounters forms requiring user data like email addresses, credentials, or preferences. Instead of failing or hallucinating, the agent asks for what it needs. --- Interactive Mode is currently in **Beta**. This initial release is optimized for form-filling scenarios. Future iterations will support more complex interactive workflows. ## The Problem When an automation agent encounters a form that requires personal data, it has a gap: it doesn’t know your email address, your password, or your preferences. Without that context, the agent either fails the task or fills in garbage data. Consider this task: *“Sign up for the Mozilla newsletter.”* The agent can navigate to the signup page, locate the form, and identify the required fields. But it can’t submit the form without your email address. Interactive Mode solves this by letting the agent pause and ask. ## How Interactive Mode Works When you enable interactive mode, the automation agent gains the ability to request information mid-task. Here’s the flow: 1. You send an automate request with `interactive: true` 2. The agent executes the task normally, navigating pages and interacting with elements 3. When the agent encounters a form requiring user data, it pauses and emits an `interactive:form_data:request` event containing the form fields it needs filled 4. Your application receives this event, collects the data (from a user prompt, a database, another agent, etc.), and submits it back via the input endpoint 5. The agent fills in the form fields and resumes the task 6. If form validation fails, the agent emits an `interactive:form_data:error` event with the specific errors, and you can re-submit corrected values The agent fills form fields directly without exposing user-provided values to the LLM, keeping sensitive data like passwords and personal information private. ## Prerequisites Before using interactive mode, you’ll need: 1. **A valid Tabstack API key:** Sign up at to get your key. 2. **An SSE-capable client:** Your HTTP client must handle `text/event-stream` responses. 3. **A way to handle input requests:** Your application needs logic to respond when the agent requests form data. Terminal window ``` export TABSTACK_API_KEY="your-api-key-here" ``` ## Enabling Interactive Mode Add `interactive: true` to your automate request: - [curl](#tab-panel-50) - [Python](#tab-panel-51) - [TypeScript](#tab-panel-52) Terminal window ``` curl -X POST https://api.tabstack.ai/v1/automate \ -H "Authorization: Bearer $TABSTACK_API_KEY" \ -H "Content-Type: application/json" \ -N \ -d '{ "task": "Sign up for the Mozilla newsletter", "url": "https://www.mozilla.org", "interactive": true }' ``` ``` from tabstack import Tabstack client = Tabstack() stream = client.agent.automate( task="Sign up for the Mozilla newsletter", url="https://www.mozilla.org", interactive=True, ) ``` ``` import Tabstack from "@tabstack/sdk"; const client = new Tabstack(); const stream = await client.agent.automate({ task: "Sign up for the Mozilla newsletter", url: "https://www.mozilla.org", interactive: true, }); ``` When `interactive` is not set or set to `false`, the agent will automatically decline any form data requests and attempt to continue without user input. ## Handling Form Data Requests When the agent needs user input, it emits an `interactive:form_data:request` event in the SSE stream. The event data contains: | Field | Type | Description | | ----------------- | -------- | --------------------------------------------------------------------------- | | `requestId` | `string` | Unique identifier for this request. Use this when submitting your response. | | `pageUrl` | `string` | URL of the page containing the form. | | `pageTitle` | `string` | Title of the page. | | `formDescription` | `string` | Human-readable description of the form’s purpose. | | `fields` | `array` | The form fields the agent needs filled. | ### Field Structure Each field in the `fields` array contains: | Field | Type | Description | | -------------- | ---------- | ------------------------------------------------------------------------------------------------------------------- | | `ref` | `string` | Element reference ID (e.g., `"E42"`). Include this in your response to map values to fields. | | `label` | `string` | The field’s visible label (e.g., `"Email Address"`). | | `fieldType` | `string` | One of: `text`, `email`, `phone`, `date`, `number`, `select`, `checkbox`, `radio`, `textarea`, `password`, `other`. | | `required` | `boolean` | Whether the field must be filled. | | `options` | `string[]` | Available options for `select` and `radio` fields. | | `currentValue` | `string` | Current value if the field is already partially filled. | | `description` | `string` | Additional context or hints about the field. | ### Example Event ``` event: interactive:form_data:request data: { "requestId": "a1b2c3d4", "pageUrl": "https://www.mozilla.org/en-US/newsletter/", "pageTitle": "Mozilla Newsletter", "formDescription": "Newsletter signup form requiring email and format preference", "fields": [ { "ref": "E12", "label": "Your email address", "fieldType": "email", "required": true }, { "ref": "E15", "label": "Format", "fieldType": "select", "required": false, "options": ["HTML", "Text"] } ] } ``` ## Submitting User Input Once you have the values for the requested fields, submit them to the input endpoint: ``` POST /v1/automate/{requestId}/input ``` - [curl](#tab-panel-53) - [Python](#tab-panel-54) - [TypeScript](#tab-panel-55) Terminal window ``` curl -X POST "https://api.tabstack.ai/v1/automate/a1b2c3d4/input" \ -H "Authorization: Bearer $TABSTACK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "fields": [ { "ref": "E12", "value": "user@example.com" }, { "ref": "E15", "value": "HTML" } ] }' ``` ``` response = client.agent.automate_input( "a1b2c3d4", fields=[ {"ref": "E12", "value": "user@example.com"}, {"ref": "E15", "value": "HTML"}, ], ) ``` ``` const response = await client.agent.automateInput("a1b2c3d4", { fields: [ { ref: "E12", value: "user@example.com" }, { ref: "E15", value: "HTML" }, ], }); ``` The endpoint returns `202 Accepted` on success. If the request has expired or already been answered, it returns `410 Gone`. Input requests expire after **2 minutes**. If you don’t respond within that window, the request returns `410 Gone` and the task is unrecoverable. You must restart the task from the beginning. ## Handling Validation Errors If the form submission fails validation (e.g., an invalid email format), the agent emits an `interactive:form_data:error` event. This event includes the same field structure as the original request, plus a `fieldErrors` map showing which fields failed and why. ``` event: interactive:form_data:error data: { "requestId": "e5f6g7h8", "pageUrl": "https://www.mozilla.org/en-US/newsletter/", "pageTitle": "Mozilla Newsletter", "formDescription": "Newsletter signup form requiring email and format preference", "fields": [ { "ref": "E12", "label": "Your email address", "fieldType": "email", "required": true, "description": "Invalid email address" } ], "fieldErrors": { "E12": "Invalid email address" } } ``` Handle this the same way as the initial request: collect corrected values from the user and submit them to the input endpoint using the new `requestId`. ## Cancelling a Request If you want to skip the form or abort the interactive request, submit with `cancelled: true`: - [curl](#tab-panel-56) - [Python](#tab-panel-57) - [TypeScript](#tab-panel-58) Terminal window ``` curl -X POST "https://api.tabstack.ai/v1/automate/a1b2c3d4/input" \ -H "Authorization: Bearer $TABSTACK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "cancelled": true }' ``` ``` response = client.agent.automate_input( "a1b2c3d4", cancelled=True, ) ``` ``` const response = await client.agent.automateInput("a1b2c3d4", { cancelled: true, }); ``` When cancelled, the agent will attempt to continue the task without the form data, which may result in task failure depending on the form’s requirements. ## Complete Example This example shows the full flow: starting an interactive automation, listening for form data requests, prompting the user, and submitting the response. - [Python](#tab-panel-59) - [TypeScript](#tab-panel-60) ``` from tabstack import Tabstack client = Tabstack() # Start a task with interactive mode enabled stream = client.agent.automate( task="Sign up for the Mozilla newsletter", interactive=True, ) # Listen for events in the stream for event in stream: print(f"[{event.event}]") # When the agent requests form data if event.event == "interactive:form_data:request": request_id = event.data.get("requestId") fields = event.data.get("fields", []) print(f"\nThe agent needs the following information:") field_values = [] for field in fields: ref = field.get("ref") label = field.get("label", "Unknown field") required = field.get("required", False) field_type = field.get("fieldType", "text") options = field.get("options") # Show available options for select/radio fields if options: print(f" {label} (options: {', '.join(options)})") prompt = f" {label}{'*' if required else ''}: " value = input(prompt) field_values.append({"ref": ref, "value": value}) # Submit the values back to the agent client.agent.automate_input(request_id, fields=field_values) print("Input submitted, agent resuming...\n") # Handle validation errors elif event.event == "interactive:form_data:error": request_id = event.data.get("requestId") field_errors = event.data.get("fieldErrors", {}) fields = event.data.get("fields", []) print(f"\nForm validation failed:") for ref, error in field_errors.items(): print(f" {ref}: {error}") field_values = [] for field in fields: ref = field.get("ref") label = field.get("label", "Unknown field") error = field_errors.get(ref, "") prompt = f" {label} ({error}): " if error else f" {label}: " value = input(prompt) field_values.append({"ref": ref, "value": value}) client.agent.automate_input(request_id, fields=field_values) print("Corrected input submitted...\n") # Print the final result elif event.event == "complete": result = event.data print(f"\nTask complete: {result}") ``` ``` import Tabstack from "@tabstack/sdk"; import * as readline from "readline"; const client = new Tabstack(); // Helper to prompt user for input function prompt(question: string): Promise { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => { rl.question(question, (answer) => { rl.close(); resolve(answer); }); }); } async function run() { // Start a task with interactive mode enabled const stream = await client.agent.automate({ task: "Sign up for the Mozilla newsletter", interactive: true, }); for await (const event of stream) { console.log(`[${event.event}]`); // When the agent requests form data if (event.event === "interactive:form_data:request") { const { requestId, fields } = event.data; console.log("\nThe agent needs the following information:"); const fieldValues = []; for (const field of fields) { const label = field.label || "Unknown field"; const required = field.required ? "*" : ""; const options = field.options; if (options) { console.log(` ${label} (options: ${options.join(", ")})`); } const value = await prompt(` ${label}${required}: `); fieldValues.push({ ref: field.ref, value }); } // Submit the values back to the agent await client.agent.automateInput(requestId, { fields: fieldValues }); console.log("Input submitted, agent resuming...\n"); } // Handle validation errors else if (event.event === "interactive:form_data:error") { const { requestId, fields, fieldErrors } = event.data; console.log("\nForm validation failed:"); for (const [ref, error] of Object.entries(fieldErrors)) { console.log(` ${ref}: ${error}`); } const fieldValues = []; for (const field of fields) { const error = fieldErrors[field.ref]; const label = field.label || "Unknown field"; const suffix = error ? ` (${error})` : ""; const value = await prompt(` ${label}${suffix}: `); fieldValues.push({ ref: field.ref, value }); } await client.agent.automateInput(requestId, { fields: fieldValues }); console.log("Corrected input submitted...\n"); } // Print the final result else if (event.event === "complete") { console.log("\nTask complete:", event.data); } } } run(); ``` ## Use Cases Interactive mode is useful whenever the agent needs context that only the caller can provide: - **Account signups:** The agent navigates the signup flow while you provide email, username, and password. - **Form submissions:** Contact forms, registration forms, surveys where personal data is required. - **Checkout flows:** Providing shipping addresses, payment preferences, or coupon codes. - **Multi-agent pipelines:** A parent orchestrator agent automatically supplies data from its own context when the automation agent requests it, with no human involvement needed. ## Limitations - **Form-filling only:** This beta release is optimized for form completion. The agent requests data specifically when it encounters form fields requiring user input. - **2-minute timeout:** Input requests expire after 2 minutes. A `410 Gone` response means the task is unrecoverable — restart it. - **Single request per form:** The agent requests all fields for a form in a single event. It does not ask for fields one at a time. - **Not available via MCP:** Interactive Mode requires a persistent SSE stream to exchange events. It is not supported through the Tabstack MCP server. Use the SDK directly.