TraqOS

Developers

TraqOS API

A REST API over your workspace: projects, lists, tasks, comments and members, plus signed event deliveries for your own endpoints and for automation tools. JSON in, JSON out, one token per workspace.

Get a tokenDownload the OpenAPI specBase URL https://www.traqos.com/api/v1

Start here

Three calls and you have read your first task.

  1. In TraqOS open Settings → Developer → Create token, pick the scopes you need (or a preset such as Zapier), copy the token. It is shown once.
  2. Check who you are:
curl
curl https://www.traqos.com/api/v1/me \
  -H "Authorization: Bearer traqos_pat_…"
200 OK
{
  "user":   { "id": "…", "email": "you@example.com" },
  "tenant": { "id": "…", "name": "Your workspace" },
  "authKind": "pat",
  "scopes": ["projects:read", "lists:read", "tasks:read"],
  "plan": "pro"
}
  1. Walk down: GET /projects → GET /projects/{projectId}/lists → GET /lists/{listId}/tasks?expand=display. Every object carries the ids you need for the next call.

Authentication and tokens

A personal access token (PAT) acts as you, limited to the scopes you picked.

  • Send it as Authorization: Bearer traqos_pat_… on every request. Tokens are tied to one workspace and one user.
  • A token never grants more than its owner's role allows: effective rights are the token's scopes intersected with your permissions in the workspace. Resources you cannot see answer 404, never 403.
  • Tokens can expire (you choose when you create them) and can be revoked at any time in Settings → Developer. Revoking a token also removes the instant triggers it created.
  • Writes need a plan with API write access (see the plan table below). A write-scoped token minted on a higher plan stops writing after a downgrade and answers 402.
  • Keep tokens out of URLs, client-side code and logs. If one leaks, revoke it; everything it created stops.

Scopes

Pick the smallest set that does the job. Read scopes can never change anything.

Projects & lists

ScopeClassGrants
projects:readreadRead projects
projects:writewriteCreate projects
lists:readreadRead task lists
lists:writewriteCreate task lists

Tasks

ScopeClassGrants
tasks:readreadRead tasks
tasks:writewriteCreate, edit and delete tasks

Comments

ScopeClassGrants
comments:readreadRead comments
comments:writewriteAdd comments

Custom fields

ScopeClassGrants
custom-fields:readreadRead custom fields
custom-fields:writewriteEdit custom field values

Docs

ScopeClassGrants
docs:readreadRead docs

Members

ScopeClassGrants
members:readreadRead workspace members

Time tracking

ScopeClassGrants
time:readreadRead time entries
time:writewriteCreate and edit time entries

Webhooks

ScopeClassGrants
webhooks:managewriteManage webhook subscriptions
hooks:managereadReceive instant events for lists you can see (Zapier and similar tools)

Errors

Every error is JSON. Two envelopes exist: the ordinary one and the plan envelope.

4xx / 5xx
{ "error": { "code": "not_found", "message": "Task not found", "details": { } } }
StatuscodeWhen
400validation_failedA field is missing, unknown or malformed. The message names it.
401unauthorizedNo token, an unknown token, or a revoked or expired one.
403insufficient_scopeThe token lacks the scope; details.scope names it.
403forbiddenYour role does not allow this (for example managing webhooks needs the developer-admin permission).
403workspace_read_onlyBilling put the workspace in read-only mode. Reads still work.
403workspace_suspendedThe workspace is suspended. Nothing works until it is restored.
404not_foundDoes not exist, or you cannot see it. The two are indistinguishable on purpose.
429rate_limitedSlow down. Retry-After says how many seconds.
500internalOur fault. Retry later; tell us if it persists.

Plan limits answer 402 with a different, flat envelope. key is the limit that was hit and requiredPlan the smallest plan that lifts it.

402 Payment Required
{
  "error": "Instant triggers are not included in your plan.",
  "code": "entitlement_denied",
  "key": "apiHooksMax",
  "current": 0,
  "max": 0,
  "requiredPlan": "pro"
}

Paging, rate limits and plans

