Skip to main content

Overview

Multi-task workflows allow you to reuse a single browser session for multiple sequential tasks. This is more efficient than creating a new session for each task, as the browser state persists between tasks. When to use:
  • Multiple related tasks on the same website
  • Workflows that build on previous actions (e.g., login → search → checkout)
  • Scenarios where maintaining browser state (cookies, local storage) is important
For workflows that span multiple sessions (e.g., daily recurring tasks that need to stay logged in), use browser profiles to persist cookies and browser data between sessions.
When not to use:
  • Single, isolated tasks (use /start/run-task instead)
  • Unrelated tasks across different websites

Creating Persistent Sessions

Use the /start/start-session endpoint to create a session that remains active for multiple tasks.
curl -X POST https://connect.webrun.ai/start/start-session \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "mode": "default",
    "initialTask": {
      "taskDetails": "Go to amazon.com",
      "startingPoint": "https://amazon.com",
      "maxDuration": 300000
    }
  }'
Response:
{
  "success": true,
  "sessionId": "a1b2c3d4e5f6",
  "socketURL": "https://connect.webrun.ai",
  "streaming": {
    "webRTCURL": "https://74.235.190.31:8889/a1b2c3d4e5f6/whep",
    "webViewURL": "https://74.235.190.31:8889/a1b2c3d4e5f6",
    "dimensions": { "width": 1024, "height": 600 }
  },
  "initialPrompt": "Go to amazon.com",
  "expiresIn": 300000,
  "balance": 12.50,
  "message": "Connect to instance using sessionId in auth"
}
Save the sessionId from the response—you’ll need it for all follow-up tasks.

Sending Follow-Up Tasks

After creating a session, send additional tasks using the /start/send-message endpoint with actionType: "newTask".
curl -X POST https://connect.webrun.ai/start/send-message \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "sessionId": "SESSION_ID",
    "message": {
      "actionType": "newTask",
      "newState": "start",
      "taskDetails": "Now add the first item to cart"
    }
  }'
Response (completed within 50 seconds):
{
  "success": true,
  "sessionId": "a1b2c3d4e5f6",
  "taskId": "x9y8z7w6v5u4",
  "result": {
    "type": "task_completed",
    "data": { "message": "Added item to cart" }
  }
}
Response (still running):
{
  "success": true,
  "sessionId": "a1b2c3d4e5f6",
  "taskId": "x9y8z7w6v5u4",
  "pending": true,
  "pollUrl": "https://connect.webrun.ai/task/a1b2c3d4e5f6/x9y8z7w6v5u4"
}

Complete Working Example

This example demonstrates a complete multi-task workflow: creating a session, sending multiple tasks, polling for results, and terminating the session.
const API_KEY = "YOUR_API_KEY";
const BASE = "https://connect.webrun.ai";

async function runMultiTaskWorkflow() {
  // 1. Create session
  const session = await fetch(`${BASE}/start/start-session`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      mode: "default",
      initialTask: {
        taskDetails: "Go to amazon.com",
        startingPoint: "https://amazon.com"
      }
    })
  }).then(r => r.json());

  console.log("Session created:", session.sessionId);

  // 2. Send follow-up task
  const task1 = await fetch(`${BASE}/start/send-message`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      sessionId: session.sessionId,
      message: {
        actionType: "newTask",
        newState: "start",
        taskDetails: "Search for 'wireless keyboard'"
      }
    })
  }).then(r => r.json());

  // 3. Poll if pending
  if (task1.pending) {
    const result = await pollForResult(session.sessionId, task1.taskId);
    console.log("Task 1 result:", result);
  }

  // 4. Send another task
  const task2 = await fetch(`${BASE}/start/send-message`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      sessionId: session.sessionId,
      message: {
        actionType: "newTask",
        newState: "start",
        taskDetails: "Add the first result to cart"
      }
    })
  }).then(r => r.json());

  if (task2.pending) {
    const result = await pollForResult(session.sessionId, task2.taskId);
    console.log("Task 2 result:", result);
  }

  // 5. Terminate when done
  await fetch(`${BASE}/start/send-message`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      sessionId: session.sessionId,
      message: { actionType: "state", newState: "terminate" }
    })
  });

  console.log("Session terminated");
}

