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

# Operator: tasks and event streams

> Submitting a task, reading it back at the right route, streaming its events past the 300-second cap, and what needs_human means.

Task execution was exercised end to end in the current preview build: a real
browser was provisioned through this API, a task was submitted against it, and
the task completed with its result on the wire. What follows is the contract
the server enforces for submission, reads, streaming, and cancellation.

## Submit against a device, read back at the top level

The routing fact most integrations trip over, stated first:

* **Create** a task with `POST /v1/devices/{device_id}/tasks`.
* **Read** it back with `GET /v1/tasks/{task_id}`.

There is no `GET /v1/devices/{device_id}/tasks/{task_id}`. Guessing that URL
returns `404`, and a 404 here never means the task is gone; it means the route
does not exist. `GET /v1/devices/{device_id}/tasks` lists a device's tasks,
and each task in that list is read individually at the top-level route.

```sh theme={null}
curl -X POST https://api.omegas.dev/v1/devices/$DEVICE_ID/tasks \
  -H "Authorization: Bearer $OMEGAS_OPERATOR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4711-status-check" \
  -d '{"prompt": "Open the orders page and read the status of order 4711"}'
```

```sh theme={null}
curl https://api.omegas.dev/v1/tasks/$TASK_ID \
  -H "Authorization: Bearer $OMEGAS_OPERATOR_API_KEY"
```

One task per device: a second submit while one is live answers
`409 device_busy`. Submission accepts an `Idempotency-Key`, so a create that
times out and is retried replays the first result instead of starting twice;
see [conventions](/operator/conventions#idempotency).

A task carries its outcome in `result`: the agent's answer on success, the
failure payload on failure, and `null` while it runs. A caller is never told
only that a task failed with nothing to act on.

## Streaming events

`GET /v1/tasks/{id}/events` streams the task's steps as server-sent events,
each carrying a monotonic `seq`. Resume from the last `seq` you stored.

**The server closes a stream after 300 seconds by design.** It is poll-based
and holds no database connection between polls. A consumer must reconnect from
its last `seq`; one that treats the close as the end sees every long task
stall at exactly five minutes.

The TypeScript client does this for you, with reconnect bounds so a broken
deployment cannot turn your consumer into a hot loop:

```ts theme={null}
for await (const event of omega.tasks.events(task.id, { afterSeq: lastSeqYouStored })) {
  if (event.type === "step") save(event.seq, event.step);
  else return event.task; // terminal; the iterator ends here and only here
}
```

Reconnects that fail to advance the cursor are capped (default 5), total
reconnects are capped outright (default 500), every reconnect waits at least
100 ms with jitter, and a `429` on reconnect honours its `Retry-After`. If you
hand-roll the SSE loop, you own all of that.

## `needs_human` is not terminal

A task that hits a login, captcha, or two-factor wall parks as `needs_human`
and waits for a takeover. It is not finished and not failed; treat it as a
signal to put a person in front of the device via
[live view](/operator/live-view). The task resumes when the human releases
control.

## Cancel

```sh theme={null}
curl -X POST https://api.omegas.dev/v1/tasks/$TASK_ID/cancel \
  -H "Authorization: Bearer $OMEGAS_OPERATOR_API_KEY"
```

Cancellation is scoped `operator:tasks:write` and was exercised end to end in
the current preview build. Cancel *requests* the stop; the terminal transition
lands when the worker settles the task, so read the task back rather than
assuming the 202 finished the job.