Lists are cursor-paged and every token has a per-minute budget set by the workspace plan.

  • List endpoints answer { "data": [...], "nextCursor": "…" | null }. Pass cursor back to get the next page; limit is 1 to 100, default 50. Order is by creation unless the endpoint offers order=desc.
  • Every token response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (unix seconds). Over budget you get 429 with Retry-After.
PlanAPI accessRequests per minuteWorkspace webhooksInstant triggers (member hooks)
Freeread only3000
Basicread only6000
Proread and write1001025
Pro+read and write20025100
Enterpriseread and write1000100500

Identity

GET/me

Token introspection

Responses
StatusMeaningBody
200Who am I
401UnauthorizedError
429Rate limitedError

Projects and lists

GET/projects

List accessible projects

Requires scope projects:read.

Parameters
NameInTypeNotes
cursorquerystring
limitqueryintegerDefault 50.
Responses
StatusMeaningBody
200Page of projects{ data: Project[], nextCursor }
401UnauthorizedError
403Missing scopeError
429Rate limitedError
POST/projects

Create a project

Requires scope projects:write. Defaults to the General board when boardId is omitted.

Request body (JSON, required)
FieldTypeNotes
name*string
descriptionstring
boardIdstring
privacy"public" | "private"
Responses
StatusMeaningBody
201Created project{ data: Project }
400Validation failedError
401UnauthorizedError
402Plan limit (locked envelope)EntitlementError
403Missing scope / insufficient permissions / workspace read-onlyError
429Rate limitedError
GET/projects/{projectId}

Get a project

Requires scope projects:read. 404 when missing OR not visible to you.

Parameters
NameInTypeNotes
projectId*pathstring
Responses
StatusMeaningBody
200Project{ data: Project }
401UnauthorizedError
403Missing scopeError
404Not foundError
429Rate limitedError
GET/projects/{projectId}/lists

List task lists in a project

Requires scope lists:read. Private lists you can't view are omitted; folder fences apply.

Parameters
NameInTypeNotes
projectId*pathstring
Responses
StatusMeaningBody
200Task lists{ data: TaskList[], nextCursor }
401UnauthorizedError
403Missing scopeError
404Not foundError
429Rate limitedError
POST/projects/{projectId}/lists

Create a task list

Requires scope lists:write. v1 creates public lists (privacy/grants are in-app flows).

Parameters
NameInTypeNotes
projectId*pathstring
Request body (JSON, required)
FieldTypeNotes
name*string
descriptionstring
colorstring
iconstring
Responses
StatusMeaningBody
201Created list{ data: TaskList }
400Validation failedError
401UnauthorizedError
402Plan limit (locked envelope)EntitlementError
403Missing scope / insufficient permissions / workspace read-onlyError
404Not foundError
429Rate limitedError
GET/lists/{listId}/custom-fields

List custom-field definitions

Requires scope custom-fields:read. fieldId is the key Task.customFieldValues uses. Private fields you can't view are omitted.

Parameters
NameInTypeNotes
listId*pathstring
Responses
StatusMeaningBody
200Custom fields{ data: CustomField[], nextCursor }
401UnauthorizedError
403Missing scope / insufficient permissionsError
404Not foundError
429Rate limitedError

Tasks

GET/lists/{listId}/tasks

List tasks in a list

Requires scope tasks:read.

Parameters
NameInTypeNotes
listId*pathstring
statusquerystring
assigneeIdquerystring
updatedSincequerystring
orderquery"asc" | "desc"By creation time. desc = newest first.
expandquery"display"display = add Task.display (link, status name, assignee names, list, project, plain-text description)
cursorquerystring
limitqueryintegerDefault 50.
Responses
StatusMeaningBody
200Page of tasks{ data: Task[], nextCursor }
400Bad filterError
401UnauthorizedError
403Missing scopeError
404Not foundError
429Rate limitedError
POST/lists/{listId}/tasks

Create a task

Requires scope tasks:write. Fires the same realtime/automation events as in-app creation.

Parameters
NameInTypeNotes
listId*pathstring
expandquery"display"display = add Task.display (link, status name, assignee names, list, project, plain-text description)
Request body (JSON, required)

