Webhook event and payload reference
An Outgoing webhook pins payload_version: 1. TOW may add optional fields within v1, but does not remove a field, rename it, change its type or meaning, or subscribe an existing webhook to a new event type. A breaking contract uses a new major payload version alongside v1.
Envelope
{
"payload_version": 1,
"webhook_id": "0198...",
"event_id": "0198...",
"delivery_id": "0198...",
"event_type": "ticket.updated",
"action": "moved",
"occurred_at": "2026-08-12T10:00:00Z",
"operation_id": "0198...",
"organization": { "id": "0198..." },
"projects": [
{ "id": "0198...", "scope": "previous" },
{ "id": "0199...", "scope": "current" }
],
"actor": {
"type": "user",
"id": "0198...",
"display_name": "Ada Lovelace"
},
"source": {
"type": "api",
"id": null,
"correlation_id": null,
"causation_event_id": null
},
"subject": {
"type": "ticket",
"id": "0198...",
"key": "TOW-42",
"api_url": "/api/tasks/TOW-42"
},
"data": {
"object": { "id": "0198...", "status": "done" },
"previous_attributes": { "status": "in_progress" },
"changed_fields": ["status"],
"redacted_fields": [],
"truncated_fields": []
},
"encryption": {
"mode": "none",
"content_included": true
}
}
event_id identifies the domain occurrence. delivery_id identifies one attempt/replay stream for one subscription. operation_id correlates independently useful resource events created by one command. For example, completing a sprint can emit one sprint event plus ticket membership events with the same operation ID.
projects is intentionally plural. Documents can be linked to several projects, sprints may span projects, links may cross projects, and moves contain both previous and current scope. A selected-project subscriber receiving only the previous side of a move gets a minimal moved_out object rather than data from the destination project.
data.object is a bounded, allowlisted snapshot captured when the mutation occurs. previous_attributes includes old values for changed fields where safe. Sensitive or end-to-end encrypted values are omitted and named in redacted_fields; oversized values are named in truncated_fields.
Event catalog
The v1 catalog contains 53 exact event types, including the test event.
System
| Event | Actions |
|---|---|
webhook.test | test |
Tickets
| Event | Actions |
|---|---|
ticket.created | created, cloned |
ticket.updated | updated, archived, restored, moved |
ticket.deleted | deleted |
ticket.comment.created, ticket.comment.updated, ticket.comment.deleted | created, updated, deleted respectively |
ticket.checklist_item.created, ticket.checklist_item.updated, ticket.checklist_item.deleted | created, updated, deleted respectively |
ticket.worklog.created, ticket.worklog.updated, ticket.worklog.deleted | created, updated, deleted respectively |
ticket.link.created, ticket.link.deleted | created, deleted respectively |
ticket.attachment.created, ticket.attachment.deleted | created, deleted respectively |
ticket.participant.added, ticket.participant.removed | added, removed respectively |
ticket.sprint_assignment.updated | updated |
ticket.sla.threatened, ticket.sla.breached | threatened, breached respectively |
Planning
| Event | Actions |
|---|---|
project.created | created |
project.updated | updated, archived, restored |
project.deleted | deleted |
sprint.created | created |
sprint.updated | updated, reordered, started, completed |
sprint.deleted | deleted |
project.version.created | created |
project.version.updated | updated, released, unreleased, archived |
project.version.deleted | deleted |
Documents
| Event | Actions |
|---|---|
document.created | created, duplicated |
document.updated | updated, moved, archived, revision_restored |
document.deleted | deleted |
document.comment.created, document.comment.updated, document.comment.deleted | created, updated, deleted respectively |
document.comment_thread.updated | resolved, reopened |
document.comment_reaction.created, document.comment_reaction.deleted | created, deleted respectively |
Goals
| Event | Actions |
|---|---|
goal.created | created |
goal.updated | updated, archived, restored |
goal.update.created | created |
goal.comment.created, goal.comment.updated, goal.comment.deleted | created, updated, deleted respectively |
goal.measure.created, goal.measure.updated, goal.measure.deleted | created, updated, deleted respectively |
goal.link.created, goal.link.deleted | created, deleted respectively |
Runs
| Event | Actions |
|---|---|
agent_run.finished | finished; inspect the safe status field for completed or failed |
automation_run.finished | finished; inspect the safe status field for completed, failed, or skipped |
Mutation rules
One mutation emits one event for each independently useful changed resource, not one event per changed field. An update carries changed_fields and safe previous values. No-op and idempotent commands emit nothing.
Associations supplied during initial creation are folded into the parent's final *.created snapshot. Later child mutations get their own event. A child mutation may also emit a correlated parent event when it changes computed state, such as checklist progress or worklog totals.
Cascades are bounded:
- Ticket deletion emits
ticket.deletedand suppresses deletion noise for its children. - Project deletion emits one
project.deletedwith cascade counts rather than thousands of child events. - Recursive document deletion emits one
document.deletedper deleted page and correlated move events for surviving reparented pages. - Historical imports and migrations do not flood subscriptions. Mutations performed after a webhook is enabled identify their source when applicable.
Per-user read state, watchers, notification preferences, drafts, staging uploads, ACL schema changes, boards, workflow definitions, custom-field definitions, spaces, goal types, and intermediate run states are not v1 public events.
Receiver guidance
Treat unknown JSON fields as additive and unknown action values as forward-compatible within a resource event. Only process the exact event types configured for the subscription. Persist event_id for domain-level correlation and deduplicate delivery attempts by delivery_id.
For protected data, inspect both data.redacted_fields and encryption. Hydrate current state through the REST API or MCP with a separate credential; webhook possession does not grant API read authority.