agent-taskbay
Reference

HTTP API reference

For people who script against Agent Taskbay or build on it: every route under /api, with its method, who may call it, what it accepts and what it

> For people who script against Agent Taskbay or build on it: every route under /api, with its method, who may call it, what it accepts and what it > returns. Examples marked "Observed" were run against a local development server.

There is no stable API yet. The routes exist to serve the console. They are not versioned (no /api/v1), have no deprecation policy, and may change in any minor release while the project is pre-1.0 (package version 0.2.0). A versioned, token-authenticated client API is proposed in #31. Until then, pin a version if you depend on a route.

The "Use" column in the route index is a reading aid, not a promise: Integration routes are the ones a script is most likely to want and that the console uses as plain request/response APIs; Console routes are UI plumbing (sign-in, live updates, access management) and are the most likely to change.

On this page: Authentication and roles · Conventions · Route index · Sign-in routes · Agents · Sending messages and commands · Tasks · Task workflow · Inbox · Approvals · Notifications · Audit · Access management · Artifacts · Push webhook · Limits

Authentication and roles

Every route except the three sign-in routes and the push webhook requires a signed-in person.

  • Session cookie only. There is no API key, bearer token or service-token authentication for these routes. In oidc mode the session cookie (__Host-a2a-session, HttpOnly, Secure, SameSite=Lax) is issued after browser sign-in. In development mode every request is the local administrator and needs no cookie. See Sign in and roles.

  • Roles. Each route is guarded by one of three permissions:

    PermissionAllowed rolesMeaning
    readadmin, operator, viewerLook at agents, tasks, approvals, inbox, audit for one's own scope
    operateadmin, operatorSend messages, claim and assign work, open and decide approvals
    administeradminRegister agents, manage teams and grants, escalation policy, notification channel, organization audit
  • Per-agent grants. Roles are a ceiling. Administrators see every agent. For operator and viewer, an agent (or one skill of it) is visible only through an enabled grant whose subject is the organization, the person or one of their teams. read needs any grant; operate needs an operate grant and a role that can operate. A resource outside the caller's grants answers as if it does not exist (404), not 403. A skill-scoped grant never authorizes a generic send.

  • Order of checks. Sign-in (401), role (403), rate limit (429), then, for non-GET requests, the origin check, then the handler.

Origin check (CSRF)

Requests other than GET, HEAD and OPTIONS are rejected with 403 (This action is not permitted.) when:

  • the Sec-Fetch-Site header is cross-site, or
  • in oidc mode the Origin header is missing or is not exactly A2A_AUTH_ORIGIN, or
  • in development mode an Origin header is present that is neither the request's own origin nor a loopback origin with the same scheme and port.

A script that is not a browser must therefore send an Origin header equal to the configured origin, and it can only do so with a valid session cookie copied from a browser. That is an awkward workflow, and the reason a token API is planned.

Conventions

TopicBehavior
FormatJSON in, JSON out, Content-Type: application/json. Request bodies larger than 16 MiB are rejected with 400. A missing body is 400 Request body is required.; invalid JSON is 400 Invalid request JSON.
CachingResponses carry Cache-Control: no-store.
IDsLocal IDs are UUIDs generated by Agent Taskbay. A task has a local ID (localId, used in URLs) and a remote ID (taskId from the agent); they are different. Remote IDs are only unique within (agent, tenant).
TimestampsISO 8601 UTC strings.
IdempotencyMutations that start durable work take an Idempotency-Key header (1 to 255 characters). Reusing a key with identical content returns the original result; with different content it returns 409.
PaginationInbox, notifications, audit and task views use opaque or UUID cursors and return next (null on the last page). The task list uses limit and offset.
Unknown JSON keysMost request schemas are strict: an unrecognized key is a 400.

Error shapes

Errors use one of these shapes. Messages are written for people and may be reworded; match on the status code.

ShapeWhere
{"error":{"message":"..."}}Sign-in, role and rate-limit failures; many validation and not-found errors.
{"error":{"name":"Error","message":"..."}}Errors raised by services and caught by the route helper.
{"error":"..."}The push webhook only.
Plain text Not foundGET /api/artifacts/{digest}.

Common statuses:

