Skip to main content
Every Grow CRM webhook event shares the same envelope — { event, id, created, data } — described in Introduction. What changes between events is the value of event and the shape of data. Rather than exposing a long flat list of narrow event keys, Grow CRM groups related changes under five keys per resource and uses a discriminator field inside data to tell you exactly what happened. This page explains the model in full; for a specific resource’s exact payload fields and discriminator values, follow the link in the Resources table below.

The five-key model

Every resource exposes up to five subscribable event keys. Subscribe to a key once and branch on the discriminator — you do not need a separate subscription per change type.

Discriminator fields

Use the discriminator values to branch your handler logic without subscribing to multiple keys:
  • .updated — branch on data.change. Every .updated delivery carries the complete, current resource object in data.<resource> — not a diff. Whatever single thing changed, the object reflects the record’s full state after the change.
  • .sundry — branch on data.field. As with .updated, data.<resource> always contains the full current object.
  • .activity — branch on both data.type and data.action. The value of data.type determines the shape of data.item. Supported types include comment, attachment, note, checklist, checklist_comment, milestone, task, log, and reply — see each resource’s own page for which types it produces. data.action is normally added, updated, or deleted; checklists additionally use status_changed for ticking an item off.
Deletion events (*.deleted) fire after the record has already been removed. Because the record no longer exists to describe, data contains only { "id": <deleted id> }. The same applies to .activity delete actions for some child record types — see each resource’s page for which delete actions carry a full item versus just { "id": ... }.

Date formatting

Date and time values inside data follow two different formats depending on the field type:
  • ISO 8601 with microseconds (2026-07-13T10:15:00.000000Z) — used for any created or updated timestamp that represents a record’s own creation or update time. This includes dates.created and dates.updated on resource objects, and the created field on .activity items such as comments, attachments, logs, and replies. These fields are always serialised as microsecond-precision UTC strings.
  • Raw database format — used for every other date or time field. Timestamps appear as Y-m-d H:i:s; date-only fields appear as Y-m-d. Examples include due dates, sent dates, expiry dates, and a ticket’s last_updated field.
Do not infer the format from the field name alone. Check each resource’s own reference page to confirm whether a specific field uses ISO 8601 or the raw database format.

Resources

The table below lists all 15 resources and their available event keys. Follow each link for full payload shapes, discriminator values, and example JSON.
Proposals, Contracts, Payments, and Refunds have no .activity key — none of those resources has comments, attachments, or logs. Timesheets and Team are lifecycle-only: time entries have no minor fields and no child records, and all team member changes arrive as team.updated.

Imports

Records created by a bulk import do not fire <resource>.created. Importers write rows directly to the database rather than going through the normal create path — 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 that carries the entire batch. The envelope id for an import event is the import’s reference string, not a numeric record id. This is the only place in the webhook system where id is not an integer.
Import payload example

Import payload fields

Batching rules

Large imports are split across multiple deliveries of up to 1,000 records each, so no single request body grows large enough to be rejected by a receiver with a strict maximum body-size limit. Every batch belonging to the same import shares the same import_ref, and the batch / batch_count fields tell you how to reassemble them.
Batches are queued in id order and delivered independently. Treat the import as complete only once you have received batch_count deliveries sharing the same import_ref. Records in each batch are deliberately compact — enough to identify each row and match it to your own data. If you need the full record, fetch it from the API using its id.
Checklist imports do not fire an import event. Checklists create child records rather than top-level resources, so individual checklist rows are associated with their parent records without triggering a batch delivery.