> ## Documentation Index
> Fetch the complete documentation index at: https://docs.webrun.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Task Workflows

> Learn how to create persistent sessions and send multiple tasks sequentially

## 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

<Tip>
  For workflows that span multiple sessions (e.g., daily recurring tasks that need to stay logged in), use [environments](/environments/overview) to persist cookies, files, and browser data between sessions.
</Tip>

**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.

```bash theme={null}
curl -X POST https://connect.webrun.ai/start/start-session \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "mode": "default",
    "task": {
      "prompt": "Go to amazon.com",
      "startingUrl": "https://amazon.com",
      "maxDuration": 300000
    }
  }'
```

**Response:**

```json theme={null}
{
  "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"
}
```

<Note>
  Save the `sessionId` from the response—you'll need it for all follow-up tasks.
</Note>

***

## Sending Follow-Up Tasks

After creating a session, send additional tasks using the `/start/send-message` endpoint with `actionType: "newTask"`.

<CodeGroup>
  ```bash REST API theme={null}
  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",
        "prompt": "Now add the first item to cart"
      }
    }'
  ```

  ```javascript WebSocket theme={null}
  socket.emit("message", {
    actionType: "newTask",
    newState: "start",
    prompt: "Now add the first item to cart",
    maxDuration: 60000
  });

  socket.on("message", (data) => {
    if (data.type === "task_completed") {
      console.log("Task done:", data.data);
    }
  });
  ```
</CodeGroup>

**Response (completed within 50 seconds):**

```json theme={null}
{
  "success": true,
  "sessionId": "a1b2c3d4e5f6",
  "taskId": "x9y8z7w6v5u4",
  "result": {
    "type": "task_completed",
    "data": { "message": "Added item to cart" }
  }
}
```

**Response (still running):**

```json theme={null}
{
  "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.

```javascript theme={null}
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",
      task: {
        prompt: "Go to amazon.com",
        startingUrl: "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",
        prompt: "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",
        prompt: "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:

```bash theme={null}
curl -X POST https://connect.webrun.ai/start/run-task \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "task": {
      "prompt": "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

```bash theme={null}
# 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:**

| Parameter       | Type   | Default   | Description                                        |
| --------------- | ------ | --------- | -------------------------------------------------- |
| `mode`          | string | "default" | Session mode (currently only "default" supported)  |
| `environmentId` | string | null      | [Environment](/environments/overview) ID to attach |
| `task`          | object | null      | Initial task configuration (see below)             |

**task parameters:**

| Parameter               | Type      | Default | Description                                                               |
| ----------------------- | --------- | ------- | ------------------------------------------------------------------------- |
| `prompt`                | string    | ""      | Initial task description (can be empty for sessions without initial task) |
| `startingUrl`           | string    | null    | Starting URL (e.g., `"https://amazon.com"`)                               |
| `maxDuration`           | number    | 300000  | Max task duration in ms (default and max: 5 minutes)                      |
| `maxInputTokens`        | number    | 100000  | Max input tokens for AI model                                             |
| `maxOutputTokens`       | number    | 100000  | Max output tokens for AI model                                            |
| `terminateOnCompletion` | boolean   | false   | Auto-terminate after first task completes                                 |
| `outputType`            | string    | "text"  | Output format: `"text"`, `"structured_json"`, or `"structured_csv"`       |
| `outputSchema`          | object    | null    | JSON Schema for structured output                                         |
| `files`                 | string\[] | \[]     | File IDs to attach to the task ([details](/environments/file-uploads))    |

**Example with parameters:**

```json theme={null}
{
  "mode": "default",
  "task": {
    "prompt": "Search for wireless keyboards",
    "startingUrl": "https://amazon.com",
    "maxDuration": 180000,
    "maxInputTokens": 50000,
    "maxOutputTokens": 50000,
    "terminateOnCompletion": false
  }
}
```

<Note>
  * `maxDuration` is the max task duration (default and max: 5 minutes). Session inactivity timeout is fixed at 5 minutes
  * `prompt` can be empty if you plan to send tasks later via `/start/send-message`
  * Always terminate sessions when done to avoid unnecessary charges
</Note>

***

## Best Practices

### 1. Always Terminate Sessions

Don't forget to terminate sessions when your workflow completes:

```javascript theme={null}
// 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:

```javascript theme={null}
message: {
  actionType: "newTask",
  newState: "start",
  prompt: "Final task",
  terminateOnCompletion: true
}
```

### 2. Handle Errors Gracefully

Always check for errors and handle guardrails:

```javascript theme={null}
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:

```javascript theme={null}
// 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:

```javascript theme={null}
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);
  }
});
```

***

<Accordion title="Related Guides">
  <CardGroup cols={2}>
    <Card title="Controlling Sessions" icon="gamepad" href="/usage-guides/controlling-sessions">
      Pause, resume, stop, and terminate tasks
    </Card>

    <Card title="Handling Guardrails" icon="shield" href="/usage-guides/handling-guardrails">
      Respond to agent requests for input
    </Card>

    <Card title="Environments" icon="box" href="/environments/overview">
      Persist browser data and files across sessions
    </Card>

    <Card title="File Uploads" icon="file-arrow-up" href="/environments/file-uploads">
      Upload files for the agent to use in sessions
    </Card>
  </CardGroup>
</Accordion>
