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

# REST API Integration

> Build browser automation with WebRun's REST API endpoints

## When to Use REST API

The REST API is ideal for:

* **Serverless environments** (AWS Lambda, Vercel, Cloudflare Workers)
* **Simple integrations** where you don't need real-time events
* **Stateless workflows** that poll for results
* **Backend services** that trigger automation tasks
* **Workflow automation** (n8n, Make.com, Zapier)

**Choose WebSocket instead if you need:**

* Live agent thoughts and reasoning
* Sub-second event notifications
* Real-time UI updates

***

## Authentication

All API requests require Bearer token authentication:

```bash theme={null}
Authorization: Bearer YOUR_API_KEY
```

Get your API key at [webrun.ai/settings](https://webrun.ai/settings)

**Base URL:**

```
https://connect.webrun.ai
```

***

## Basic Patterns

### Pattern 1: Single Task (run-task)

The simplest pattern for one-off tasks. Session auto-terminates after completion.

```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": "Go to google.com and search for Anthropic"
    }
  }'
```

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

```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
  }
}
```

**Response (still running 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."
}
```

<Tip>
  Most browser tasks complete in 10-40 seconds. The API waits up to 50 seconds before responding, so typical requests return results immediately—no polling required.
</Tip>

***

### Pattern 2: Multi-Task Session

For workflows that require multiple sequential tasks in the same browser session.

**Step 1: Create persistent session**

```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"
    }
  }'
```

**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 }
  },
  "expiresIn": 300000,
  "balance": 12.50
}
```

**Step 2: Send follow-up task**

```bash 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": "a1b2c3d4e5f6",
    "message": {
      "actionType": "newTask",
      "newState": "start",
      "prompt": "Search for wireless keyboards",
      "terminateOnCompletion": true
    }
  }'
```

<Warning>
  **💰 Cost Tip:** Set `terminateOnCompletion: true` on your last task to auto-close the session and avoid idle charges. Sessions remain billable until terminated or timed out.
</Warning>

***

### Pattern 3: Polling for Results

When a task returns `status: "pending"`, poll until completion:

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

  const data = await response.json();

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

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

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

async function pollForResult(sessionId, taskId, apiKey) {
  const maxAttempts = 60; // 2 minutes max
  const interval = 2000; // Poll every 2 seconds

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

    const data = await res.json();

    if (data.type === "task_completed") return data;
    if (data.type === "guardrail_trigger") return data;
    if (!data.success && data.status === "failed") throw new Error(data.error);

    // Still running - wait and retry
    await new Promise(r => setTimeout(r, interval));
  }

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

// Usage
const result = await runTask("Search Google for Anthropic", "enig_xxx");
console.log(result.data.message);
console.log(`Cost: $${result.usage.cost}`);
```

***

## Complete Examples

### Example 1: Simple Task Execution

```javascript theme={null}
const API_KEY = "enig_xxxxxxxxxxxx";

async function searchGoogle(query) {
  const response = await fetch("https://connect.webrun.ai/start/run-task", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      task: {
        prompt: `Search Google for "${query}" and summarize the first result`
      }
    })
  });

  const data = await response.json();

  if (data.status === "complete") {
    return data.result.data.message;
  } else if (data.status === "pending") {
    // Poll for result
    const result = await pollForResult(data.sessionId, data.taskId, API_KEY);
    return result.data.message;
  }

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

// Usage
const summary = await searchGoogle("Anthropic Claude");
console.log(summary);
```

***

### Example 2: Multi-Step Workflow

```javascript theme={null}
const API_KEY = "enig_xxxxxxxxxxxx";
const BASE = "https://connect.webrun.ai";

