Skip to main content

Event reference

All events share the envelope described in Getting started: { event, id, created, data }. id is always the id of the resource the event is about. This page explains the shape every resource follows. For a specific resource’s exact fields, its list of change/field/activity values, and full example payloads, go to its own page — linked in the table below.

The five keys per resource

Every resource exposes up to five subscribable keys. Instead of a long list of narrow keys, related changes are grouped and a discriminator inside data tells you exactly what happened: Subscribe once to <resource>.updated and branch on data.change — you do not need a separate subscription per change type. The same is true for .sundry (branch on data.field) and .activity (branch on data.type + data.action).
  • .updated/.sundry always carry the complete, current resource object — not a diff. Whatever single thing changed, data.<resource> reflects the record’s full state afterwards.
  • .activity’s data.item shape depends on data.type (comment, attachment, note, checklist, checklist_comment, milestone, task, log, or reply) — see the resource’s own page for which types it produces and their exact fields, including which delete actions carry a full item vs. just { "id": ... }.
  • data.action is normally added, updated or deleted. Checklists add one more, status_changed, for ticking an item off — see Projects.
Deletion events (*.deleted) fire after the record is gone, so data is just { "id": <deleted id> }.

Dates

The envelope’s top-level created field is ISO 8601 with a UTC offset (2026-07-13T10:15:00+00:00). Inside data, it depends on the field: any created/updated timestamp that names a record’s own creation/update — the resource’s dates.created/dates.updated, and the created field on .activity items (comment/attachment/log/reply) — is ISO 8601 with microseconds and a literal Z (2026-07-13T10:15:00.000000Z). Every one of these models (Client, Lead, Comment, Attachment, LeadLog, TicketReply, etc.) recasts its create/update columns to Carbon instances via Eloquent’s CREATED_AT/UPDATED_AT constants, which is what produces this format instead of a plain string. Every other date/time field (due dates, sent dates, expiry dates, a ticket’s last_updated, etc.) is a raw database value — Y-m-d H:i:s for timestamps, Y-m-d for date-only fields — not reformatted at all. Check each resource’s own page for exactly which fields are which; don’t assume from the field name alone.

Resources

Proposals, Contracts, Payments and Refunds have no .activity key — none of them has comments, attachments, or logs in core. Timesheets and Team are lifecycle-only: a time entry has no minor fields and no child records, and a team member’s changes all arrive as team.updated.

Imports

Records created by a bulk import do not fire <resource>.created. Importers write rows directly rather than going through the normal create path, so a 5,000-row import would otherwise produce 5,000 separate deliveries. Instead, the four importable resources — Clients, Leads, Projects and Products — fire a single <resource>.imported event carrying the batch:
Batching. Large imports are split across several deliveries of up to 1,000 records each, so a single request never grows large enough to be rejected by a receiver (nginx, for example, refuses request bodies over 1 MB by default). Every batch of one import shares the same import_ref, and batch/batch_count tell you how to reassemble them. Batches are queued in id order and delivered independently, so treat them as a set that has arrived only once you have seen batch_count of them. Records are deliberately compact — enough to identify each row and match it to your own data. If you need the full record, read it back from the API using its id. Checklist imports do not fire an import event; they create child records rather than resources.