Activity verbs
Every mutation appends an Activity row to the append-only log. That log is the audit trail, the webhook payload source, the agent poll history, and the trigger vocabulary for automations. The wire shape:
{
"id": "uuid", // activity id (UUIDv7)
"boardId": "uuid",
"cardId": "uuid | null", // present for card-scoped verbs
"actorId": "uuid", // WHO caused it
"verb": "card.created",
"payload": { /* verb-specific */ },
"createdAt": "ISO-8601"
}Subscribe a webhook to specific verbs or to "*" for all; poll the same verbs over GET /api/boards/:id/activity?since=….
Board
| Verb | Payload |
|---|---|
board.created | { name } |
board.updated | { ...changed fields } |
board.view_saved | { viewId, name } (shared views) |
board.view_updated | { viewId, ...changed } |
board.view_removed | { viewId } |
List
| Verb | Payload |
|---|---|
list.created | { listId, name } |
list.updated | { listId, ...changed } |
list.moved | { listId, position } |
list.archived | { listId } |
Card
| Verb | Payload |
|---|---|
card.created | { title, listId } |
card.updated | { ...changed fields } |
card.moved | { fromListId, toListId, position } |
card.archived | { cardId } |
card.assigned | { actorId } |
card.unassigned | { actorId } |
card.delegated | { agentActorId, delegatedByActorId } |
card.undelegated | { agentActorId } |
card.labeled | { labelId } |
card.unlabeled | { labelId } |
card.linked | { linkId, externalSystem, externalId, externalUrl } |
card.unlinked | { linkId, externalSystem, externalId, externalUrl } |
card.mentioned | { cardId, mentionedActorIds } |
card.priority_changed | { priority } |
card.field_set | { fieldId, value } |
card.field_cleared | { fieldId } |
card.attachment_added | { cardId, attachmentId, filename, contentType, sizeBytes } |
card.attachment_removed | { cardId, attachmentId, filename } |
card.checklist_item_added | { cardId, itemId, text } |
card.checklist_item_updated | { cardId, itemId, text } |
card.checklist_item_completed | { cardId, itemId, text } |
card.checklist_item_removed | { cardId, itemId, text } |
Comment
| Verb | Payload |
|---|---|
comment.added | { commentId, body } |
comment.edited | { commentId, body } |
comment.deleted | { cardId, commentId } |
comment.mentioned | { cardId, commentId, mentionedActorIds } |
Field
| Verb | Payload |
|---|---|
field.created | { fieldId, ... } |
field.updated | { fieldId, ...changed } |
field.deleted | { fieldId } |
Label
| Verb | Payload |
|---|---|
label.created | { labelId, name, color } |
Member
| Verb | Payload |
|---|---|
member.added | { actorId, role } |
member.role_changed | { actorId, role } |
member.removed | { actorId } |
Actor
| Verb | Payload |
|---|---|
actor.capabilities_changed | { actorId, capabilities } |
Integration
| Verb | Payload |
|---|---|
integration.installed | { integrationId, type, actorId } |
integration.updated | { integrationId, status } |
integration.removed | { integrationId, type } |
Agent session
Emitted as an agent drives a session. These route to matching board subscriptions and to an agent's actor-scoped subscription (matched on agentActorId), so a mention-invokable agent is reached on any board.
| Verb | When |
|---|---|
agent_session.created | a session opens (delegation / mention / api) — payload carries the full trigger context |
agent_session.updated | a state change / status update |
agent_session.progress | a progress note |
agent_session.question | the agent posted ask_human (session → awaiting_input) |
agent_session.answered | a human answered (session → working) |
agent_session.log | a raw inspectable log entry |
agent_session.completed | the agent finished successfully |
agent_session.failed | the agent failed (or the watchdog timed it out) |
VCS (integration-emitted)
The github/gitlab integrations translate PR/MR lifecycle into these verbs, carrying the linked card, on the event path the rules engine watches. Write automations that trigger on them.
| Verb | When |
|---|---|
vcs.branch_created | a branch whose name carries a card key is pushed |
vcs.pr_drafted | draft PR/MR opened (or converted to draft) |
vcs.pr_opened | ready (non-draft) PR/MR opened / reopened |
vcs.review_requested | a review is requested / submitted (non-approval) |
vcs.pr_approved | a review approves the PR/MR |
vcs.pr_merged | PR/MR merged — carries filters.targetBranch |
vcs.pr_closed | PR/MR closed without merging |
Additive by default
Treat unknown future verbs and extra payload keys as additive — don't fail on them. The grouped catalog the webhook UI renders is WEBHOOK_EVENT_GROUPS in packages/shared; new verbs enter the domain layer over time.