DoAnything
DoAnything is the open-ended browser task API and WebAgent's primary execution entry point. You provide a goal; the agent decides how to navigate, search, click, fill forms, and read pages in a real browser, then returns progress and a result. This page introduces the problems it solves and its core capabilities first, then the current call surface.
What it solves
- Web tasks whose steps cannot be scripted: the goal spans multiple pages and the path shifts with page structure, so traditional scripts break on every redesign. DoAnything plans and adjusts the execution path at runtime — you maintain the goal, not the selectors.
- Signed-in operations with no API: many back offices and member areas are web-only. DoAnything reuses login state from Profiles inside a controlled browser and works directly on the page.
- Key actions need a human: confirmation requests surface as
interactionevents; the task continues after a human confirms, so you are not forced to choose between fully automated and fully manual. - Execution must be observable and traceable: every step streams back over SSE, screenshots can be captured as evidence, and a run can be canceled, reattached, or continued in the same browser context.
Core capabilities
| Capability | Description |
|---|---|
| Autonomous execution | Create a run from one instruction; the agent plans the browser action sequence |
| Real browser | Navigation, search, clicks, form filling, and reading happen in a controlled real browser |
| Context reuse | session reuses the browser context; profileId reuses login state |
| Event stream | SSE delivers progress, message, interaction, screenshot, and done |
| Human confirmation | interaction events carry confirmation requests, answered via interactionHandle |
| Evidence capture | capture.screenshots enables screenshots; the event stream keeps the execution trace |
| Run control | limits.maxDurationMinutes caps duration; cancel, pause, resume, and reattach are supported |
Typical scenarios
- Sign in to internal systems or SaaS back offices, query and verify data, and bring results back to your systems (see the Compliance risk agent).
- Read and organize multiple pages on sites that require sign-in (see the Travel planning agent).
- Check site content page by page against a defined rubric and produce a report (see the Brand consistency agent).
When to use it
- The goal spans multiple pages and the steps should not be hardcoded.
- You need login-state reuse, browser-context reuse, or human confirmation.
- The output shape is flexible; the agent organizes the result around the goal.
For fixed-structure search results or research reports, use WebSearch or DeepResearch; for scheduled, long-lived page watching, use Track.
Current call
const { token } = (await qoni.delegateToken({
user: { id: process.env.QONI_USER_ID! },
products: ["doAnything"],
})).data;
const run = await qoni.doAnything.run({
token,
prompt: "Open Hacker News and list the first five story titles and links.",
capture: { screenshots: true },
limits: { maxDurationMinutes: 10 },
profileId: "prof_alice",
});
const result = await run.wait();
console.log(result.status, result.output);run() returns a RunHandle; use run.id, not run["run_id"]. The old client.sessions, client.events, and runAsync() calls are not current SDK APIs.
REST surface
POST /api/v1/projects/{pid}/do_anything/runs
Authorization: Bearer wa_...
Content-Type: application/json
{"instructions":"Open Hacker News and list the first five stories.","max_duration_minutes":10}Without session_id, the backend creates a session for the run. Pass an existing session_id to continue in the same browser context. The create response is 202 plus a run envelope.
Lifecycle and events
Run statuses are pending, running, awaiting_input, succeeded, failed, and canceled; terminal_reason can be done, failed, canceled, or expired.
GET /api/v1/projects/{pid}/do_anything/runs/{run_id}/events
Authorization: Bearer wa_...The Node SDK normalizes events to progress, message, interaction, screenshot, and done. Low-level wire names are available under event.raw.event. Reconnect with Last-Event-ID.
for await (const event of run.events()) {
if (event.type === "interaction") {
const interaction = run.interactionHandle(event.data);
if (interaction.can("confirm")) await interaction.confirm();
}
}Follow-up and reconnect
const next = await qoni.doAnything.run({
token,
prompt: "Open the first link and summarize its comments.",
session: run.sessionRef,
});
const reattached = await qoni.doAnything.attach(run.id, { token });The run-id REST surface also provides detail, cancel, intervene, messages, screenshots, pause, resume, and continue routes. Their exact request bodies are defined in the OpenAPI spec.