Opbox

Agent Tasks API

The AI agent runs as a queue of AgentTask rows. Tasks are produced by assignment, @mention, workflow trigger, or manual creation, and are consumed by one of two execution paths:

  • In-process worker. Runs inside the main app. Polls the queue every five seconds, claims tasks directly via the database, calls the Anthropic Messages API, and executes tools in process.
  • External agent bridge. A separate Claude Code CLI process. Claims tasks over HTTP using this API, streams progress, and reports completion.

Both paths compete for the same queue. Whoever successfully transitions a PENDING row to CLAIMED owns the task. Every claim carries an autonomy level that gates which tools the agent is allowed to invoke for the duration of the task.

Authentication

Two auth modes apply, depending on the endpoint.

Admin routes (list, get, create, cancel, retry) use the standard authenticated session with CSRF on mutations and require ADMIN or OWNER role on the active workspace.

Bridge routes (claim, progress, complete) use an MCP API key with the cp_live_ prefix:

POST /api/agent-tasks/:taskId/claim
Authorization: Bearer cp_live_REDACTED...

MCP keys are scoped to a workspace and carry a stored autonomy level (0 through 3). The claim response echoes the effective autonomy level so the bridge can enforce it locally. Every tool call the bridge makes back into Opbox over MCP is re-validated against this level server-side. Keys default to autonomy level 0 (read-only) at creation.

Bridge routes are rate-limited per API key: 60 requests per minute for claim and complete, 120 per minute for progress.

List and Get Tasks

MethodEndpointAuthDescription
GET/api/agent-tasksSession (ADMIN / OWNER)List tasks for the active workspace.
POST/api/agent-tasksSession (ADMIN / OWNER) + CSRFCreate a manual task.
GET/api/agent-tasks/:taskIdSession (ADMIN / OWNER)Full task details including result, error, timings.
PATCH/api/agent-tasks/:taskIdSession (ADMIN / OWNER) + CSRFCancel a task (only while PENDING, CLAIMED, or PROCESSING).
POST/api/agent-tasks/:taskId/retrySession (ADMIN / OWNER) + CSRFCreate a new task copying the original prompt. Only FAILED and TIMED_OUT tasks can be retried.

Query parameters on GET /api/agent-tasks:

ParameterTypeDescription
statusstringFilter by task status (see below).
limitnumberPage size. Default 50, max 100.
offsetnumberOffset for pagination.

Response:

{
  "tasks": [
    {
      "id": "cln_task_abc",
      "title": "Summarise matter progress",
      "status": "DONE",
      "priority": 1,
      "sourceType": "MANUAL",
      "sourceMatterId": null,
      "errorMessage": null,
      "createdAt": "2026-04-16T08:12:00.000Z",
      "startedAt": "2026-04-16T08:12:02.000Z",
      "completedAt": "2026-04-16T08:12:45.000Z",
      "createdBy": { "id": "cln_usr_u1", "name": "Will Lilley", "email": "will@example.com" },
      "agentUser": { "id": "cln_usr_agent", "name": "Workspace Agent" }
    }
  ],
  "total": 142,
  "limit": 50,
  "offset": 0
}

Create a manual task

POST /api/agent-tasks
x-csrf-token: <token>
{
  "title": "Draft engagement letter for Acme",
  "prompt": "Look up the Acme Holdings matter, extract engagement terms from the pipeline field map, and draft a short engagement letter using the standard template.",
  "priority": 2
}

Response 201 Created:

{
  "id": "cln_task_xyz",
  "title": "Draft engagement letter for Acme",
  "status": "PENDING",
  "priority": 2,
  "sourceType": "MANUAL",
  "createdAt": "2026-04-16T09:30:00.000Z"
}

Prompts are scanned for prompt-injection patterns at creation time. Detected injections reject the task with 400 Bad Request. The workspace must have the AI Agent addon installed (which provisions the agent user) or creation fails with 400.

Claim, Update, Complete

These routes are the contract between Opbox and the external bridge. The in-process worker bypasses them entirely and transitions state via the database.

MethodEndpointDescription
POST/api/agent-tasks/:taskId/claimAtomic claim. Transitions PENDING to CLAIMED, mints a one-time claimToken, returns the task payload.
PATCH/api/agent-tasks/:taskId/progressUpdate progressText. First call promotes CLAIMED to PROCESSING and stamps startedAt. Requires claimToken.
PATCH/api/agent-tasks/:taskId/completeTerminal transition. Mark DONE or FAILED. Requires claimToken. Runs post-completion side effects (matter comment, audit log).

Claim

POST /api/agent-tasks/:taskId/claim
Authorization: Bearer cp_live_REDACTED...

Response 200 OK:

