> ## 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.

# Product Webhook Events: Full Payload Reference Guide

> Reference for product webhook events—created, updated, deleted, sundry, activity, and imported—with payload shapes, field tables, and import notes.

Grow CRM emits webhook events across the full product lifecycle. Your endpoint may receive six event keys: `item.created`, `item.updated`, `item.deleted`, `item.sundry`, `item.activity`, and `item.imported`. Every event key uses the `item.*` prefix — you will see `item.created`, `item.updated`, and so on rather than `product.*`. Use `item.created` for single product creation, `item.updated` for form edits, `item.activity` to track task template changes, and `item.imported` for bulk imports — note that imported products do **not** also fire `item.created`, so you must subscribe to both events if you need to capture all new products regardless of how they were added.

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

***

## The product object

Every event key except `item.deleted` and `item.imported` carries the full product object. In `item.created` the object appears flat inside `data`; in `item.updated` and `item.sundry` it is nested under `data.item`.

| Field           | Type           | Notes                                                                                                                                                 |
| --------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | integer        | Unique product identifier.                                                                                                                            |
| `description`   | string         | The product's name — products have no separate title field.                                                                                           |
| `type`          | string         | `product` or `service`.                                                                                                                               |
| `rate`          | string         | Decimal as string.                                                                                                                                    |
| `unit`          | object         | `{ id, name }` — the unit of measure.                                                                                                                 |
| `category`      | object         | `{ id, name }`.                                                                                                                                       |
| `tax`           | object         | `status` is the product's tax status; `default_tax_id` is its default tax rate, if set.                                                               |
| `notes`         | string \| null |                                                                                                                                                       |
| `dates`         | object         | Raw database value. `created` uses `Y-m-d H:i:s` format, **not** the ISO-with-microseconds format used by most other resources.                       |
| `custom_fields` | object         | Only populated slots appear. A product using none of the ten custom field slots emits `{}` rather than ten nulls — do not assume a fixed set of keys. |

<Info>
  The `dates.created` timestamp on product objects is a raw database value in `Y-m-d H:i:s` format (for example `"2026-07-31 09:00:00"`), not the ISO 8601 with microseconds format you see on invoices, payments, and expenses.
</Info>

```json theme={null}
{
  "id": 58,
  "description": "Website hosting — annual",
  "type": "product",
  "rate": "480.00",
  "unit": { "id": 2, "name": "year" },
  "category": { "id": 4, "name": "Hosting" },
  "tax": { "status": "taxable", "default_tax_id": 1 },
  "notes": "Renews each January",
  "dates": { "created": "2026-07-31 09:00:00" },
  "custom_fields": { "item_custom_field_1": "SKU-4417" }
}
```

***

## `item.created`

Fires when a product is created. `data` is the full product object.

```json theme={null}
{
  "event": "item.created",
  "id": 58,
  "created": "2026-07-31T09:00:00+00:00",
  "data": {
    "id": 58,
    "description": "Website hosting — annual",
    "type": "product",
    "rate": "480.00",
    "unit": { "id": 2, "name": "year" },
    "category": { "id": 4, "name": "Hosting" },
    "tax": { "status": "taxable", "default_tax_id": 1 },
    "notes": null,
    "dates": { "created": "2026-07-31 09:00:00" },
    "custom_fields": {}
  }
}
```

***

## `item.updated`

Fires when a significant change is made to a product. Read `data.change` to determine what changed; `data.item` contains the full product object.

| `data.change` | Fires when                      |
| ------------- | ------------------------------- |
| `edited`      | The product edit form is saved. |

```json theme={null}
{
  "event": "item.updated",
  "id": 58,
  "created": "2026-07-31T09:05:00+00:00",
  "data": {
    "change": "edited",
    "item": {
      "id": 58,
      "rate": "520.00",
      "...": "..."
    }
  }
}
```

***

## `item.sundry`

Fires when a minor or cosmetic field is edited. Read `data.field` to determine which field changed; `data.item` contains the full product object.

| `data.field` | Fires when                                                |
| ------------ | --------------------------------------------------------- |
| `category`   | The product's category is changed from the products list. |

<Tip>
  Changing the category of several products at once delivers one `item.sundry` event per product — not one event containing a list.
</Tip>

```json theme={null}
{
  "event": "item.sundry",
  "id": 58,
  "created": "2026-07-31T09:07:00+00:00",
  "data": {
    "field": "category",
    "item": {
      "id": 58,
      "category": { "id": 4, "name": "Hosting" },
      "...": "..."
    }
  }
}
```

***

## `item.activity`

Fires when a child record on the product changes. Products produce only `type: "task"` — these are the task templates attached to a product, which Grow CRM copies onto a project when the product is used. Read `data.action` to distinguish additions, edits, and deletions.

| `data.type` | `data.action` | `data.item` shape                                                                                               |
| ----------- | ------------- | --------------------------------------------------------------------------------------------------------------- |
| `task`      | `added`       | `{ id, title, description, created }`                                                                           |
| `task`      | `updated`     | `{ id, title, description, created }`                                                                           |
| `task`      | `deleted`     | `{ id, title, description, created }` — the record is read before removal, so the full shape is always present. |

```json theme={null}
{
  "event": "item.activity",
  "id": 58,
  "created": "2026-07-31T09:10:00+00:00",
  "data": {
    "type": "task",
    "action": "added",
    "item": {
      "id": 22,
      "title": "Provision hosting account",
      "description": "Create the account and send credentials",
      "created": "2026-07-31 09:10:00"
    }
  }
}
```

***

## `item.deleted`

Fires after the product is permanently deleted. Because the record no longer exists, `data` contains only the product `id`.

Both delete paths are covered: the checkbox selection on the products list, and the bulk delete action. Deleting several products at once delivers one event **per product** — not one event containing a list.

```json theme={null}
{
  "event": "item.deleted",
  "id": 58,
  "created": "2026-07-31T09:15:00+00:00",
  "data": { "id": 58 }
}
```

***

## `item.imported`

Fires when a bulk product import finishes. Imported products do **not** also fire `item.created` — subscribe to `item.imported` in addition to `item.created` if you need to capture all new products regardless of how they entered the system.

<Warning>
  Bulk-imported products fire only `item.imported`, not `item.created`. If you subscribe to `item.created` alone, you will miss products added via the import flow.
</Warning>

Each record in `data.records` carries a condensed shape — not the full product object:

| Field         | Type    |
| ------------- | ------- |
| `id`          | integer |
| `description` | string  |
| `rate`        | string  |
| `created`     | string  |

The `data` envelope for `item.imported` follows the standard import batch shape. The top-level `id` in the envelope is the `import_ref` string, not a numeric resource id.

```json theme={null}
{
  "event": "item.imported",
  "id": "8f3ka92m",
  "created": "2026-07-31T10:00:00+00:00",
  "data": {
    "import_ref": "8f3ka92m",
    "imported_by": 3,
    "batch": 1,
    "batch_count": 1,
    "totals": { "imported": 42, "skipped": 0, "errors": 1 },
    "records": [
      {
        "id": 58,
        "description": "Website hosting — annual",
        "rate": "480.00",
        "created": "2026-07-31 10:00:00"
      }
    ]
  }
}
```
