> ## Documentation Index
> Fetch the complete documentation index at: https://webhooks.docs.crm.africa/llms.txt
> Use this file to discover all available pages before exploring further.

# Client Webhooks — Full Payload Reference | Grow CRM

> Explore every client webhook event Grow CRM emits — created, updated, deleted, sundry, activity, and imported — with full payload examples.

Grow CRM fires six distinct event keys for client records: `client.created`, `client.updated`, `client.deleted`, `client.sundry`, `client.activity`, and `client.imported`. Use these events to keep external systems in sync whenever a client is created, edited, annotated, or deleted, and to handle bulk-import completions as a single batched notification rather than a flood of individual creation events.

<Note>
  The webhook envelope structure, delivery semantics, retry behaviour, and signature verification conventions are documented in [Introduction](/introduction) and [Verification](/verification). Those rules apply to every event on this page and are not repeated here.
</Note>

***

## The client object

Every event key except `client.deleted` carries the full client object. For `client.created` it appears flat directly inside `data`; for `client.updated` and `client.sundry` it is nested under `data.client`.

```json theme={null}
{
  "id": 12,
  "company_name": "Acme Inc",
  "description": "Key account.",
  "status": "active",
  "phone": "+1 555 0100",
  "website": "https://acme.example",
  "category": { "id": 2, "name": "Standard" },
  "billing": {
    "street": "1 Market St",
    "city": "San Francisco",
    "state": "CA",
    "zip": "94105",
    "country": "United States"
  },
  "dates": { "created": "2026-06-15T09:30:00.000000Z", "updated": "2026-07-16T08:12:00.000000Z" },
  "tags": ["priority", "retainer"]
}
```

<ResponseField name="id" type="integer">
  The client's primary key (`client_id`).
</ResponseField>

<ResponseField name="company_name" type="string">
  The client's company or display name.
</ResponseField>

<ResponseField name="description" type="string | null">
  Free-text description; `null` when not set.
</ResponseField>

<ResponseField name="status" type="string">
  Either `active` or `suspended`.
</ResponseField>

<ResponseField name="phone" type="string | null">
  Primary contact phone number; `null` when not set.
</ResponseField>

<ResponseField name="website" type="string | null">
  Client website URL; `null` when not set.
</ResponseField>

<ResponseField name="category" type="object">
  `{ id, name }` — the client category assigned in Grow CRM.
</ResponseField>

<ResponseField name="billing" type="object">
  Billing address only — `street`, `city`, `state`, `zip`, `country`. No shipping address or VAT number on this shape.
</ResponseField>

<ResponseField name="dates" type="object">
  `created` and `updated`, both ISO 8601 timestamps with microseconds (`…T…Z`).
</ResponseField>

<ResponseField name="tags" type="array of strings">
  Tag titles attached to the client.
</ResponseField>

***

## `client.created`

Grow CRM fires `client.created` immediately after a new client record is saved. `data` is the full client object shown above.

```json theme={null}
{
  "event": "client.created",
  "id": 12,
  "created": "2026-07-16T08:00:00+00:00",
  "data": {
    "id": 12,
    "company_name": "Acme Inc",
    "description": null,
    "status": "active",
    "phone": null,
    "website": null,
    "category": { "id": 1, "name": "Default" },
    "billing": { "street": null, "city": null, "state": null, "zip": null, "country": null },
    "dates": { "created": "2026-07-16T08:00:00.000000Z", "updated": "2026-07-16T08:00:00.000000Z" },
    "tags": []
  }
}
```

<Tip>
  Clients created through a bulk import do **not** trigger `client.created`. Use `client.imported` instead to capture those records.
</Tip>

***

## `client.updated`

Grow CRM fires `client.updated` whenever a significant field on the client changes. Inspect `data.change` to determine what changed; `data.client` always carries the complete, up-to-date client object.