A TaskInput object with name required.

Responses
StatusMeaningBody
201Created task{ data: Task }
400Validation failedError
401UnauthorizedError
402Plan limit (locked envelope)EntitlementError
403Missing scope / insufficient permissions / workspace read-onlyError
404Not foundError
429Rate limitedError
GET/tasks/{taskId}

Get a task

taskId is the canonical id (Task.id). Requires scope tasks:read.

Parameters
NameInTypeNotes
taskId*pathstring
expandquery"display"display = add Task.display (link, status name, assignee names, list, project, plain-text description)
Responses
StatusMeaningBody
200Task{ data: Task }
401UnauthorizedError
403Missing scopeError
404Not foundError
429Rate limitedError
PATCH/tasks/{taskId}

Update a task

Requires scope tasks:write. Formula fields recompute; realtime/automation events fire.

Parameters
NameInTypeNotes
taskId*pathstring
expandquery"display"display = add Task.display (link, status name, assignee names, list, project, plain-text description)
Request body (JSON, required)

A TaskInput object.

Responses
StatusMeaningBody
200Updated task{ data: Task }
400Validation failedError
401UnauthorizedError
402Plan limit (locked envelope)EntitlementError
403Missing scope / insufficient permissions / workspace read-onlyError
404Not foundError
429Rate limitedError
DELETE/tasks/{taskId}

Delete a task

Requires scope tasks:write. Full cascade (time entries, comments, files) — same as in-app deletion.

Parameters
NameInTypeNotes
taskId*pathstring
Responses
StatusMeaningBody
200Deleted
401UnauthorizedError
403Missing scope / insufficient permissions / workspace read-onlyError
404Not foundError
429Rate limitedError

Comments

GET/tasks/{taskId}/comments

List task comments

Requires scope comments:read. Newest first. Same thread the app UI shows.

Parameters
NameInTypeNotes
taskId*pathstring
limitqueryintegerDefault 50.
Responses
StatusMeaningBody
200Comments{ data: Comment[], nextCursor }
401UnauthorizedError
403Missing scope / insufficient permissionsError
404Not foundError
429Rate limitedError
POST/tasks/{taskId}/comments

Add a comment

Requires scope comments:write. Mentioned member ids are validated and notified (same machinery as in-app comments).

Parameters
NameInTypeNotes
taskId*pathstring
Request body (JSON, required)
FieldTypeNotes
content*string
mentionsarray of string
parentIdstring
Responses
StatusMeaningBody
201Created comment{ data: Comment }
400Validation failedError
401UnauthorizedError
403Missing scope / insufficient permissions / workspace read-onlyError
404Not foundError
429Rate limitedError

Members and search

GET/members

List workspace members

Requires scope members:read AND the member-view role permission. Use ids for assigneeIds and mentions. Guests are never listed.

Responses
StatusMeaningBody
200Members{ data: Member[], nextCursor }
401UnauthorizedError
403Missing scope / insufficient permissionsError
429Rate limitedError

Workspace webhooks

GET/webhooks

List workspace webhooks

Requires scope webhooks:manage and the developer-admin permission.

Responses
StatusMeaningBody
200Webhooks{ data: Webhook[], nextCursor }
401UnauthorizedError
403Missing scope / not a developer-adminError
429Rate limitedError
POST/webhooks

Create a workspace webhook

Requires scope webhooks:manage and the developer-admin permission. The signing secret is returned once, in this response only. Counts toward the plan's webhooksMax.

Request body (JSON, required)

A WebhookInput object.

Responses
StatusMeaningBody
201Created webhook, with its secret (shown only here){ data: Webhook + secret }
400Validation failed (name, URL, events)Error
401UnauthorizedError
402Plan limit (locked envelope)EntitlementError
403Missing scope / not a developer-admin / workspace read-onlyError
429Rate limitedError
GET/webhooks/{webhookId}

Get a workspace webhook

Requires scope webhooks:manage and the developer-admin permission.