StatusMeaning
400Validation failure (message names the problem).
401Sign in to continue. No valid session.
403This action is not permitted. Role, grant or origin check failed; some services return a specific reason.
404Unknown or not visible.
409State conflict: idempotency key reuse with different content, closed or expired approval, task already finished, task already owned.
422Well-formed but not allowed: assignee or delegate cannot operate the agent, an edit that changes nothing.
429Rate limit. Too many requests. Try again shortly.
502Upstream agent error on routes that call the agent directly (for example agent discovery).
503Identity service unavailable. (sign-in or database problem).
504A blocking route stopped waiting; the work continues (see /messages).

Rate limits

Fixed 60-second windows in the database, so they hold across web replicas.

BucketLimit per window
Per person, read routes900
Per person, operate routes120
Per person, administer routes60
GET /api/auth/login and /api/auth/callback, all callers together240 each
POST /api/webhooks/a2a/{id}, all callers together1200

When a limit is hit the route answers 429. The webhook adds Retry-After: 60. The limits are not configurable.

Route index

Method and pathPermissionUseSummary
GET /api/auth/loginpublicConsoleStart sign-in (redirect)
GET /api/auth/callbackpublicConsoleOIDC redirect target
POST /api/auth/logoutreadConsoleEnd the session
GET /api/auth/sessionreadConsoleWho am I, and the auth mode
GET /api/agentsreadIntegrationCatalog with discovered cards
POST /api/agentsadministerIntegrationRegister an agent by Agent Card URL
POST /api/agents/previewadministerConsoleFetch and validate a card without registering
GET /api/agents/{agentId}readIntegrationOne agent with its card and trust status
DELETE /api/agents/{agentId}administerIntegrationRemove a managed agent
POST /api/agents/{agentId}/commandsoperateIntegrationSend a message or cancel (durable, returns 202)
GET /api/commands/{commandId}readIntegrationCommand status and result
POST /api/agents/{agentId}/messagesoperateIntegrationSend and wait up to 50 s
POST /api/agents/{agentId}/streamoperateConsoleSend or resubscribe, as server-sent events
POST /api/agents/{agentId}/tasks/{taskId}/canceloperateIntegrationCancel by remote task ID, and wait
POST /api/agents/{agentId}/ag-uioperateIntegrationAG-UI run (off unless enabled); see AG-UI
GET /api/tasksreadIntegrationTask list
GET /api/tasks/{taskId}readIntegrationTask detail
GET /api/task-viewsreadConsolePaged task content for chat and flows
GET /api/tasks/eventsreadConsoleLive "something changed" signal (SSE)
GET /api/tasks/{taskId}/workflowreadIntegrationOwner, due time, history, notes
POST /api/tasks/{taskId}/workflow/claimoperateIntegrationTake an unowned task
POST /api/tasks/{taskId}/workflow/releaseoperateIntegrationGive a task up
POST /api/tasks/{taskId}/workflow/assignoperateIntegrationAssign to a person
POST /api/tasks/{taskId}/workflow/dueoperateIntegrationSet or clear the due time
POST /api/tasks/{taskId}/workflow/notesoperateIntegrationAdd an internal note
GET /api/inboxreadIntegrationTasks and approvals the caller may act on
GET /api/decisionsreadIntegrationList approval requests
POST /api/decisionsoperateIntegrationOpen an approval request
GET /api/decisions/{decisionId}readIntegrationRequest with revisions, decisions, executions
POST /api/decisions/{decisionId}/revisionsoperateIntegrationPropose a new revision
POST /api/decisions/{decisionId}/decisionsoperateIntegrationApprove, reject, edit, request changes or delegate
GET /api/reviewersoperateConsolePeople who could review a task
GET /api/escalation-policiesadministerConsoleEscalation targets
PUT /api/escalation-policiesadministerConsoleSet an escalation target
GET /api/notificationsreadIntegrationThe caller's in-app notifications
POST /api/notifications/readreadConsoleMark notifications read
GET /api/notifications/channeladministerConsoleWebhook channel status
POST /api/notifications/testadministerConsoleQueue a test notification
GET /api/auditreadIntegrationAudit trail
GET /api/audit/exportadministerIntegrationOne CSV page of the audit trail
GET /api/admin/securityadministerConsoleTeams, grants, credential metadata
POST /api/admin/securityadministerConsoleCreate teams, grant and revoke access
GET /api/artifacts/{digest}readIntegrationDownload a stored artifact
POST /api/webhooks/a2a/{registrationId}push credentialAgents onlyA2A push notification receiver

Route handlers run on the Node.js runtime. Task and event content shown here is what Agent Taskbay has stored, not a live read from the agent.

