Skip to content

DoAnything

DoAnything is WebAgent's open-ended API. You give one natural-language instruction, and the agent picks its own tools, decides its own path, runs the steps, and does its best to reach the goal in a browser environment.

Unlike DeepResearch / WebSearch / Track — those three are shaped APIs with a fixed artifact shape and a quality contract — DoAnything has no fixed artifact shape: you may not fully know what the goal looks like in the end, so the agent improvises. It therefore makes no quality contract; when you want a contract, use the matching shaped API. The 4 APIs are peers — pick whichever fits what you want.

When to use it

  • The goal is undefined or compound, and the agent needs to decide how to do it.
  • The task spans multiple steps and pages, and may need login state, human confirmation, or long-running execution.
  • You accept "best effort" rather than a fixed-shape result contract.

When you want a fixed-shape artifact, use DeepResearch / WebSearch / Track.

Resource model

A session is one runtime container — it owns the browser, the profile (cookies / login state), and the workspace (file system). A run is one instruction that runs inside a session. You can fire follow-up runs at the same session; they share the state the previous run left behind.

text
project
└── session                   id: sess_…
    ├── browser, profile, workspace
    └── run                   id: run_…
        ├── instructions      "Search Hacker News..."
        ├── status            running | done | …
        └── events (SSE)      run.status_changed, run.message, …

Lifecycle (seven states)

StateMeaningNext moves
pendingAccepted, queued for an agent slotrunning, canceled
runningAgent is actively workingdone, failed, awaiting_input, paused, canceled
awaiting_inputAgent paused itself; needs you to answerrunning (via intervene)
pausedYou paused it (manual)running (via resume), canceled
doneCompleted successfully; output populatedterminal
failedHit an error; error.code and error.detail populatedterminal
canceledYou canceled (or scheduled max-duration tripped)terminal

A run in any non-terminal state holds session resources. Cap with max_duration_minutes to bound that.

Submitting a run

The first run is submitted together with the CreateSessionRequest when you create a session. Request fields:

FieldTypeRequiredNotes
instructionsstringYesThe instruction in plain English. Up to 10 000 chars.
max_duration_minutesintNo1–10 080 (one week).
recordingobjectNo{enabled, quality, capture_during_take_control}; omit for off.
keep_aliveboolNoWhen the run ends, keep the session warm for follow-up runs.
allowed_actionsstring[]NoWhitelist of tool actions the agent may call. Empty = all allowed.
profile_idstringNoReuse cookies/auth from a Profile.

The full schema is in the OpenAPI spec.

Example:

python
from web_agent import Client
from web_agent.v1.types import CreateSessionRequest, RecordingConfigRequest

session = await client.sessions.create(CreateSessionRequest(
    instructions="Find the top 5 Show HN posts from the last 24 hours.",
    max_duration_minutes=10,
    recording=RecordingConfigRequest(enabled=True),
    keep_alive=True,
))
run = session.runs[0]

Follow-up runs

python
from web_agent.v1.types import CreateRunRequest

followup = await client.sessions.create_run(
    session.id,
    CreateRunRequest(
        instructions="Now click into the first post and summarise the discussion.",
    ),
)

The follow-up runs in the same browser, with the same cookies, against the same DOM the previous run left.

Events

Every run emits a Server-Sent Events stream:

http
GET /v1/projects/{pid}/do_anything/sessions/{sid}/runs/{rid}/events
Authorization: Bearer wa_…

Eleven event types (the envelope is the same; data shape varies):

TypeWhen
run.status_changedState transition
run.messageAgent or user message in the chat thread
run.action.startedAgent invoked a tool
run.action.completedTool returned
run.action.failedTool threw
run.screenshotNew browser frame (url is short-lived)
run.input_requestAgent paused; needs you to answer
run.input_request_resolvedYour intervene was accepted
run.cost_updatePer-step cost delta
run.completedTerminal; output populated
stream.heartbeatEvery ~15 s; harmless

Reconnect cleanly:

http
GET …/events
Last-Event-ID: 142

The server replays events with id > 142 so you don't miss anything.

Input request (human in the loop)

When the agent hits a captcha, a 2FA prompt, or any judgment call, it emits run.input_request:

json
{
  "type": "run.input_request",
  "data": {
    "input_request_id": "ir_01HXX…",
    "prompt": "I see a 'Verify you're human' challenge. Solve it for me?",
    "schema": { "type": "object", "properties": { "solved": { "type": "boolean" } } }
  }
}

You answer via POST /intervene:

python
await client.messages.intervene(
    session.id, run.id,
    input_request_id="ir_01HXX…",
    response={"solved": True},
)

The run transitions back to running. The whole cycle is one round-trip; no polling.

Take Control

Some steps the agent cannot finish on its own — a captcha, 2FA, a password only a person should type. For these you can hand the browser to a human: an interactive live browser opens, the person clicks and types, and control is then handed back to the agent.

This differs from an input request above: an input request is the agent asking a question that you answer with JSON; Take Control is a human directly operating the agent's browser.

Take Control uses the same intervene endpoint, with kind selecting the action.

1. Request control

http
POST /v1/projects/{pid}/do_anything/sessions/{sid}/runs/{rid}/intervene
Content-Type: application/json

{ "kind": "take_control", "reason": "captcha" }

It is commonly triggered after the agent emits a run.input_request (captcha / login needed), but you can request it at any time.

2. Get the control URL, hand it to a person

The backend issues a standalone control URL, delivered via the run.take_control_pending event (data carries standalone_control_url / exp / reason). It is an unbranded live-browser page — you can forward it straight to your end user. In that page, the person clicks and types directly on the agent's browser: solving captchas, logging in, handling judgment calls.

After you request control the agent does not stop immediately — only once someone actually opens the URL and connects does the run move to paused and the agent pause (event run.user_paused). If no one connects, the URL expires after about 5 minutes and run.take_control_expired fires.

3. Release control

When the person is done, call intervene again:

http
{ "kind": "release_control", "trigger": "client_release" }

The run returns to running, and the agent re-observes the current page and continues (event run.user_released). The control page also auto-releases after about 30s idle. To re-issue an expired URL, use { "kind": "refresh_control_url" }.

EventMeaning
run.take_control_pendingThe control URL is issued, awaiting a connection; data.standalone_control_url is the page for the human
run.user_pausedSomeone connected and is operating; the agent is paused
run.user_releasedControl handed back; the agent continues
run.take_control_expiredNo one connected; the URL timed out

SDK note: the Python / TypeScript SDK intervene() currently covers only answer_input_request. Take Control's take_control / release_control / refresh_control_url go through the HTTP intervene endpoint directly. See the OpenAPI spec for the full fields.

Profiles

A profile is a single, whole browser identity (one per user, accumulating their login state across every site). Reference one when creating a session:

python
await client.sessions.create(CreateSessionRequest(
    instructions="Open my LinkedIn inbox and reply to the latest message.",
    profile_id="prof_alice",
))

The first time, set up the Profile manually in the Console (log into whichever sites you need). Future sessions reuse the same one. See Profiles.

Workspaces

A workspace is a persistent file system. The agent can read and write files; you fetch them via signed URL after the run is done. Useful for "scrape this site, write a CSV, hand it back."

Next steps