| `data.change` | Fires when                                                                                             |
| ------------- | ------------------------------------------------------------------------------------------------------ |
| `edited`      | The main client edit form is saved (name, phone, website, category, billing address, status, or tags). |
| `owner`       | The client's primary account owner is reassigned.                                                      |

```json theme={null}
{
  "event": "client.updated",
  "id": 12,
  "created": "2026-07-16T08:05:00+00:00",
  "data": {
    "change": "owner",
    "client": { "id": 12, "company_name": "Acme Inc", "...": "..." }
  }
}
```

***

## `client.sundry`

Grow CRM fires `client.sundry` for minor, cosmetic field edits that don't warrant a full `client.updated` signal. Inspect `data.field` to identify which field changed; `data.client` carries the full client object — it is not a partial diff.

| `data.field`  | Fires when                                                                      |
| ------------- | ------------------------------------------------------------------------------- |
| `description` | The client's description is edited on its own (inline edit, not the main form). |
| `logo`        | The client's logo is changed.                                                   |

```json theme={null}
{
  "event": "client.sundry",
  "id": 12,
  "created": "2026-07-16T08:07:00+00:00",
  "data": {
    "field": "description",
    "client": { "id": 12, "company_name": "Acme Inc", "...": "..." }
  }
}
```

***

## `client.activity`

Grow CRM fires `client.activity` whenever a child record attached to a client is created, updated, or deleted. Use `data.type` and `data.action` together to identify the exact operation; `data.item` describes the child record.

<Note>
  Clients have no comments feature. Unlike projects, leads, and tasks, `client.activity` never carries `type: "comment"`.
</Note>

| `data.type`  | `data.action`                 | `data.item` shape                                                                                                                                           |
| ------------ | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `attachment` | `added`, `deleted`            | `{ id, uniqueid, filename }` — files uploaded to the client's Files tab. On delete, the record is read before removal so the full shape is still available. |
| `note`       | `added`, `updated`, `deleted` | `{ id, title, text, created }` — notes marked **private** are never delivered; the CRM restricts those to their author.                                     |

```json theme={null}
{
  "event": "client.activity",
  "id": 12,
  "created": "2026-07-16T08:10:00+00:00",
  "data": {
    "type": "note",
    "action": "added",
    "item": {
      "id": 72,
      "title": "Renewal",
      "text": "Renewed for another year.",
      "created": "2026-07-16T08:10:00.000000Z"
    }
  }
}
```

***

## `client.deleted`

Grow CRM fires `client.deleted` after the client record is permanently removed. Because the record no longer exists, `data` contains only the client's `id`.

```json theme={null}
{
  "event": "client.deleted",
  "id": 12,
  "created": "2026-07-16T08:15:00+00:00",
  "data": { "id": 12 }
}
```

***

## `client.imported`

Grow CRM fires `client.imported` once when a bulk client import job completes. Imported clients do **not** individually fire `client.created`. See [Imports](/events#imports) for the batch shape, batching rules, and the fields common to every import event.

Each record inside `data.records` carries the following fields:

| Field     | Type           |
| --------- | -------------- |
| `id`      | integer        |
| `name`    | string         |
| `email`   | string \| null |
| `created` | string         |

```json theme={null}
{
  "event": "client.imported",
  "id": "8f3ka92m",
  "created": "2026-07-31T10:00:00+00:00",
  "data": {
    "import_ref": "8f3ka92m",
    "imported_by": 3,
    "batch": 1,
    "batch_count": 2,
    "totals": { "imported": 1420, "skipped": 8, "errors": 0 },
    "records": [
      {
        "id": 12,
        "name": "Acme Inc",
        "email": "billing@acme.example",
        "created": "2026-07-31T10:00:00.000000Z"
      }
    ]
  }
}
```

<Info>
  `batch_count` tells you the total number of delivery batches for this import run. Wait until you have received all `batch_count` payloads sharing the same `import_ref` before treating the import as fully processed.
</Info>