Sign-in routes

GET /api/auth/login

Public. In development mode it answers 303 to /tasks. In oidc mode it stores encrypted login state for 10 minutes, sets the __Host-a2a-login cookie and answers 303 to the provider. Query returnTo is honored only if it is a relative path starting with a single / (otherwise /tasks). Errors: 429 Too many sign-in requests.; 503 Sign-in service unavailable. (misconfiguration or provider unreachable).

Observed (development mode): 303, location: http://localhost:<port>/tasks.

GET /api/auth/callback

Public. Completes the authorization-code flow with PKCE, requires an enabled membership for the configured organization, rotates any previous session and answers 303 to the validated returnTo on the configured origin with the session cookie set. Every failure, including an expired or reused attempt, an unknown person and a provider error, is the same 401 Sign-in failed. Start a new sign-in attempt.; provider responses are never reflected. Rate limited (429).

POST /api/auth/logout

read. Revokes the session, records session.ended and clears the cookie. Response 200 {"signedOut":true}.

GET /api/auth/session

read. Observed (development mode):

{ "user": { "displayName": "Local operator", "role": "admin" }, "mode": "development" }

mode is development or oidc. The launcher polls this route to know the server is up.

Agents

GET /api/agents

read. Fetches every visible agent's Agent Card (this makes outbound requests and records a card snapshot per agent) and returns { "agents": [ ... ] }. Each entry has id, cardUrl, source (env or managed) and either name, description, skills, card, trust or, when discovery failed, error. A failing agent stays in the list. trust is unsigned, verified, untrusted or invalid. Non-administrators receive a reduced card for agents they only partly hold (see Authentication and roles).

Calling this route is also what stores the card snapshot that extension negotiation reads; see the note there.

POST /api/agents

administer. Body { "cardUrl": "<string>" }. Registers the agent; it does not fetch the card. Response 201 { "agent": { "id", "cardUrl", "source": "managed" } }. Registering the same URL again returns the same agent (and re-enables it if it was removed). 400 for a missing cardUrl, a URL that is not http(s), or one with credentials, a query string or a fragment. The network policy (origin allowlist, private addresses) is applied when the card is first fetched, not at registration. The audit trail records agent.registered.

Observed: {"agent":{"id":"e6e890ae-...","cardUrl":"http://127.0.0.1:4011/showcase/card.json","source":"managed"}}.

POST /api/agents/preview

administer. Body { "cardUrl": "<string>" }. Fetches and validates a card without credentials and without registering it. Response { "card", "resolvedCardUrl", "report" } where report is the compliance report (version 1.0, 0.3 or unknown, a score and a list of issues). Does not record a snapshot. The card URL may be the card itself (a path ending in .json) or a base URL, in which case /.well-known/agent-card.json is tried.

GET /api/agents/{agentId}

read. { "id", "cardUrl", "source", "card", "trust", "report" }. 404 if unknown or not visible. Failures talking to the agent surface as 502 (or the status of the underlying error). Records a card snapshot.

DELETE /api/agents/{agentId}

administer. 200 { "removed": true }. 409 with This agent comes from A2A_REGISTERED_AGENTS and can't be removed from the UI. for environment-seeded agents.

Sending messages and commands

All sends are recorded as a durable command first, then dispatched by a worker. The routes below differ in how long they wait.

Command body

POST /api/agents/{agentId}/commands, /messages and /stream accept the same strict object:

FieldTypeNotes
action"send" or "cancelTask"Default send.
textstringPlain text. Sent as a text/plain part before any parts.
partsarray of objectsA2A v1 part objects, for example { "text": "...", "mediaType": "text/plain" } or { "data": { ... }, "mediaType": "application/json" }. A send needs non-empty text or at least one part.
taskIdstringRemote task ID. Required for cancelTask; on send it continues that task.
contextIdstringRemote context ID. Must match the task's context when taskId is given (403 Task context mismatch.).
tenantstring, at most 255Default empty. Part of the task's identity.
messageIdstring, 1 to 255Becomes the A2A messageId; generated if omitted.
skillIdstring, 1 to 255Restrict the send to one skill. Needs a skill-routing agent; see Skill routing.
configobjectreturnImmediately, historyLength, acceptedOutputModes, referenceTaskIds, extensions, metadata, requestMetadata. Strict. Referenced tasks must be known (404 Referenced task not found.).

