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

# Response Model

> Understanding WebRun's hybrid synchronous/asynchronous response model

## Synchronous vs Asynchronous

WebRun uses a **hybrid response model** that combines the simplicity of synchronous APIs with the reliability of asynchronous polling:

* **Tasks completing in \< 50 seconds:** Result returned inline (synchronous)
* **Tasks taking > 50 seconds:** Poll URL returned for async polling

This design eliminates unnecessary polling for the 90% of tasks that complete quickly, while gracefully handling longer operations.

***

## Why This Design?

Pure sync doesn't work because HTTP timeouts (30-60s) are too short for complex browser tasks. Pure async doesn't work because polling adds unnecessary complexity for simple tasks that finish in seconds.

The hybrid approach waits up to 50 seconds. If the task finishes in time, the result comes back inline. If not, you get a poll URL. In practice, \~90% of tasks return inline.

***

## How It Works

```
POST /start/run-task
     ↓
WebRun starts task
     ↓
Wait up to 50 seconds
     ↓
┌────────────────────────────────────┐
│  Did task complete in < 50s?       │
│                                    │
│  YES → Return result inline        │
│  NO  → Return pollUrl              │
└────────────────────────────────────┘
```

***

## Inline Response (\< 50 seconds)

When your task completes within 50 seconds, you get the full result in the initial response:

**Request:**

```bash theme={null}
POST /start/run-task
{
  "prompt": "Search Google for Anthropic"
}
```

**Response:**

```json theme={null}
{
  "success": true,
  "sessionId": "a1b2c3d4e5f6",
  "taskId": "x9y8z7w6v5u4",
  "type": "task_completed",
  "data": {
    "message": "Successfully searched for Anthropic on Google",
    "files": [],
    "network": [
      {
        "id": "tevo9",
        "taskId": "x9y8z7w6v5u4",
        "urls": [
          {
            "url": "https://www.google.com/search?q=Anthropic",
            "timestamp": 1771138379087.646
          }
        ]
      }
    ]
  },
  "usage": {
    "prompt_tokens": 12450,
    "completion_tokens": 3200,
    "total_tokens": 15650,
    "completion_time": 23.5,
    "cost": 0.0124
  }
}
```

**Key indicator:** `type: "task_completed"`

***

## Pending Response (> 50 seconds)

For tasks that take longer than 50 seconds, you receive a `pollUrl` immediately:

**Request:**

```bash theme={null}
POST /start/run-task
{
  "prompt": "Complete a complex multi-step workflow"
}
```

**Response (after 50 seconds):**

```json theme={null}
{
  "success": true,
  "sessionId": "a1b2c3d4e5f6",
  "taskId": "x9y8z7w6v5u4",
  "status": "pending",
  "pollUrl": "https://connect.webrun.ai/task/a1b2c3d4e5f6/x9y8z7w6v5u4",
  "message": "Task still running. Poll GET /task/:sessionId/:taskId for result."
}
```

**Key indicator:** `status: "pending"`

You must then poll the `pollUrl` until the task completes.

***

## Polling for Results

### Polling Endpoint

```
GET /task/:sessionId/:taskId
```

### Poll Until Completion

Poll every 2-3 seconds until you receive a final state:

**Still Running:**

```json theme={null}
{
  "success": true,
  "sessionId": "a1b2c3d4e5f6",
  "taskId": "x9y8z7w6v5u4",
  "status": "running",
  "usage": {
    "prompt_tokens": 8000,
    "completion_tokens": 2100,
    "total_tokens": 10100,
    "completion_time": 12.3,
    "cost": 0.0067
  }
}
```

**Completed:**

```json theme={null}
{
  "success": true,
  "sessionId": "a1b2c3d4e5f6",
  "taskId": "x9y8z7w6v5u4",
  "type": "task_completed",
  "data": {
    "message": "Task finished successfully",
    "files": [],
    "network": [
      {
        "id": "abc12",
        "taskId": "x9y8z7w6v5u4",
        "urls": [
          {
            "url": "https://example.com/page",
            "timestamp": 1771138379087.646
          }
        ]
      }
    ]
  },
  "usage": {
    "prompt_tokens": 15420,
    "completion_tokens": 4200,
    "total_tokens": 19620,
    "completion_time": 87.3,
    "cost": 0.0187
  }
}
```