Parameters
NameInTypeNotes
webhookId*pathstring
Responses
StatusMeaningBody
200Webhook{ data: Webhook }
401UnauthorizedError
403Missing scope / not a developer-adminError
404Not foundError
429Rate limitedError
PATCH/webhooks/{webhookId}

Update, pause or resume a workspace webhook

Requires scope webhooks:manage and the developer-admin permission. Send only the fields to change; status accepts active or paused (active also clears auto_disabled).

Parameters
NameInTypeNotes
webhookId*pathstring
Request body (JSON, required)

A WebhookPatch object.

Responses
StatusMeaningBody
200Updated webhook{ data: Webhook }
400Validation failedError
401UnauthorizedError
403Missing scope / not a developer-admin / workspace read-onlyError
404Not foundError
429Rate limitedError
DELETE/webhooks/{webhookId}

Delete a workspace webhook

Requires scope webhooks:manage and the developer-admin permission. Its delivery log goes with it.

Parameters
NameInTypeNotes
webhookId*pathstring
Responses
StatusMeaningBody
200Deleted{ data: { id, deleted } }
401UnauthorizedError
403Missing scope / not a developer-adminError
404Not foundError
429Rate limitedError
GET/webhooks/{webhookId}/deliveries

Delivery log of a workspace webhook

The last 50 deliveries, newest first: status, attempts, your response code, the payload that was sent.

Parameters
NameInTypeNotes
webhookId*pathstring
Responses
StatusMeaningBody
200Deliveries{ data: WebhookDelivery[], nextCursor }
401UnauthorizedError
403Missing scope / not a developer-adminError
404Not foundError
429Rate limitedError
POST/webhooks/{webhookId}/test

Send a test delivery

Posts a webhook.test event to the endpoint right now and returns the outcome.

Parameters
NameInTypeNotes
webhookId*pathstring
Responses
StatusMeaningBody
200Outcome of the attempt{ data: { status, responseStatus, durationMs, error } }
401UnauthorizedError
403Missing scope / not a developer-adminError
404Not foundError
429Rate limitedError

Member hooks (instant triggers)

POST/hooks

Subscribe to one list's events (member hook)

Requires scope hooks:manage plus the read scope of each event family (tasks:read, comments:read). The list must be readable by you now (otherwise 404), and every delivery is checked again when it is sent. Repeating the same request (same URL, list and events) answers 200 with the existing hook instead of creating another. Counts toward the plan's apiHooksMax.

Request body (JSON, required)

A HookInput object.

Responses
StatusMeaningBody
200The same hook already existed; returned again with its secret{ data: Hook + secret }
201Created hook, with its secret (shown only here){ data: Hook + secret }
400Validation failed (events, URL)Error
401UnauthorizedError
402Plan limit (locked envelope)EntitlementError
403Missing scope / events not allowed for your roleError
404List not found or not visible to youError
429Rate limitedError
DELETE/hooks/{hookId}

Unsubscribe (delete a member hook)

Only your own hooks; anyone else's answer 404. Never blocked by the plan.

Parameters
NameInTypeNotes
hookId*pathstring
Responses
StatusMeaningBody
200Deleted{ data: { id, deleted } }
401UnauthorizedError
403Missing scopeError
404Not foundError
429Rate limitedError

The spec itself

GET/openapino token needed

This document

Responses
StatusMeaningBody
200OpenAPI 3.1 spec

Events and deliveries

TraqOS can call your endpoint when something happens. There are two kinds of subscription.

Workspace webhookMember hook (instant trigger)
Who creates itA developer-admin, in Settings → Developer or with POST /webhooksAny member with a token carrying hooks:manage, with POST /hooks (this is what Zapier-style tools use)
What it coversEvery event in the workspace, or one projectThe events of one task list
What it carriesThe full eventOnly what the token's owner can read at send time: private custom-field values are removed, and the event is dropped if the owner lost access
Plan allowancewebhooksMaxapiHooksMax
When it stopsPaused by an admin, or auto-disabled after 20 failed deliveries in a rowToken revoked or expired, owner leaves, your endpoint answers 410, or 20 failed deliveries in a row

Event catalog