Server rules: the console always asks the agent to return immediately and observes progress separately; extensions the agent advertises and Agent Taskbay understands are negotiated automatically; a task's skill and context cannot be changed by a later message; a scoped caller cannot start a send in an existing context.

POST /api/agents/{agentId}/commands

operate. Requires the Idempotency-Key header (400 An idempotency key of 1–255 characters is required. without it). Returns immediately.

curl -X POST http://127.0.0.1:3002/api/agents/AGENT_ID/commands \
  -H 'Content-Type: application/json' -H 'Idempotency-Key: demo-1' \
  -d '{"text":"hello"}'

Observed: 202 Accepted, header Location: /api/commands/<id>, body:

{ "command": { "id": "69c44ee5-700f-47ab-aafd-193b19fc94d2", "agentId": "e6e890ae-...", "tenant": "", "action": "send",
  "messageId": "27a02477-...", "status": "pending", "result": null, "error": null,
  "createdAt": "2026-10-06T20:04:40.363Z", "updatedAt": "2026-10-06T20:04:40.363Z" } }

Repeating the request with the same key and body returns 202 with the same command and its current state. Same key with different content: 409 This idempotency key already belongs to different command content. Unknown agent: 404 Unknown agent. Unknown keys or an empty send: 400 with the validation message.

Command status values: pending, dispatching, succeeded, failed, uncertain. uncertain means the request may have reached the agent and the result is unknown; Agent Taskbay does not resend automatically. When succeeded, result is { event, localId, taskId, tenant }, where localId is the task's local ID and event is the agent's reply (task or message).

GET /api/commands/{commandId}

read. { "command": { ... } } as above. 404 for an unknown or invisible command. Poll this after a 202.

POST /api/agents/{agentId}/messages

operate. Compatibility route: submits the command, waits up to 50 seconds, and returns the result. The idempotency key defaults to the messageId when the body has one (so retries are safe), otherwise to a random value (retries are not safe).

Success 200: { "result": <task or message>, "localId", "commandId", "sessionId", "requestId", "telemetry": [] }. 409 when the command failed or ended uncertain (the message carries the reason). 504 when still pending after 50 seconds: Command accepted; waiting stopped. Query /api/commands/<id> for its outcome before submitting again. The command continues; do not resubmit.

POST /api/agents/{agentId}/tasks/{taskId}/cancel

operate. {taskId} here is the remote task ID. Optional query ?tenant=. Optional Idempotency-Key (random if absent, so a retry is not de-duplicated). Waits like /messages and returns the same shape; the result is the agent's updated task.

POST /api/agents/{agentId}/stream

operate. Server-sent events (Content-Type: text/event-stream, Cache-Control: no-store, no-transform). Body is a command body, or { "resubscribe": true, "taskId": "<remote id>", "tenant": "" } to follow an existing task. Events come from committed state, not from the agent's own stream, so a dropped browser connection never loses or repeats work.

EventData
accepted{ "commandId" } (new sends only)
persisted{ "localId", "taskId", "tenant" }
snapshotThe task view, sent on first read and whenever the task version changes
metaProtocol metadata for the next a2a event, when recorded
a2aOne stored protocol event from the agent
sidebandA sideband event decoded from a negotiated extension
error{ sessionId, requestId, message }
end{ sessionId, requestId }

The stream ends with end when the task is paused (input required, auth required) or finished, after about 50 seconds, or on error. The task keeps running; reconnect with resubscribe. Pre-stream problems are ordinary HTTP errors (400, 403, 404). Observed event order for a new send: accepted, persisted, snapshot.

Tasks

GET /api/tasks

read. Query:

ParameterDefaultValues
filterallall, active, needs-input, done, mine, overdue, unassigned. active is any non-terminal state; needs-input is input-required or auth-required; done is terminal; the last three use ownership.
limit50Integer 1 to 100.
offset0Integer 0 or more.

Response { "tasks": [ ... ] }, newest first. Rows carry localId, taskId, tenant, agentId, agentName, state (TASK_STATE_*), kind, title, contextId, createdAt, updatedAt, referenceLinks, workflow (null or { assigneeMembershipId, assigneeName, dueAt, escalationLevel }), and empty messages and artifacts arrays; use the detail route for content. Observed: {"tasks":[{"localId":"7470c984-...","taskId":"form-task-0","state":"TASK_STATE_INPUT_REQUIRED","kind":"task","title":"hello", ...,"workflow":null}]}. Bad filter: 400 Invalid task filter.

