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

# Expense Webhook Events: Full Payload Reference Guide

> Reference for expense webhook events—created, updated, deleted, sundry, and activity—with payload shapes, field tables, and attachment event details.

Grow CRM emits webhook events across the full expense lifecycle. Your endpoint may receive five event keys: `expense.created`, `expense.updated`, `expense.deleted`, `expense.sundry`, and `expense.activity`. Use `expense.updated` to react to significant changes such as editing core fields, updating recurring settings, or attaching an expense to a project; use `expense.sundry` for lower-significance field edits such as category changes; and use `expense.activity` to track file attachments added or removed from an expense. Both cloning an expense and creating one from scratch fire `expense.created`, so you can rely on that single event to capture the full object at the point of entry.

<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 expense object

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

| Field            | Type             | Notes                                                          |
| ---------------- | ---------------- | -------------------------------------------------------------- |
| `id`             | integer          | Unique expense identifier.                                     |
| `description`    | string \| null   |                                                                |
| `date`           | string           | Date only — also present in `dates.date`.                      |
| `amount`         | string           | Decimal as string.                                             |
| `billable`       | string           | `billable` or `not_billable`.                                  |
| `billing_status` | string           | `invoiced` or `not_invoiced`.                                  |
| `category`       | object           | `{ id, name }`.                                                |
| `client`         | object           | `{ id, name }`.                                                |
| `project_id`     | integer \| null  |                                                                |
| `dates`          | object           | `date` is a raw date; `created` is ISO 8601 with microseconds. |
| `tags`           | array of strings |                                                                |

```json theme={null}
{
  "id": 214,
  "description": "AWS hosting — July",
  "date": "2026-07-16",
  "amount": "120.00",
  "billable": "billable",
  "billing_status": "not_invoiced",
  "category": { "id": 3, "name": "Hosting" },
  "client": { "id": 12, "name": "Acme Inc" },
  "project_id": 305,
  "dates": {
    "date": "2026-07-16",
    "created": "2026-07-16T08:00:00.000000Z"
  },
  "tags": []
}
```

***

## `expense.created`

Fires when an expense is created, **including when an existing expense is cloned**. `data` is the full expense object.

```json theme={null}
{
  "event": "expense.created",
  "id": 214,
  "created": "2026-07-16T08:00:00+00:00",
  "data": {
    "id": 214,
    "description": "AWS hosting — July",
    "date": "2026-07-16",
    "amount": "120.00",
    "billable": "billable",
    "billing_status": "not_invoiced",
    "category": { "id": 3, "name": "Hosting" },
    "client": { "id": 12, "name": "Acme Inc" },
    "project_id": 305,
    "dates": {
      "date": "2026-07-16",
      "created": "2026-07-16T08:00:00.000000Z"
    },
    "tags": []
  }
}
```

***

## `expense.updated`

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

| `data.change` | Fires when                                               |
| ------------- | -------------------------------------------------------- |
| `edited`      | The main expense edit form is saved.                     |
| `recurring`   | The expense's recurring settings are updated or stopped. |
| `project`     | The expense is attached to a project.                    |

```json theme={null}
{
  "event": "expense.updated",
  "id": 214,
  "created": "2026-07-16T08:05:00+00:00",
  "data": {
    "change": "project",
    "expense": {
      "id": 214,
      "project_id": 305,
      "...": "..."
    }
  }
}
```

***

## `expense.sundry`

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

| `data.field` | Fires when                         |
| ------------ | ---------------------------------- |
| `category`   | The expense's category is changed. |

```json theme={null}
{
  "event": "expense.sundry",
  "id": 214,
  "created": "2026-07-16T08:07:00+00:00",
  "data": {
    "field": "category",
    "expense": {
      "id": 214,
      "category": { "id": 3, "name": "Hosting" },
      "...": "..."
    }
  }
}
```

***

## `expense.activity`

Fires when a child record on the expense changes. Expenses produce only `type: "attachment"`. Read `data.action` to distinguish additions from deletions.

| `data.type`  | `data.action` | `data.item` shape                                                                                                                           |
| ------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `attachment` | `added`       | `{ id, uniqueid, filename, created }`                                                                                                       |
| `attachment` | `deleted`     | `{ id, uniqueid, filename, created }` — the attachment record is looked up **before** it is removed, so the full shape is always available. |

<Tip>
  Unlike invoices, expense attachment deletion uses a pre-delete hook, so the full item shape (including `filename` and `created`) is reliably present in both `added` and `deleted` deliveries.
</Tip>

```json theme={null}
{
  "event": "expense.activity",
  "id": 214,
  "created": "2026-07-16T08:10:00+00:00",
  "data": {
    "type": "attachment",
    "action": "added",
    "item": {
      "id": 88,
      "uniqueid": "ryy388jf",
      "filename": "receipt.jpg",
      "created": "2026-07-16T08:10:00.000000Z"
    }
  }
}
```

***

## `expense.deleted`

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

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