Task lifecycleMeaningMember hooks
task.createdTask createdyes
task.updatedTask updatedyes
task.deletedTask deletedyes
Task field changesMeaningMember hooks
task.status_changedStatus changedyes
task.assignee_addedAssignee addedyes
task.assignee_removedAssignee removedyes
task.due_date_changedDue date changedyes
task.start_date_changedStart date changedyes
task.custom_field_changedCustom field changedno
Work intakeMeaningMember hooks
task.form_submittedForm response receivedyes
task.email_receivedEmail receivedyes
CommentsMeaningMember hooks
comment.createdComment addedyes

The delivery

One HTTP POST per event, JSON body, within seconds of the event. Task events carry data.task (a Task) and, when something changed, data.changes with from and to per field; comment events carry data.comment (a Comment). Member-hook deliveries always include the task's display block, name both statuses of a status change (changes.status.fromName, toName) and point a comment at its task (comment.display.taskRef). The test button sends webhook.test.

POST https://your-endpoint.example.com/traqos
{
  "id": "0f2e6a9e-4a1c-4a40-9a0b-5a0f7a1c2d3e",
  "type": "task.status_changed",
  "createdAt": "2026-10-06T09:30:00.000Z",
  "tenantId": "…", "projectId": "…", "listId": "…",
  "actor": { "userId": "…" },
  "data": {
    "task": { "id": "…", "taskId": 42, "name": "Write the launch announcement", "status": "status_done", "display": { "status": "Done", "url": "https://www.traqos.com/…", "…": "…" } },
    "changes": { "status": { "from": "status_in_progress", "to": "status_done", "fromName": "In progress", "toName": "Done" } }
  }
}
DeliverySummaryHeadersYour answer
taskEventSent for every task.* event the subscription carries.X-TraqOS-Event, X-TraqOS-Delivery, X-TraqOS-Signature
  • 2XX Received. Answer within 10 seconds.
  • 410 Member hooks only: the hook is removed and nothing more is sent
  • 429 Retried after Retry-After; never counted as a failure
  • default Retried after 1, 5, 30, 120 and 360 minutes (5 attempts); 20 failures in a row disable the subscription
commentEventSent for comment.created.X-TraqOS-Event, X-TraqOS-Delivery, X-TraqOS-Signature
  • 2XX Received. Answer within 10 seconds.
  • 410 Member hooks only: the hook is removed and nothing more is sent
  • 429 Retried after Retry-After; never counted as a failure
  • default Retried after 1, 5, 30, 120 and 360 minutes (5 attempts); 20 failures in a row disable the subscription
testEventSent by the test button or POST /webhooks/{webhookId}/test; data is a small sample.X-TraqOS-Event, X-TraqOS-Delivery, X-TraqOS-Signature
  • 2XX Received. Answer within 10 seconds.
  • 410 Member hooks only: the hook is removed and nothing more is sent
  • 429 Retried after Retry-After; never counted as a failure
  • default Retried after 1, 5, 30, 120 and 360 minutes (5 attempts); 20 failures in a row disable the subscription
  • Answer any 2xx within 10 seconds. Anything else is retried after 1, 5, 30, 120 and 360 minutes (5 attempts in all); 429 is retried after your Retry-After and never counts as a failure.
  • Deliveries of one subscription are sent in order; a slow subscription never delays another one.
  • Member hooks may carry only these events: task.created, task.updated, task.deleted, task.status_changed, task.assignee_added, task.assignee_removed, task.due_date_changed, task.start_date_changed, task.form_submitted, task.email_received, comment.created.

Verifying signatures

Every delivery is signed with the subscription's secret, which you received once when you created it.

The header X-TraqOS-Signature: t=<unix seconds>,v1=<hex> carries a timestamp and an HMAC-SHA256 of the string <t>.<raw body> with the secret. Compute it over the exact bytes you received, compare in constant time, and reject timestamps older than 5 minutes.

Node.js
import crypto from "node:crypto";

export function verifyTraqOS(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const equal = expected.length === parts.v1.length &&
    crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(parts.v1, "hex"));
  return fresh && equal;
}