{
  "claimToken": "8e15bb6e-3a22-4fd1-9a8b-1f63c5f8e0a4",
  "task": {
    "id": "cln_task_xyz",
    "title": "Draft engagement letter for Acme",
    "prompt": "Look up the Acme Holdings matter...",
    "priority": 2,
    "sourceType": "MANUAL",
    "sourceMatterId": null,
    "sourceStepId": null,
    "depth": 0,
    "autonomyLevel": 2,
    "workspaceName": "Acme Corporate Services"
  }
}
  • The claim is atomic: only a task that is still PENDING and belongs to the authenticated workspace succeeds. Anything else returns 409 Conflict.
  • claimToken is a random UUID. Only its SHA-256 hash is persisted. The bridge must carry the raw token on all subsequent calls for this task.
  • Tasks auto-time-out ten minutes after claim if no complete call arrives.

Progress

PATCH /api/agent-tasks/:taskId/progress
Authorization: Bearer cp_live_REDACTED...
{
  "claimToken": "8e15bb6e-3a22-4fd1-9a8b-1f63c5f8e0a4",
  "progressText": "Reading matter record cln_matter_acme123..."
}

The first progress call flips status CLAIMED -> PROCESSING and stamps startedAt. Subsequent calls only update progressText so the UI can stream live output.

Complete

PATCH /api/agent-tasks/:taskId/complete
Authorization: Bearer cp_live_REDACTED...
{
  "claimToken": "8e15bb6e-3a22-4fd1-9a8b-1f63c5f8e0a4",
  "status": "done",
  "result": "Drafted engagement letter and posted to the matter as a comment.",
  "costUsd": 0.047,
  "durationMs": 38200,
  "toolCallCount": 12
}

Response 200 OK:

{
  "success": true,
  "taskId": "cln_task_xyz",
  "status": "DONE"
}
  • status is "done" or "failed".
  • errorMessage is required when status is "failed".
  • On DONE, post-completion side effects run fire-and-forget: matter comment (if the task was matter-sourced) and audit log entry.

Task Statuses

StatusMeaning
PENDINGQueued. Eligible for claim.
CLAIMEDA worker or bridge has claimed the task. claimToken issued.
PROCESSINGFirst progress update received. startedAt stamped.
DONECompleted successfully. result populated.
FAILEDExecution raised an error. errorMessage populated.
CANCELLEDCancelled by an admin before completion.
TIMED_OUTNo complete call within ten minutes of claim.

Example: bridge walkthrough

# 1. List PENDING tasks
curl "https://opbox.app/api/agent-tasks?status=PENDING&limit=10" \
  -H "Authorization: Bearer cp_live_$KEY"

# 2. Claim one
curl -X POST https://opbox.app/api/agent-tasks/cln_task_xyz/claim \
  -H "Authorization: Bearer cp_live_$KEY"
# -> { "claimToken": "<uuid>", "task": { "prompt": "...", "autonomyLevel": 2, ... } }

# 3. Stream progress as work happens
curl -X PATCH https://opbox.app/api/agent-tasks/cln_task_xyz/progress \
  -H "Authorization: Bearer cp_live_$KEY" \
  -H "Content-Type: application/json" \
  -d '{"claimToken":"<uuid>","progressText":"Pulling matter context..."}'

# 4. Complete
curl -X PATCH https://opbox.app/api/agent-tasks/cln_task_xyz/complete \
  -H "Authorization: Bearer cp_live_$KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "claimToken":"<uuid>",
    "status":"done",
    "result":"Posted a summary comment on the matter.",
    "costUsd":0.031,
    "durationMs":22400,
    "toolCallCount":7
  }'

Rate Limits and Autonomy

Every MCP call the agent makes back into Opbox is re-checked against the API key's stored autonomy level. The level set on the key is the ceiling; individual tasks cannot escalate it at runtime.

LevelAccessTypical uses
0Read-only. All write and destructive tools blocked.Safe summarisation, reporting, ad-hoc queries.
1Low-risk writes (comments, notifications, draft updates).Triage, routing, tagging.
2Full data writes scoped to the workspace.Matter work, record updates, linking.
3Destructive operations (delete, cancel, bulk update).Reserved for explicit admin authorisation.

Additional guard rails:

  • Task depth cap. A task spawned by another task increments depth. A hard ceiling of 5 prevents runaway self-spawning.
  • Prompt-injection scanning. Applied at task creation and on every tool result re-entering the model context. Detected injections are sanitised rather than silently dropped.
  • Bridge rate limits. 60/min for claim and complete, 120/min for progress - both per API key. Excess returns 429 Too Many Requests.
  • Per-tool tier limits. Applied by the MCP layer on top of the per-key bridge limits above.
  • Claim timeout. A claimed task that receives no terminal complete call within ten minutes is automatically marked TIMED_OUT and becomes retryable.

Status Codes

StatusMeaning
200 OKSuccessful read, progress update, or completion.
201 CreatedManual task or retry created.
400 Bad RequestValidation failure, prompt injection detected, or retry attempted on a non-terminal task.
401 UnauthorizedMissing or invalid MCP key, or missing session on an admin route.
403 ForbiddenRole check failed on an admin route.
404 Not FoundTask does not exist or belongs to another workspace.
409 ConflictAtomic claim lost (task already claimed, wrong status, or invalid claimToken).
429 Too Many RequestsBridge per-key rate limit exceeded.
500 Internal Server ErrorUnexpected error. Side effects on completion are fire-and-forget and will not produce a 500 on the bridge call.