async function amazonProductResearch(productName) {
  // 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());

  const sessionId = session.sessionId;

  // 2. Search for product
  const searchTask = await fetch(`${BASE}/start/send-message`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      sessionId,
      message: {
        actionType: "newTask",
        newState: "start",
        prompt: `Search for "${productName}" and list the top 3 results with prices`
      }
    })
  }).then(r => r.json());

  // Poll if needed
  let searchResult;
  if (searchTask.pending) {
    searchResult = await pollForResult(sessionId, searchTask.taskId, API_KEY);
  } else {
    searchResult = searchTask.result;
  }

  console.log("Search results:", searchResult.data.message);

  // 3. Add first item to cart
  const cartTask = await fetch(`${BASE}/start/send-message`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      sessionId,
      message: {
        actionType: "newTask",
        newState: "start",
        prompt: "Add the first product to cart",
        terminateOnCompletion: true // Auto-close session
      }
    })
  }).then(r => r.json());

  // Poll if needed
  let cartResult;
  if (cartTask.pending) {
    cartResult = await pollForResult(sessionId, cartTask.taskId, API_KEY);
  } else {
    cartResult = cartTask.result;
  }

  return {
    searchResults: searchResult.data.message,
    cartStatus: cartResult.data.message
  };
}

// Usage
const results = await amazonProductResearch("wireless keyboard");
console.log(results);
```

***

### Example 3: Using a Proxy

Route traffic through a proxy for geographic targeting or IP management. The `proxy` field is top-level, alongside `environmentId`:

```javascript theme={null}
const API_KEY = "enig_xxxxxxxxxxxx";

// WebRun-managed proxy (specify country)
const response = await fetch("https://connect.webrun.ai/start/run-task", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${API_KEY}`
  },
  body: JSON.stringify({
    task: {
      prompt: "Check product prices on amazon.co.uk",
      startingUrl: "https://amazon.co.uk"
    },
    proxy: {
      source: "WebRun",
      country: "GB"
    }
  })
});

const data = await response.json();
console.log(data);
```

See the [Proxies guide](/usage-guides/proxies) for custom proxy configuration and best practices.

***

### Example 4: Using an Environment

Attach an [environment](/environments/overview) to reuse saved cookies and login sessions across tasks:

```javascript theme={null}
const API_KEY = "enig_xxxxxxxxxxxx";

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

  return response.json();
}

// First run: log in and save cookies to the environment's profile
await runWithEnvironment("<ENV_ID>", "Go to amazon.com and log in");

// Subsequent runs: environment has saved cookies, no login needed
const result = await runWithEnvironment(
  "<ENV_ID>",
  "Go to amazon.com and check my recent orders"
);
console.log(result);
```

***

### Example 5: Authenticated Task with Secrets

Use [secrets](/usage-guides/secrets) to provide credentials upfront so the agent can authenticate without guardrail interruptions:

```javascript theme={null}
async function loginAndExtractWithSecrets(url) {
  const BASE = "https://connect.webrun.ai";
  const API_KEY = "enig_xxxxxxxxxxxx";

  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 ${url}, log in, and extract all user data from the dashboard`,
        startingUrl: url,
        terminateOnCompletion: true,
        secrets: [
          {
            match: `*.${new URL(url).hostname}`,
            fields: {
              email: process.env.SITE_EMAIL,
              password: process.env.SITE_PASSWORD
            }
          }
        ]
      }
    })
  }).then(r => r.json());

  // Poll for result — no guardrail handling needed
  const taskId = session.taskId || session.sessionId;
  return await pollForResult(session.sessionId, taskId, API_KEY);
}
```

***

### Example 6: Handling Guardrails

```javascript theme={null}
async function loginAndExtractData(url, credentials) {
  const BASE = "https://connect.webrun.ai";
  const API_KEY = "enig_xxxxxxxxxxxx";

  // Create session with login task
  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 ${url} and log in`,
        startingUrl: url
      }
    })
  }).then(r => r.json());

  const sessionId = session.sessionId;

  // Start login task
  const loginTask = await fetch(`${BASE}/start/send-message`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      sessionId,
      message: {
        actionType: "newTask",
        newState: "start",
        prompt: "Log in to the website"
      }
    })
  }).then(r => r.json());

  // Poll for result
  const taskId = loginTask.taskId;
  let result = await pollForResult(sessionId, taskId, API_KEY);

  // Handle guardrail if triggered
  if (result.type === "guardrail_trigger") {
    console.log("Guardrail:", result.data.value);

    // Provide credentials
    await fetch(`${BASE}/start/send-message`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${API_KEY}`
      },
      body: JSON.stringify({
        sessionId,
        message: {
          actionType: "guardrail",
          prompt: `Username: ${credentials.username}, Password: ${credentials.password}`,
          newState: "resume"
        }
      })
    });

    // Continue polling
    result = await pollForResult(sessionId, taskId, API_KEY);
  }

  // Extract data after login
  const extractTask = await fetch(`${BASE}/start/send-message`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      sessionId,
      message: {
        actionType: "newTask",
        newState: "start",
        prompt: "Extract all user data from the dashboard",
        terminateOnCompletion: true
      }
    })
  }).then(r => r.json());

  if (extractTask.pending) {
    return await pollForResult(sessionId, extractTask.taskId, API_KEY);
  }

  return extractTask.result;
}
```

***

## Session Control

Control task execution with state commands:

### Pause Task

```bash 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": "a1b2c3d4e5f6",
    "message": { "actionType": "state", "newState": "pause" }
  }'