async function pollForResult(sessionId, taskId) {
  const maxAttempts = 60;
  const interval = 2000;

  for (let i = 0; i < maxAttempts; i++) {
    const res = await fetch(`${BASE}/task/${sessionId}/${taskId}`, {
      headers: { "Authorization": `Bearer ${API_KEY}` }
    });
    const data = await res.json();

    if (data.type === "task_completed") return data;
    if (data.type === "guardrail_trigger") throw new Error(`Guardrail: ${data.data.value}`);
    if (data.status === "failed") throw new Error(data.error);

    if (data.pending) {
      await new Promise(r => setTimeout(r, interval));
      continue;
    }

    return data;
  }

  throw new Error("Polling timeout");
}

runMultiTaskWorkflow();

Single vs Multi-Task Comparison

Use /start/run-task for Single Tasks

When you only need to execute one task and don’t need session persistence:
curl -X POST https://connect.webrun.ai/start/run-task \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "taskDetails": "Search Google for Anthropic and return the first result"
  }'
  • Session automatically terminates when task completes
  • No need to manage sessionId
  • terminateOnCompletion: true is set automatically
  • More cost-effective for isolated tasks

Use /start/start-session for Multi-Task Workflows

When you need to:
  • Execute multiple related tasks
  • Maintain browser state between tasks
  • Potentially interact manually between tasks
  • Have control over session lifecycle
# Create session
POST /start/start-session

# Send task 1
POST /start/send-message { actionType: "newTask", ... }

# Send task 2
POST /start/send-message { actionType: "newTask", ... }

# Terminate when done
POST /start/send-message { actionType: "state", newState: "terminate" }

Session Parameters

Configure your session behavior with these parameters: Top-level parameters:
ParameterTypeDefaultDescription
modestring”default”Session mode (currently only “default” supported)
profileIdstringnullBrowser profile ID to attach
initialTaskobjectnullInitial task configuration (see below)
initialTask parameters:
ParameterTypeDefaultDescription
taskDetailsstring""Initial task description (can be empty for sessions without initial task)
startingPointstringnullStarting URL (e.g., "https://amazon.com")
maxDurationnumber300000Max task duration in ms (default and max: 5 minutes)
maxInputTokensnumber100000Max input tokens for AI model
maxOutputTokensnumber100000Max output tokens for AI model
avoidDomainsstring[][]Domains agent should not visit (e.g., ["facebook.com"])
terminateOnCompletionbooleanfalseAuto-terminate after first task completes
outputTypestring”text”Output format: "text", "structured_json", or "structured_csv"
outputSchemaobjectnullJSON Schema for structured output
filesstring[][]File IDs to attach to the task (details)
Example with parameters:
{
  "mode": "default",
  "initialTask": {
    "taskDetails": "Search for wireless keyboards",
    "startingPoint": "https://amazon.com",
    "maxDuration": 180000,
    "maxInputTokens": 50000,
    "maxOutputTokens": 50000,
    "avoidDomains": ["facebook.com", "twitter.com"],
    "terminateOnCompletion": false
  }
}
  • maxDuration is the max task duration (default and max: 5 minutes). Session inactivity timeout is fixed at 5 minutes
  • taskDetails can be empty if you plan to send tasks later via /start/send-message
  • Always terminate sessions when done to avoid unnecessary charges

Best Practices

1. Always Terminate Sessions

Don’t forget to terminate sessions when your workflow completes:
// At the end of your workflow
await fetch(`${BASE}/start/send-message`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${API_KEY}`
  },
  body: JSON.stringify({
    sessionId: session.sessionId,
    message: { actionType: "state", newState: "terminate" }
  })
});
Or use terminateOnCompletion on your final task:
message: {
  actionType: "newTask",
  newState: "start",
  taskDetails: "Final task",
  terminateOnCompletion: true
}

2. Handle Errors Gracefully

Always check for errors and handle guardrails:
const result = await pollForResult(sessionId, taskId);

if (result.type === "guardrail_trigger") {
  // Handle guardrail - see Handling Guardrails guide
  console.log("Guardrail:", result.data.value);
} else if (result.status === "failed") {
  // Handle error
  console.error("Task failed:", result.error);
}

3. Poll with Reasonable Intervals

Don’t poll too frequently:
// Good: 2-second intervals
await new Promise(r => setTimeout(r, 2000));

// Bad: 100ms intervals (wastes requests)
await new Promise(r => setTimeout(r, 100));

4. Use WebSocket for Real-Time Updates

For better UX and efficiency, consider WebSocket instead of polling:
socket.on("message", (data) => {
  if (data.type === "task_completed") {
    console.log("Task done:", data.data);
  } else if (data.type === "guardrail_trigger") {
    console.log("Guardrail:", data.data.value);
  }
});