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

# Grow CRM Timesheet Webhook Events: Payload Reference

> Reference for timesheet.created, timesheet.updated, and timesheet.deleted webhook event payloads for manually recorded time entries.

Grow CRM emits three webhook event keys for the Timesheets resource: `timesheet.created`, `timesheet.updated`, and `timesheet.deleted`. Use these events to sync billable hours, update invoicing systems, and audit time records as they are created, edited, or removed. There is no `.sundry` or `.activity` key — a time entry has no minor fields and carries no child records.

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

<Warning>
  **These events cover manually recorded time only.** Time captured with the built-in task timer (start/stop on a task) does **not** fire them. A running timer is not yet a completed record, and stop/start churn would produce a large volume of low-value deliveries. If you need timer-tracked hours, read them from the API rather than expecting a webhook.
</Warning>

***

## The timesheet object

`timesheet.created` and `timesheet.updated` carry this shape — flat inside `data` for `.created`, or nested under `data.timesheet` for `.updated`.

| Field         | Type          | Notes                                                                                                                                 |
| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `id`          | integer       | `timer_id`.                                                                                                                           |
| `status`      | string        | `stopped` for a recorded timesheet. `running` only appears on live timers fetched via the API — these webhooks never fire for one.    |
| `seconds`     | integer       | Recorded time in whole seconds, as stored.                                                                                            |
| `hours`       | number        | `seconds / 3600`, rounded to 2 decimal places. A convenience field — `seconds` is authoritative.                                      |
| `task`        | object        | `{ id, title }` — the task the time was logged against.                                                                               |
| `project_id`  | integer\|null | Denormalised from the task.                                                                                                           |
| `client_id`   | integer\|null | Denormalised from the task.                                                                                                           |
| `user`        | object        | `{ id, name }` — who the time is **for**.                                                                                             |
| `recorded_by` | integer\|null | Who **entered** it. Differs from `user.id` when a manager logs time on someone else's behalf.                                         |
| `billing`     | object        | `status` is `invoiced` or `not_invoiced`; `invoice_id` is set once billed.                                                            |
| `dates`       | object        | `created` uses `YYYY-MM-DD HH:MM:SS` format (no timezone offset), unlike the ISO 8601 with microseconds used by most other resources. |

```json theme={null}
{
  "id": 904,
  "status": "stopped",
  "seconds": 5400,
  "hours": 1.5,
  "task": { "id": 611, "title": "Build the export screen" },
  "project_id": 305,
  "client_id": 12,
  "user": { "id": 7, "name": "Dana" },
  "recorded_by": 3,
  "billing": { "status": "not_invoiced", "invoice_id": null },
  "dates": { "created": "2026-07-31 09:00:00" }
}
```

***

## `timesheet.created`

Fires when time is recorded in the Timesheets section. `data` is the full timesheet object.

<Tip>
  One submission can create several timesheets — the form accepts a row per assigned task. Each recorded row delivers its own `timesheet.created` event; there is no combined batch event.
</Tip>

```json theme={null}
{
  "event": "timesheet.created",
  "id": 904,
  "created": "2026-07-31T09:00:00+00:00",
  "data": {
    "id": 904,
    "status": "stopped",
    "seconds": 5400,
    "hours": 1.5,
    "task": { "id": 611, "title": "Build the export screen" },
    "project_id": 305,
    "client_id": 12,
    "user": { "id": 7, "name": "Dana" },
    "recorded_by": 3,
    "billing": { "status": "not_invoiced", "invoice_id": null },
    "dates": { "created": "2026-07-31 09:00:00" }
  }
}
```

***

## `timesheet.updated`

Fires on a significant change to a timesheet. Read `data.change` to determine what changed; `data.timesheet` contains the full timesheet object reflecting the new state.

| `data.change` | Fires when                                                                |
| ------------- | ------------------------------------------------------------------------- |
| `edited`      | The timesheet edit form is saved. Only the recorded duration is editable. |

```json theme={null}
{
  "event": "timesheet.updated",
  "id": 904,
  "created": "2026-07-31T09:30:00+00:00",
  "data": {
    "change": "edited",
    "timesheet": { "id": 904, "seconds": 7200, "hours": 2, "...": "..." }
  }
}
```

***

## `timesheet.deleted`

Fires after a timesheet is deleted. Because the record no longer exists at delivery time, `data` contains only the timesheet id.

<Tip>
  Deletion is a checkbox selection, so removing several timesheets at once delivers one `timesheet.deleted` event **per timesheet** — not one event containing a list.
</Tip>

```json theme={null}
{
  "event": "timesheet.deleted",
  "id": 904,
  "created": "2026-07-31T09:40:00+00:00",
  "data": { "id": 904 }
}
```
