Tasks API
Task reads (list, detail, tree) live under /projects/{slug}/tasks. Most
agent-executed mutations (retry, cancel, bulk retry, queue purge, and worker
control) go through /projects/{slug}/commands so the brain can mint an
audit-chained command record. Bulk delete is the brain-local exception at
/projects/{slug}/tasks/bulk-delete.
List tasks
Section titled “List tasks”GET /api/v1/projects/{slug}/tasksRole: viewer.
| Query param | Type | Notes |
|---|---|---|
state |
string | One of pending, received, started, success, failure, retry, revoked, rejected, unknown. Invalid and empty values return 422. |
priority |
string | Comma-separated, e.g. critical,high. |
name |
string | Literal task-name substring. |
search |
string | Literal, case-insensitive substring across name, queue, worker, and task ID. |
queue |
string | Exact match. |
worker |
string | Exact match on worker_name. |
since / until |
RFC 3339 | Bound on received_at. |
cursor |
opaque | Pagination cursor from the prior page. |
limit |
int | Request validation accepts 1..5000, then normal JSON listing clamps it to Z4J_REST_MAX_PAGE_SIZE (default 500). |
format |
string | csv, xlsx, or json. Export mode ignores cursor pagination but returns at most Z4J_TASKS_EXPORT_MAX_ROWS; CSV/JSON silently stop at that cap, while XLSX fails above its separate 25,000-row memory cap. |
fields |
string | Comma-separated projection for export. |
Response (JSON mode):
{ "items": [ { "id": "01H...", "project_id": "...", "engine": "celery", "task_id": "9d2c...", "name": "email.send", "queue": "default", "state": "failure", "priority": "normal", "args": ["<redacted>"], "kwargs": {"to": "<email>"}, "result": null, "exception": "ValueError: ...", "traceback": "...", "retry_count": 0, "eta": null, "received_at": "...", "started_at": "...", "finished_at": "...", "runtime_ms": 142, "worker_name": "worker-1@host", "parent_task_id": null, "root_task_id": null, "tags": [], "created_at": "...", "updated_at": "..." } ], "next_cursor": "..."}Get task
Section titled “Get task”GET /api/v1/projects/{slug}/tasks/{engine}/{task_id}Role: viewer. Same TaskPublic shape as the list endpoint.
Get task tree (canvas / chain / group)
Section titled “Get task tree (canvas / chain / group)”GET /api/v1/projects/{slug}/tasks/{engine}/{task_id}/treeRole: viewer. Walks to the task's root via root_task_id (or treats the task itself as root if the field is null) and returns every task in the project sharing that root. Capped at 500 nodes; the response carries truncated: true when the cap kicks in.
Bulk delete
Section titled “Bulk delete”POST /api/v1/projects/{slug}/tasks/bulk-deleteRole: admin. CSRF-protected. Throttled by the shared bulk-action bucket used
by other destructive and expensive routes.
{ "filter_state": "failure", "filter_priority": ["critical", "high"], "filter_search": "email_%"}The request must select exactly one mode: a non-empty task_ids list of at
most 1000 task-row UUIDs, or at least one validated filter_* field. Empty or
all-null bodies, empty ID/priority lists, blank text filters, unknown state or
priority values, duplicate priorities, and mixed ID/filter requests return
422. Every mode is scoped to the path project.
Filter mode accepts filter_state, filter_priority, filter_search,
filter_name, filter_queue, filter_worker, filter_since, and
filter_until. Terms are intersected. filter_search mirrors list-search
semantics across task name, queue, worker, and task ID; filter_name searches
only task names. Both are literal substring filters, so %, _, and escape
characters do not broaden the selection. Filter mode deletes at most 10,000
rows in stable task-UUID order. Returns {"deleted_count": N}.
Commands (retry, cancel, bulk-retry, purge-queue)
Section titled “Commands (retry, cancel, bulk-retry, purge-queue)”Every action that needs an agent round-trip is issued as a command. The brain
inserts a row in commands, signs it, and delivers it over WebSocket or the
agent long-poll transport. The agent returns signed command_ack and
command_result frames over that same transport; there is no result POST
endpoint.
List commands
Section titled “List commands”GET /api/v1/projects/{slug}/commands?status=&cursor=&limit=Role: viewer. status is one of pending, dispatched, completed,
failed, timeout, or cancelled. An unknown filter value is currently
ignored and therefore returns all statuses.
Get command
Section titled “Get command”GET /api/v1/projects/{slug}/commands/{command_id}Role: viewer.
Retry one task
Section titled “Retry one task”POST /api/v1/projects/{slug}/commands/retry-taskRole: operator.
{ "agent_id": "...", "engine": "celery", "task_id": "9d2c...", "idempotency_key": "optional-string", "override_args": [], "override_kwargs": {"foo": "bar"}}Supply neither override field to retry by reference, or supply both
override_args and override_kwargs to replace the original inputs; a partial
override is rejected. Each field has a 64 KiB serialized cap, but the whole
request is also subject to the lower default HTTP body limit.
Cancel one task
Section titled “Cancel one task”POST /api/v1/projects/{slug}/commands/cancel-taskRole: operator.
{ "agent_id": "...", "engine": "celery", "task_id": "9d2c...", "idempotency_key": "optional-string"}Bulk retry
Section titled “Bulk retry”POST /api/v1/projects/{slug}/commands/bulk-retryRole: operator.
{ "agent_id": "...", "filter": {"task_ids": ["9d2c..."], "engine": "celery"}, "max": 1000, "idempotency_key": "optional-string"}This compatibility command route accepts only an explicit, non-empty
task_ids selection and requires a known engine; every selected ID must
belong to this project and engine. max is bounded 1..10000 and truncates the
deduplicated selection to its first max IDs. Filter-only/all-matching retries
return 410 and must use /api/v1/projects/{slug}/bulk-retry-requests.
Purge a queue
Section titled “Purge a queue”POST /api/v1/projects/{slug}/commands/purge-queueRole: admin.
{ "agent_id": "...", "queue": "default", "confirm_token": "<hmac of (queue_name, observed_depth)>", "force": false, "idempotency_key": "optional-string"}Clients may pass a precomputed confirm_token, or observed_depth so the brain
computes the keyed token, or force=true. The HTTP route also accepts none of
them and returns 202; in that case the agent refuses the command asynchronously.
force=true bypasses the token/depth guards. The Celery adapter logs that
bypass at CRITICAL; the RQ and Dramatiq paths do not currently make the same
logging guarantee.
Worker-control commands
Section titled “Worker-control commands”The same /commands router exposes five worker-control routes. Each accepted
command is audit-logged and signed, then delivered to the named agent over
WebSocket or long-poll.
Worker control is an operator capability, not an admin one. Only queue purge
is raised to admin, because it destroys queued work. Assign operator with
that in mind.
Restart a worker
Section titled “Restart a worker”POST /api/v1/projects/{slug}/commands/restart-workerRole: operator.
{ "agent_id": "...", "worker_name": "celery@worker-1", "idempotency_key": "optional-string"}For Celery this broadcasts pool_restart(reload=True): the parent worker stays
alive while its child pool is respawned. The broadcast is fire-and-forget and
requires Celery's pool-restart support; it is not proof that in-flight work was
preserved. Other adapters may refuse the operation or use the guarded
supervisor-based self-exit fallback.
Resize a worker's process pool
Section titled “Resize a worker's process pool”POST /api/v1/projects/{slug}/commands/pool-resizeRole: operator.
{ "agent_id": "...", "worker_name": "celery@worker-1", "delta": 2, "idempotency_key": "optional-string"}delta is bounded -100..100. Positive grows the pool, negative shrinks it. The
agent's engine adapter translates the delta into the engine-native call
(pool_grow / pool_shrink). Unsupported adapters still leave the HTTP call
at 202; the command later reaches status="failed" with an error string. The
wire result has status, not an ok field.
Add a consumer
Section titled “Add a consumer”POST /api/v1/projects/{slug}/commands/add-consumerRole: operator.
{ "agent_id": "...", "worker_name": "celery@worker-1", "queue": "billing", "idempotency_key": "optional-string"}Tells the worker to start consuming from the named queue.
Cancel a consumer
Section titled “Cancel a consumer”POST /api/v1/projects/{slug}/commands/cancel-consumerRole: operator. Same body shape as add-consumer. The worker stops consuming from queue but keeps running on its remaining queues.
Set a per-task rate limit
Section titled “Set a per-task rate limit”POST /api/v1/projects/{slug}/commands/rate-limitRole: operator.
{ "agent_id": "...", "task_name": "myapp.tasks.send_email", "rate": "100/m", "worker_name": "celery@worker-1", "idempotency_key": "optional-string"}rate follows Celery's grammar: integer optionally suffixed with /s, /m, or
/h; "0" clears the limit. The pattern is enforced server-side.
worker_name is optional in the request schema, but omission does not
currently broadcast: the dispatcher falls back to the command target id, which
for this route is the task name, and sends the control command to a worker with
that name. The command can therefore report success without changing the
intended fleet. No CRITICAL global-throttle log or audit flag is produced on
this path; pass an explicit worker name.
Agent targeting
Section titled “Agent targeting”Every worker-control command requires an agent_id, which the
agents API returns. Discover engine-native worker identifiers
(for example Celery's worker@host) from
GET /api/v1/projects/{slug}/workers. The separate
/api/v1/projects/{slug}/agent-workers collection describes z4j agent
processes, not engine-native workers; neither collection is nested under
/agents.