GET /api/tasks/{taskId}

read. {taskId} is the local ID. { "task": <view> } with messages (each with id, role user or agent, normalized parts, timestamp, fromStatus for the agent's question), artifacts, transitions, version and the fields above. 404 Task not found.

GET /api/task-views

read. Pages durable content by local task ID, for chat and flow views. limit 1 to 100 (default 100) and after (a local UUID) are validated: 400 limit must be 1–100 and after must be a local UUID. Response { "tasks": [ <view> ], "next": "<local id>" | null }.

GET /api/tasks/events

read. A long-lived SSE signal that "something changed for this organization". Frames carry no task data (data: {}); clients re-query. Event names: ready (first frame), freshness (a change was committed), resync (sent after 15 seconds of silence so clients refresh anyway). Each frame is preceded by retry: 1000. The server closes the stream after 55 seconds; reconnect.

Task workflow

Ownership is local to Agent Taskbay and independent of the remote task state. All routes need operate on the task's agent and skill, and apply only to A2A tasks (not direct-message threads: 409 Only A2A tasks can be assigned.). Responses are { "assignment": { assigneeMembershipId, claimedAt, dueAt, escalationLevel, escalatedAt, updatedAt } } (or null fields). Rules: anyone with access may claim an unowned task; only the owner or an administrator may release, reassign or change the due time of an owned task (403); a claim on a task owned by someone else is 409 This task is already owned by someone else.; claiming, assigning and setting a due time need an unfinished task (409 The task is already finished.).

RouteBodyNotes
GET /api/tasks/{taskId}/workflow (read)none{ assignment, events, notes, people, viewer }. Events are newest first (at most 50), notes at most 100. Invalid UUID or unknown task: 404.
POST .../workflow/claimnoneIdempotent for the current owner.
POST .../workflow/releasenoneNo-op when unowned.
POST .../workflow/assign{ "assigneeMembershipId": "<uuid>" }422 The assignee cannot operate this agent. when the person lacks an operate grant. Notifies the assignee.
POST .../workflow/due{ "dueAt": "<ISO datetime>" or null }Must be in the future and within one year (400). A new due time re-arms escalation.
POST .../workflow/notes{ "body": "<1-4000 chars>", "noteKey": "<1-255 chars>" }201 { "note": { id, authorUserId, body, createdAt } }. Repeating a noteKey with the same body returns the same note; a different body is 409. Notes are internal and never sent to the agent.

Every change is recorded in the audit trail. Observed: claim returned {"assignment":{"assigneeMembershipId":"89254850-...","claimedAt":"...","dueAt":null,"escalationLevel":0,"escalatedAt":null,"updatedAt":"..."}}.

Escalation policy

GET /api/escalation-policies (administer) returns { "policies": [ { id, agentId, targetMembershipId, enabled, updatedAt } ], "people": { ... } }. PUT takes { "agentId": "<uuid>" or null, "targetMembershipId": "<uuid>", "enabled": true|false } (agentId: null applies to every agent) and answers { "policy": { ... } }. The target must be an active operator or administrator (422). When a task passes its due time and a policy exists, a worker reassigns it to the target once per due time. With no policy it only shows as overdue.

Inbox

GET /api/inbox

read. One page of tasks and approval requests the caller may read, newest update first.

ParameterDefaultValues
viewallall, active, needs-input, assigned, overdue, done
kindanytask, approval
agentIdanyUUID
skillIdanyAt most 255 characters
riskanylow, medium, high (approvals)
statusanyTask state or approval status, at most 64 characters
updatedAfternoneISO timestamp
cursornoneThe next value of the previous page
limit30Integer 1 to 100

Response { "items": [ ... ], "next": "<cursor>" | null }. Each item: kind, id (task local ID or request ID), taskId, agentId, agentName, skillId, status, open, title, risk, assigneeMembershipId, assigneeName, dueAt, expiresAt, escalationLevel, updatedAt. Errors are 400 (Unknown inbox view., Invalid cursor., ...). No event payloads are read, so the route stays cheap on large histories.

Approvals

An approval request asks a reviewer to approve one exact action on a task. The decision is bound to the revision the reviewer saw. Concepts: Human in the loop.

GET /api/decisions

read. Query status (pending, approved, rejected, changes_requested, expired, superseded; else 400 Unknown status.), taskId, and assigned=me. Returns up to 100 requests, { "decisions": [ <request> ] }, each with the agent and task context (agentName, taskTitle, taskState). There is no paging.

A request has id, requesterUserId (null when the agent asked), taskId, agentId, tenant, skillId, kind (send_message or send_data), status, title, summary, risk, policy ({ allowedOutcomes, separationOfDuties }), assignedMembershipId, currentRevision, expiresAt, createdAt, updatedAt.

POST /api/decisions

operate. Opens a request (a person proposing an action for review).

FieldRequiredNotes
taskIdyesLocal task UUID.
requestKeyone of this or the Idempotency-Key header1 to 255 characters. The body field wins.
titleyes1 to 300 characters.
summarynoAt most 4000 characters.
risknolow, medium (default), high.
actionyes{ "kind": "send_message", "text": "<1-20000 chars>", "data": { } } or { "kind": "send_data", "form": { ... }, "values": { ... } }. For send_data the form must be in the structured-form subset (at most 64 KB) and values must validate against it. Message data is at most 64 KB.
expiresAtyesISO datetime in the future and at most 30 days ahead.
assignedMembershipIdnoA person who can operate the agent (422 otherwise).
policyno{ "allowedOutcomes": [...], "separationOfDuties": true }. Outcomes are approve, reject, edit, request_changes, delegate; at least approve or edit must be allowed. separationOfDuties defaults to true.

Opening a request marks any older open request on the same task superseded: one approval is live per task. Response 201 { "decision": <request> } with Location: /api/decisions/<id>; 200 when the same key and content already created it. Errors: 404 unknown task, 409 task finished or not an A2A task or key reused for a different request, 422 assignee. Observed: a minimal body (taskId, title, expiresAt, action) produced status: "pending", risk: "medium", currentRevision: 1 and the default policy.

GET /api/decisions/{decisionId}

read. { "request", "revisions", "decisions", "executions", "people", "viewer" }. Revisions carry number, action, digest, authorType (agent, user, system). Decisions carry outcome, rationale, revisionId, revisionDigest, reviewer and delegate IDs. Executions correlate each approval with the command it produced (status, observedTaskState). Reading refreshes execution status from the task. A non-UUID or invisible ID is 404 Decision request not found.

POST /api/decisions/{decisionId}/revisions

operate. { "action": <action>, "expectedRevision": <int >= 1> }. Adds a revision (same action kind), resets the request to pending and voids earlier reviews. 201 { "decision": <request>, "revision": { id, number, action, digest, authorType, authorUserId, createdAt } }. 409 when closed, expired or expectedRevision is stale; 422 for a different kind.

POST /api/decisions/{decisionId}/decisions

operate. Records one decision. Requires Idempotency-Key. Body:

FieldRequiredNotes
outcomeyesapprove, reject, edit, request_changes, delegate.
expectedRevisionyesMust equal the request's current revision (409 A newer revision exists; review it before deciding.).
rationalerequired except for approveAt most 4000 characters.
editwith edit onlyThe edited action. May change values but not the form; must differ from the current proposal (422).
delegateMembershipIdwith delegate onlyNot yourself; must be able to operate the agent (422).

Rules: the request must be pending and unexpired; the policy must allow the outcome (403); with separationOfDuties the requester cannot decide (403 The requester cannot decide their own request.); an assigned request can be decided only by the assignee or an administrator. approve and edit send the exact revision to the agent once, as a command with metadata.approval (requestId, decisionId, revision, revisionDigest); reject and request_changes send nothing. Approving a request whose task has finished, or deciding after expiry, closes it (superseded or expired) and answers 409 This request is <reason> and can no longer authorize an action.

Response 201 { "decision", "request", "execution", "replay": false }; a replay of the same key returns 200 with "replay": true. A key reused with a different decision is 409. Observed: self-deciding with the default policy returned 403, and a reject without a rationale returned 400 A rationale is required for this decision.

GET /api/reviewers

operate. Query taskId (local UUID). { "reviewers": [ { membershipId, displayName, role, self } ] }: active admins and operators who may operate that task's agent and skill. 404 Task not found.

Notifications

GET /api/notifications

read. The caller's own notifications. Query unread (true/false, default false), limit (1 to 100, default 30), cursor. Response { "items": [ { id, kind, title, body, link, taskId, createdAt, readAt } ], "next", "unread" }. Items about tasks the person can no longer read are hidden. Kinds: approval.requested, approval.revised, approval.assigned, approval.decided, approval.expiring, approval.expired, approval.superseded, task.assigned, task.escalated, task.needs_input, task.finished, task.failed, test. Unknown query keys are 400.

POST /api/notifications/read

read. Body { "ids": [ "<uuid>", ... ] } (1 to 100) or { "all": true }, not both (400 Provide either ids or all.). Response { "updated": <count> }. Only the caller's own rows are affected.

GET /api/notifications/channel

administer. Webhook status: { configured, enabledForOrganization, host, counts: { pending, processing, delivered, failed }, lastFailure: { at, message } | null }. Observed with no webhook set: configured: false.

POST /api/notifications/test

administer. Queues a test notification through the real pipeline, including the webhook. 202 { "queued": true }.

Audit

GET /api/audit

read. Query taskId (UUID), group (all, approvals, ownership, commands, access), actor (user UUID), since and until (ISO datetimes), cursor, limit (1 to 200, default 50). Without taskId the caller must be an administrator (403). With taskId any member who can read that task's agent sees that task's trail, without identity and access events (404 if not visible). Response { entries: [ { key, at, kind, actorUserId, taskId, subjectId, data } ], people, context, next, viewer }. since after until is 400. Unknown query keys are 400.

GET /api/audit/export

administer. Same query; limit defaults to 200. Returns text/csv with columns time, kind, actor, task, subject, detail as an attachment named audit-trail.csv. When more rows exist the header X-Next-Cursor carries the cursor to pass as cursor. Cells that start with =, +, -, @, tab or carriage return are prefixed with ' so spreadsheets do not execute them. The audit trail is append-only: database triggers reject updates and deletes.

Access management

GET /api/admin/security

administer. { organizationId, teams, memberships, teamMembers, grants, credentials }. Credential entries show only agentId, kind, enabled and updatedAt; secrets are never returned.

POST /api/admin/security

administer. A strict, discriminated body on action; success is { "updated": true }, a malformed body is 400 Invalid access management request., and other failures are 400 with a message.

actionFields
createTeamname (1 to 200)
addTeamMember, removeTeamMemberteamId, membershipId
disableTeamteamId
grantagentId, skillId (string or null for the whole agent), subjectType (organization, membership, team), subjectId, permission (read, operate)
revokeGrantgrantId

All changes are audited. Credential values are managed with npm run security:credentials, not through the API.

Artifacts

GET /api/artifacts/{digest}

read. {digest} is a 64-character lowercase hex SHA-256. Returns the stored bytes only if the caller can read a task that references that artifact; otherwise 404 Not found (plain text). The response is always application/octet-stream with Content-Disposition: attachment; filename="artifact", X-Content-Type-Options: nosniff and Content-Security-Policy: default-src 'none'; sandbox, so stored content is never rendered by the browser from this route. Objects are limited to 16 MiB each.

Push webhook (agents only)

POST /api/webhooks/a2a/{registrationId}

Not for people. This is the callback that Agent Taskbay registers with agents that support A2A push notifications when A2A_PUSH_CALLBACK_ORIGIN is set. It does not use a session. The caller must send Authorization: Bearer <token> where the token is the 43-character value Agent Taskbay derived for that registration. The body must be JSON with content type application/json or application/a2a+json. An optional X-A2A-Delivery-Id header (printable ASCII, at most 255 characters) de-duplicates repeats.

StatusMeaning
204Accepted (also returned for late deliveries to a finished task, which change nothing).
400Invalid body or delivery ID.
401Invalid push authentication. Wrong, missing or revoked credential, or a deleted/failed registration.
415Push requires a JSON content type.
429Rate limited (Retry-After: 60).
503Push delivery is not configured. (observed when A2A_PUSH_CALLBACK_ORIGIN is unset) or Push receipt unavailable; retry later.

Limits

  • No versioning, deprecation policy, OpenAPI document or typed client. See #31.
  • Session-cookie authentication only; scripts need a browser-issued cookie and a matching Origin. User-delegated and service-token access: #1, #31.
  • No search, saved views or bulk actions on lists (#5). Approval lists are capped at 100 and are not paged.
  • Task content is what Agent Taskbay has observed. It can lag the agent by the worker's polling interval when streaming and push are unavailable.
  • Real-time updates are a "something changed" signal followed by a re-query, not a data stream.

Created and maintained by Shashi Kanth G S at allsrc.dev. Visit shashikanth.me.

On this page