**Guardrail Triggered:**

```json theme={null}
{
  "success": true,
  "type": "guardrail_trigger",
  "data": {
    "type": "human_input_needed",
    "value": "I need login credentials"
  }
}
```

**Failed:**

```json theme={null}
{
  "success": false,
  "status": "failed",
  "error": "Navigation timeout after 30 seconds",
  "code": "NAVIGATION_TIMEOUT"
}
```

***

## Polling Best Practices

### 1. Poll Interval

Poll every 2-3 seconds. Faster polling wastes resources; slower polling adds unnecessary latency.

```javascript theme={null}
const POLL_INTERVAL = 2000; // 2 seconds
```

### 2. Maximum Attempts

Set a reasonable timeout (e.g., 2 minutes = 60 attempts at 2-second intervals):

```javascript theme={null}
const MAX_ATTEMPTS = 60;
```

### 3. Handle All Terminal States

Check for all possible completion states:

```javascript theme={null}
if (data.type === "task_completed") return data;
if (data.type === "guardrail_trigger") return data;
if (data.status === "failed") throw new Error(data.error);
if (data.status === "terminated") throw new Error("Session terminated");
```

### 4. Track Incremental Cost

The polling endpoint includes current cost in the `usage` object, allowing you to monitor spending in real-time.

***

## Complete Polling Implementation

```javascript theme={null}
async function pollForResult(sessionId, taskId, apiKey) {
  const maxAttempts = 60; // 2 minutes
  const interval = 2000; // 2 seconds

  for (let i = 0; i < maxAttempts; i++) {
    const res = await fetch(
      `https://connect.webrun.ai/task/${sessionId}/${taskId}`,
      { headers: { "Authorization": `Bearer ${apiKey}` } }
    );

    if (!res.ok) {
      throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    }

    const data = await res.json();

    // Terminal states
    if (data.type === "task_completed") return data;
    if (data.type === "guardrail_trigger") return data;
    if (data.status === "failed") throw new Error(data.error || "Task failed");
    if (data.status === "terminated") throw new Error("Session terminated");

    // Still running
    if (data.pending || data.status === "active") {
      console.log(`Poll ${i + 1}/${maxAttempts}: Still running (cost so far: $${data.usage?.cost || 0})`);
      await new Promise(r => setTimeout(r, interval));
      continue;
    }

    // Unexpected state
    return data;
  }

  throw new Error("Polling timeout after 2 minutes");
}
```

***

## Unified Request Handler

Handle both inline and pending responses with a single function:

```javascript theme={null}
async function runTask(prompt, apiKey) {
  // Initial request
  const response = await fetch("https://connect.webrun.ai/start/run-task", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${apiKey}`
    },
    body: JSON.stringify({ prompt })
  });

  const data = await response.json();

  // Inline result
  if (data.status === "complete") {
    return data.result;
  }

  // Pending - start polling
  if (data.status === "pending") {
    return await pollForResult(data.sessionId, data.taskId, apiKey);
  }

  throw new Error(data.message || "Task failed");
}

// Usage
const result = await runTask("Search Google for Anthropic", "enig_xxx");
console.log(result.data.message);
```

***

## WebSocket Alternative

For real-time updates without polling, use WebSocket:

```javascript theme={null}
socket.on("message", (data) => {
  if (data.type === "task_completed") {
    console.log("Done:", data.data.message);
  }
});
```

WebSocket removes the need for polling entirely by pushing updates as they occur.

[Learn more about WebSocket →](/integrations/websocket)

***

## Response Time Distribution

Based on typical usage patterns:

| Task Type             | Typical Duration | Response Mode |
| --------------------- | ---------------- | ------------- |
| Simple search         | 10-20s           | Inline        |
| Form filling          | 20-40s           | Inline        |
| Multi-step navigation | 40-80s           | Polling       |
| Complex workflow      | 80-180s          | Polling       |

**\~90% of tasks complete within 50 seconds and return inline.**

***

<Accordion title="Related">
  <CardGroup cols={2}>
    <Card title="API Endpoints" icon="code" href="/api-reference/endpoints">
      Full endpoint reference
    </Card>

    <Card title="WebSocket Events" icon="bolt" href="/integrations/websocket">
      Real-time alternative to polling
    </Card>
  </CardGroup>
</Accordion>
