Appearance
Webhook events
An outgoing webhook subscribes to a list of events on some or all of your boards. When one happens, Truetask posts a JSON payload to your URL. This page is the full catalog: the events, the fields each one carries, and how a delivery is signed and retried.
Set webhooks up in Settings > Integrations > Webhooks, or from the App menu > Webhooks. See Webhooks for the walkthrough.
Event names keep the API vocabulary
The product calls them tasks. The API and these event names call them cards, so the event stays card.created and the payload field stays card_id.
The envelope
Every delivery has the same four top-level keys.
| Field | What it is |
|---|---|
id | Delivery id, unique per delivery and stable across retries. |
event | Event name. |
timestamp | When the event happened (ISO 8601, UTC). |
data | The event record (see the fields per event). |
json
{
"id": "k7f2b1c9e3a4d5f6-2r8sn4vtqcx1wla",
"event": "card.moved",
"timestamp": "2026-09-22T09:14:03.117000+00:00",
"data": { "id": "...", "key": "DEVE-412", "title": "...", "...": "..." }
}Because id is stable across retries, use it to deduplicate. Delivery is at-least-once, so the same id can arrive twice.
Events
Each event has a kind, which decides the base payload fields in data. Events with extra fields add them on top of that kind's fields.
Task events
| Event | Kind | What it means | Extra fields |
|---|---|---|---|
card.created | card | A task was created | |
card.updated | card | A task's title, description, dates, tags or files changed | changes (changed fields with old and new values) |
card.deleted | card | A task was moved to the trash | |
card.completed | card | A task was completed | |
card.uncompleted | card | A completed task was reopened | |
card.assigned | card | Members were assigned to a task | assigned, assigned_names |
card.unassigned | card | Members were removed from a task | unassigned, unassigned_names |
card.moved | card | A task moved to another list | from_list, from_list_name, to_list, to_list_name |
card.proposed_done | card | An agent proposed a task as done, for a human to approve or return | run_id, proposal_event_id, summary, server_check |
server_check on a proposal is the list of gaps Truetask found between what the agent claims and the task's real state, as sentences.
Question events
| Event | Kind | What it means |
|---|---|---|
card.question_asked | card_question | A blocking question was asked on a task |
card.question_answered | card_question | A blocking question was answered or dismissed |
Comment and checklist events
| Event | Kind | What it means |
|---|---|---|
card.comment_added | comment | A comment was posted on a task |
comment.deleted | comment | A comment was deleted |
checklist.completed | checklist | A checklist item was ticked |
checklist.uncompleted | checklist | A checklist item was unticked |
List, board and tag events
| Event | Kind | What it means | Extra fields |
|---|---|---|---|
list.created | list | A list was added to a board | |
list.deleted | list | A list was deleted | |
board.member_added | board | Members joined a board | members_added, member_names |
board.member_removed | board | Members left a board | members_removed, member_names |
tag.created | tag | A tag was created | |
tag.deleted | tag | A tag was deleted |
Time events
| Event | Kind | What it means | Extra fields |
|---|---|---|---|
time_entry.started | time_entry | A timer started on a task | |
time_entry.stopped | time_entry | A timer stopped | timestamp_end, duration (tracked seconds) |
Agent events
| Event | Kind | What it means | Extra fields |
|---|---|---|---|
agent_run.kickoff | agent_kickoff | An agent kickoff reached a runner (first delivery only) | kickoff_text |
agent_run.handoff | agent_run | An agent run was handed to another agent | from_agent, to_agent, step |
agent_run.completed | agent_run | An agent run finished | |
agent_run.failed | agent_run | An agent run failed | |
agent_run.blocked | agent_run | An agent run is blocked | |
agent_run.needs_input | agent_run | An agent asked a question | |
agent_run.cancelled | agent_run | An agent run was cancelled | |
agent_run.stalled | agent_run | An agent run went silent past the watchdog window | stalled_at |
agent_run.approved | agent_run | A human approved an agent's done proposal | decision, reviewer_id, agent_id, feedback (always empty here) |
agent_run.returned | agent_run | A human returned an agent's done proposal with feedback | decision, reviewer_id, agent_id, feedback |
agent_kickoff.expired | agent_kickoff | Nothing picked an agent kickoff up before it expired | attempts, last_error |
kickoff_text is the instruction text the runner received. It never contains a secret or a token.
Audit events
| Event | Kind | What it means |
|---|---|---|
audit.log | audit | Any audited change on a board |
audit.log fires on nearly every record change, so subscribe to it only when you really want the whole stream.
Payload fields by kind
Fields marked (kept) predate the enrichment pass and never change shape, so an old receiver keeps working. Everything else was added so a Zapier, Make or n8n step has the names and links it needs without a second lookup.
card
| Field | What it is |
|---|---|
id | Task id (kept) |
key | Human key, e.g. DEVE-123 |
number | Task number on its board |
title | Title (kept) |
url | Link to the task |
board | Board id (kept) |
board_name | Board name |
list | List id |
list_name | List name |
completed | Whether the task is done (kept) |
members | Assignee user ids (kept) |
member_names | Assignee names |
tags | Tag ids (kept) |
tag_names | Tag names |
priority | Priority id or empty |
priority_name | Priority name or empty |
start | Start date or empty |
due | Due date or empty |
description_excerpt | First 280 characters of the description |
created | When the task was created |
updated | When the task last changed |
comment
| Field | What it is |
|---|---|
comment_id | Comment id (kept) |
message | Comment text (Markdown) |
sender | Author user id (kept) |
sender_name | Author name |
card_id | Task id (kept) |
card_key | Task key |
card_title | Task title (kept) |
card_url | Link to the task |
board | Board id (kept) |
board_name | Board name |
checklist
| Field | What it is |
|---|---|
checklist_id | Checklist item id (kept) |
context | Item text (kept) |
completed | Whether the item is ticked (kept) |
card_id | Task id (kept) |
card_key | Task key |
card_title | Task title (kept) |
card_url | Link to the task |
board | Board id (kept) |
board_name | Board name |
list
| Field | What it is |
|---|---|
id | List id (kept) |
name | List name (kept) |
board | Board id (kept) |
board_name | Board name |
tag
| Field | What it is |
|---|---|
id | Tag id (kept) |
name | Tag name (kept) |
color | Tag colour (kept) |
board | Board id (kept) |
board_name | Board name |
board
| Field | What it is |
|---|---|
board | Board id (kept) |
board_name | Board name (kept) |
time_entry
| Field | What it is |
|---|---|
time_entry_id | Time entry id (kept) |
user | User id (kept) |
user_name | User name |
timestamp_start | When the timer started (kept) |
card_id | Task id (kept) |
card_key | Task key |
card_title | Task title (kept) |
card_url | Link to the task |
board | Board id (kept) |
board_name | Board name |
agent_run
| Field | What it is |
|---|---|
run_id | Agent run id (kept) |
status | Run status (kept) |
previous_status | Status before the change (kept) |
agent | Agent {id, name} (kept) |
summary | Run summary (kept) |
card_id | Task id (kept) |
card_key | Task key |
card_title | Task title (kept) |
card_url | Link to the task |
board | Board id (kept) |
board_name | Board name |
agent_kickoff
| Field | What it is |
|---|---|
kickoff_id | Agent kickoff id |
status | Kickoff status: sent, started, expired, and so on |
power | How it was delivered: wake, desktop or truetask (empty if never delivered) |
source | Where it came from, e.g. manual, mcp, automation, pipeline or assignment |
requested_by | User id that asked for the kickoff, or empty |
workflow_id | Agent workflow id |
agent | Agent {id, name} |
card_id | Task id |
card_key | Task key |
card_title | Task title |
card_url | Link to the task |
board | Board id |
board_name | Board name |
card_question
| Field | What it is |
|---|---|
question_id | Question id |
question | The question |
options | One-click answer choices (may be empty) |
status | open, answered or dismissed |
asked_by | User id that asked (usually an agent) |
asked_by_name | Name of who asked |
answer | The answer text, or the picked option (empty while open) |
answer_choice | The picked option, or empty |
answered_by | User id that answered or dismissed, or empty |
answered_by_name | Name of who answered, or empty |
card_id | Task id |
card_key | Task key |
card_title | Task title |
card_url | Link to the task |
board | Board id |
board_name | Board name |
audit
| Field | What it is |
|---|---|
actor | User id (kept) |
actor_name | User name |
action | create, update or delete (kept) |
entity_type | Record type (kept) |
entity_id | Record id (kept) |
entity_name | Record name (kept) |
meta | Diff or snapshot (kept) |
board | Board id (kept) |
board_name | Board name |
Headers and signing
Every delivery carries these headers.
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | Truetask-Webhook/1.0 |
X-Webhook-Event | The event name, the same value as event in the body |
X-Truetask-Signature | sha256= plus the HMAC-SHA256 of the raw request body, keyed with your signing secret |
X-Webhook-Signature | A plain SHA256 of the body concatenated with the secret. Deprecated, kept for older receivers. |
The two signature headers are only sent when the webhook has a signing secret. Verify X-Truetask-Signature, and compare it in constant time:
python
import hashlib, hmac
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
assert hmac.compare_digest(expected, request.headers["X-Truetask-Signature"])Sign over the raw bytes you received, before any JSON parsing or re-serialization.
A webhook can also authenticate to your endpoint on top of the signature, with a bearer token, basic auth or a custom header. That is set per webhook under Authentication and is separate from the signing secret.
A secret belongs in the secret field
Never put a credential in the URL or in a custom header you can read back. The signing secret is stored write-only: you can replace it, but nobody can read it again.
Delivery, retries and logs
Deliveries go out off the request path, so a slow or dead endpoint never blocks the person who triggered the event.
- Truetask sends
POSTby default; a webhook can be set toPUTorPATCHinstead. - Each attempt has a 10 second timeout, and redirects are never followed.
- Any
2xxresponse counts as delivered. - A
4xxresponse is permanent: Truetask logs it and gives up, because a retry would be rejected the same way. - A
5xxresponse, a timeout or a connection failure is retried up to 5 attempts, waiting 60 seconds, then 120, then 300, then 1800 seconds between them.
Every attempt writes a row you can read under Recent deliveries on the webhook, with the status code, the body Truetask sent and the answer it got back.