Two more headers help with logging and deduplication: X-TraqOS-Event (the event type) and X-TraqOS-Delivery (a unique id per delivery; a retry keeps the same id). The user agent is TraqOS-Webhooks/1.0.

Object reference

The objects the endpoints exchange. A star marks a required field on input.

Task

FieldTypeNotes
idstringCanonical task id
taskIdintegerPer-list numeric id (display)
namestring
descriptionstringTiptap JSON serialized as a string
descriptionPinnedboolean
statusstring | null
typestring
colorstring | null
startDatestring | null (date-time)
endDatestring | null (date-time)
durationnumber | null
progressnumber
timeEstimatenumber | null
timeTrackednumber
assigneeIdsarray of string
dependenciesarray of stringPredecessor strings, e.g. "12FS"
parentIdstring | null
sectionIdstring | null
listIdstring
projectIdstring
boardIdstring | null
customFieldValuesobject
checklistsarray of object
isRecurringboolean
createdAtstring | null (date-time)
updatedAtstring | null (date-time)
displayobjectHuman-readable companions. Present only with ?expand=display, and on member-owned hook deliveries.
↳ urlstringLink to the task in the app
↳ statusstring | nullThe status NAME (Task.status holds its value)
↳ assigneesarray of object
↳ idstring
↳ namestring | null
↳ liststring | nullList name
↳ projectstring | nullProject name
↳ descriptionstringThe description as plain text

TaskInput

Public write fields. Unknown keys are rejected.

FieldTypeNotes
namestring
descriptionstring
descriptionTextstring | nullThe description as plain text, one paragraph per line. Send this OR description, not both. "" clears it.
descriptionPinnedboolean
startDatestring | null
endDatestring | null
durationnumber | null
progressnumber
statusstring | null
typestring
colorstring | null
sectionIdstring | null
assigneeIdsarray of string
dependenciesarray of string
timeEstimatenumber | null
customFieldValuesobject
isRecurringboolean
recurrenceobject
checklistsarray of object

Project

FieldTypeNotes
idstring
namestring
descriptionstring
boardIdstring | null
colorstring | null
iconstring | null
privacy"public" | "private"
isDefaultboolean
createdAtstring | null (date-time)
updatedAtstring | null (date-time)

TaskList

FieldTypeNotes
idstring
namestring
colorstring | null
iconstring | null
isDefaultboolean
folderIdstring | null
lockedbooleantrue = metadata stub (no access to contents)
descriptionstring
statusesarray of objectStatus options for this list. `value` is what Task.status stores.
↳ valuestring
↳ labelstring
↳ colorstring | null
↳ categorystring | nullnot_started | active | closed
↳ orderinteger
taskTypesarray of objectTask types for this list. `id` is what Task.type stores.
↳ idstring
↳ namestring
↳ iconstring | null
↳ colorstring | null
↳ isEnabledboolean
↳ orderinteger
defaultTaskTypestring | null
canEditboolean
createdAtstring | null (date-time)
updatedAtstring | null (date-time)

Member

FieldTypeNotes
idstring
namestring | null
emailstring | null
rolestring

Comment

FieldTypeNotes
idstring
taskIdinteger | nullNumeric TaskID (thread key)
parentIdstring | null
threadDepthinteger
contentstringSanitized HTML
plainTextstring
authorobject | null
↳ idstring
↳ namestring | null
mentionsarray of object
↳ idstring
↳ namestring | null
replyCountinteger
createdAtstring | null (date-time)
updatedAtstring | null (date-time)

CustomField

FieldTypeNotes
fieldIdstringThe key used inside Task.customFieldValues
namestring
typestring
descriptionstring | null
optionsarray of object
orderinteger
createdAtstring | null (date-time)
updatedAtstring | null (date-time)

Webhook

A workspace webhook: every event of the workspace, or of one project. Managed by developer-admins.