```

### Resume Task

```bash 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": "a1b2c3d4e5f6",
    "message": { "actionType": "state", "newState": "resume" }
  }'
```

### Stop Task

```bash 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": "a1b2c3d4e5f6",
    "message": { "actionType": "state", "newState": "stop" }
  }'
```

### Terminate Session

```bash 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": "a1b2c3d4e5f6",
    "message": { "actionType": "state", "newState": "terminate" }
  }'
```

***

## Error Handling

All errors follow this format:

```json theme={null}
{
  "success": false,
  "message": "Human-readable error message",
  "code": "ERROR_CODE"
}
```

### Common Error Codes

| Status | Code                     | Solution                   |
| ------ | ------------------------ | -------------------------- |
| 400    | `INVALID_PARAMS`         | Check request parameters   |
| 401    | `INVALID_API_KEY`        | Verify API key is correct  |
| 402    | `INSUFFICIENT_BALANCE`   | Add funds to account       |
| 404    | `SESSION_NOT_FOUND`      | Session expired or invalid |
| 429    | `RATE_LIMIT_EXCEEDED`    | Wait before retrying       |
| 503    | `NO_INSTANCES_AVAILABLE` | Retry in a few moments     |

### Error Handling Example

```javascript theme={null}
async function executeTaskWithRetry(prompt, apiKey, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch("https://connect.webrun.ai/start/run-task", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${apiKey}`
        },
        body: JSON.stringify({ task: { prompt } })
      });

      const data = await response.json();

      if (!response.ok) {
        // Handle specific error codes
        if (response.status === 503) {
          console.log(`Attempt ${attempt}: No instances available, retrying...`);
          await new Promise(r => setTimeout(r, 2000 * attempt));
          continue;
        }

        if (response.status === 429) {
          console.log(`Attempt ${attempt}: Rate limited, waiting...`);
          await new Promise(r => setTimeout(r, 5000 * attempt));
          continue;
        }

        throw new Error(`HTTP ${response.status}: ${data.message}`);
      }

      return data;
    } catch (error) {
      if (attempt === maxRetries) throw error;
      console.error(`Attempt ${attempt} failed:`, error.message);
    }
  }
}
```

***

<Accordion title="Related Guides">
  <CardGroup cols={2}>
    <Card title="WebSocket Integration" icon="plug" href="/integrations/websocket">
      Real-time events and live updates
    </Card>

    <Card title="API Reference" icon="book" href="/api-reference/endpoints">
      Complete endpoint documentation
    </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>