FieldTypeNotes
idstring
namestring
urlstring (uri)
eventsarray of "task.created" | "task.updated" | "task.deleted" | "task.status_changed" | "task.assignee_added" | "task.assignee_removed" | "task.due_date_changed" | "task.start_date_changed" | "task.custom_field_changed" | "task.form_submitted" | "task.email_received" | "comment.created"
status"active" | "paused" | "auto_disabled"auto_disabled = 20 failed deliveries in a row. Fix the endpoint, then PATCH status to active.
projectIdstring | nullSet = only this project's events
consecutiveFailuresinteger
lastDeliveryAtstring | null (date-time)
lastDeliveryStatusstring | null
createdAtstring | null (date-time)
updatedAtstring | null (date-time)

WebhookInput

FieldTypeNotes
name*string
url*string (uri)https URL on a public host. Private, loopback and link-local addresses are refused.
events*array of "task.created" | "task.updated" | "task.deleted" | "task.status_changed" | "task.assignee_added" | "task.assignee_removed" | "task.due_date_changed" | "task.start_date_changed" | "task.custom_field_changed" | "task.form_submitted" | "task.email_received" | "comment.created"
projectIdstring | nullLimit the webhook to one project

WebhookPatch

Send only the fields to change.

FieldTypeNotes
namestring
urlstring (uri)
eventsarray of "task.created" | "task.updated" | "task.deleted" | "task.status_changed" | "task.assignee_added" | "task.assignee_removed" | "task.due_date_changed" | "task.start_date_changed" | "task.custom_field_changed" | "task.form_submitted" | "task.email_received" | "comment.created"
projectIdstring | null
status"active" | "paused"paused = keep the subscription, send nothing. active = resume, also after auto_disabled.

WebhookDelivery

FieldTypeNotes
idstring
eventTypestring
status"pending" | "processing" | "succeeded" | "failed" | "dead"
attemptsinteger
responseStatusinteger | null
responseBodySnippetstring | nullFirst 1024 characters of your answer
durationMsinteger | null
lastErrorstring | null
nextAttemptAtstring | null (date-time)
createdAtstring | null (date-time)
payloadobjectThe EventEnvelope that was sent

Hook

A member hook: the events of ONE task list, owned by the token's user, carrying only what that user can read.

FieldTypeNotes
idstring
namestring
urlstring (uri)
eventsarray of "task.created" | "task.updated" | "task.deleted" | "task.status_changed" | "task.assignee_added" | "task.assignee_removed" | "task.due_date_changed" | "task.start_date_changed" | "task.form_submitted" | "task.email_received" | "comment.created"
listIdstring
projectIdstring | null
status"active" | "paused" | "auto_disabled"
createdAtstring | null (date-time)

HookInput

FieldTypeNotes
url*string (uri)Where to POST deliveries. https, public host.
events*array of "task.created" | "task.updated" | "task.deleted" | "task.status_changed" | "task.assignee_added" | "task.assignee_removed" | "task.due_date_changed" | "task.start_date_changed" | "task.form_submitted" | "task.email_received" | "comment.created"
listId*stringThe task list to watch. Must be readable by the token's owner, otherwise 404.
namestring

EventEnvelope

The body of every delivery. `data` holds `task` (+ `changes` with from/to per field) for task events and `comment` for comment events.

FieldTypeNotes
id*stringUnique per event; a retry keeps the same id
type*"task.created" | "task.updated" | "task.deleted" | "task.status_changed" | "task.assignee_added" | "task.assignee_removed" | "task.due_date_changed" | "task.start_date_changed" | "task.custom_field_changed" | "task.form_submitted" | "task.email_received" | "comment.created" | "webhook.test"
createdAt*string (date-time)
tenantId*string
projectIdstring | null
listIdstring | null
actorobject
↳ userIdstring | nullWho caused the event
data*objecttask.* → { task: Task, changes?: { <field>: { from, to } } }. comment.created → { comment: Comment }. Member-hook deliveries always include task.display; a status change adds changes.status.fromName and toName; a comment adds comment.display.taskRef { id, taskId }.

Error

FieldTypeNotes
errorobject
↳ code*string
↳ message*string
↳ detailsobject

EntitlementError

FieldTypeNotes
errorstringHuman-readable reason
code"entitlement_denied"
keystring
currentinteger | null
maxinteger | null
requiredPlanstring