Webhooks

Events Reference

Every webhook event type, when it fires, the read scope its endpoint's owner needs, and a complete example payload for each.

Subscribe an endpoint to the specific events it needs, or to Every event its owner's role may receive. An event is only delivered when the endpoint's owner could read the record it is about through the API: their role must reach the event's read scope, and the record must be one they can see.

Events are raised by the same code whether a change was made in the app, through the API or by an import, so an integration sees every change, whatever made it.

Each example below is the exact body your endpoint receives, with sample values. data.object has the same fields as the matching API endpoint at its standard tier; follow the event name to its schema in the API Reference. GET /webhook-events returns this catalogue to a token with webhooks:read.

Leave

EventFires whenOwner needs
leave.requestedA leave request was created. Sent for every new request, whatever its status: PENDING, or APPROVED when it needed no approval or was booked by the person's manager.leaves:read
leave.approvedA leave request was approved.leaves:read
leave.rejectedA leave request was rejected.leaves:read
leave.cancelledA leave was cancelled or withdrawn. Sent when a pending request is withdrawn, an approved leave is cancelled by an administrator or manager, or a cancellation request is approved. previous.status is the status before.leaves:read
leave.cancellation_requestedCancellation of an approved leave was requested. The person asked for their approved leave to be cancelled. It stays APPROVED until an approver decides.leaves:read
leave.updatedThe dates, half days or type of a leave request changed. A pending request was edited. previous holds the old value of each changed field.leaves:read
{
  "id": "evt_01JABCDEFGHJKMNPQRSTVWXYZ",
  "type": "leave.requested",
  "api_version": "v1",
  "created_at": "2026-10-12T17:04:11.512Z",
  "organization_id": "cmorg0000000000000000000",
  "data": {
    "object": {
      "id": "cmlve0hmarsh2026080300",
      "user_id": "cmusr0lenafischer000000",
      "leave_type_id": "cmlvt0annual00000000000",
      "leave_type": {
        "id": "cmlvt0annual00000000000",
        "name": "Annual Leave",
        "code": "ANNUAL",
        "color": "#3b82f6",
        "is_private": false
      },
      "status": "PENDING",
      "start_date": "2026-08-03",
      "end_date": "2026-08-07",
      "start_half": "FULL_DAY",
      "end_half": "FULL_DAY",
      "start_time": null,
      "end_time": null,
      "total_days": 5,
      "total_hours": null,
      "reason": "Family trip to Tofino",
      "decided_by_id": null,
      "decided_at": null,
      "decision_comment": null,
      "rejection_reason": null,
      "cancellation_requested_at": null,
      "cancellation_request_reason": null,
      "cancelled_at": null,
      "submitted_at": "2026-07-14T16:20:05.000Z",
      "created_at": "2026-07-14T16:20:05.000Z",
      "updated_at": "2026-07-14T16:20:05.000Z"
    }
  }
}

People

EventFires whenOwner needs
user.createdA person was added. Delivered when the endpoint owner may see the person in the directory. data.object is the directory (users:read) view of the person.users:read
user.updatedA person's directory or employment fields changed. A directory or employment field changed. previous holds the old values of the directory fields that changed.users:read
user.deactivatedA person's account was deactivated, including on completed offboarding. Sent when offboarding completes.users:read
user.reactivatedA person's account was reactivated.users:read
{
  "id": "evt_01JABCDEFGHJKMNPQRSTVWXYZ",
  "type": "user.created",
  "api_version": "v1",
  "created_at": "2026-10-12T17:04:11.512Z",
  "organization_id": "cmorg0000000000000000000",
  "data": {
    "object": {
      "id": "cmusr4k2h0000ex01lena0001",
      "first_name": "Lena",
      "last_name": "Fischer",
      "middle_name": null,
      "preferred_name": null,
      "email": "lena.fischer@northwind.example",
      "job_title": "Operations Analyst",
      "division": "North America",
      "work_location": "Vancouver, BC",
      "department_id": "cmdep4k2h0000ex01ops00001",
      "manager_id": "cmusr4k2h0000ex01marc0001",
      "role": "EMPLOYEE",
      "level": "STANDARD",
      "is_active": true,
      "created_at": "2024-02-20T17:00:00.000Z",
      "updated_at": "2026-09-01T17:00:00.000Z"
    }
  }
}

Departments

EventFires whenOwner needs
department.createdA department was created.departments:read
department.updatedA department was changed. previous holds the old values of the fields that changed.departments:read
department.deletedA department was deleted. A thin event: data.object carries only the id, because the department no longer exists.departments:read
{
  "id": "evt_01JABCDEFGHJKMNPQRSTVWXYZ",
  "type": "department.created",
  "api_version": "v1",
  "created_at": "2026-10-12T17:04:11.512Z",
  "organization_id": "cmorg0000000000000000000",
  "data": {
    "object": {
      "id": "cmdep4k2h0000ex01ops00001",
      "name": "Operations",
      "code": "OPS",
      "description": "Warehouse and dispatch operations",
      "color": "#3b82f6",
      "head_id": "cmusr4k2h0000ex01marc0001",
      "is_active": true,
      "member_count": 24,
      "created_at": "2024-01-10T00:00:00.000Z",
      "updated_at": "2026-08-14T21:30:00.000Z"
    }
  }
}

Time tracking

Delivered only while time tracking is switched on for the organization. An event raised while it is off is not queued, and a queued delivery is cancelled if the module is switched off before it is sent.

EventFires whenOwner needs
time_entry.submittedA time entry was submitted for approval. A completed entry that needs approval was created (by hand, by clocking out, or through the API), or an approved or rejected entry was changed and went back to PENDING. Delivered when time tracking is switched on, to endpoints whose owner may see the entry.time_entries:read
time_entry.approvedA time entry was approved.time_entries:read
time_entry.rejectedA time entry was rejected.time_entries:read
timesheet.finalizedA period timesheet was finalized. An approved timesheet was locked for payroll. Pay figures are never included in the event; read them with GET /timesheets/{id}.timesheets:read
{
  "id": "evt_01JABCDEFGHJKMNPQRSTVWXYZ",
  "type": "time_entry.submitted",
  "api_version": "v1",
  "created_at": "2026-10-12T17:04:11.512Z",
  "organization_id": "cmorg0000000000000000000",
  "data": {
    "object": {
      "id": "cmte00000000000000000001",
      "user_id": "cmusr0000000000000000012",
      "user": {
        "id": "cmusr0000000000000000012",
        "first_name": "Lena",
        "last_name": "Fischer"
      },
      "clock_in": "2026-10-08T16:00:00.000Z",
      "clock_out": "2026-10-09T00:30:00.000Z",
      "duration_minutes": 480,
      "break_minutes": 30,
      "project_id": "cmprj0000000000000000003",
      "project": {
        "id": "cmprj0000000000000000003",
        "name": "Warehouse migration"
      },
      "task_id": "cmtsk0000000000000000007",
      "task": {
        "id": "cmtsk0000000000000000007",
        "name": "Inventory count"
      },
      "description": "Cycle count, aisles 4 to 9",
      "notes": null,
      "tags": [
        "onsite"
      ],
      "is_billable": true,
      "status": "ACTIVE",
      "approval_status": "PENDING",
      "approved_by_id": null,
      "approved_at": null,
      "source": "API",
      "scheduled_shift_id": null,
      "created_at": "2026-10-09T00:31:12.000Z",
      "updated_at": "2026-10-09T00:31:12.000Z"
    }
  },
  "previous": {
    "approval_status": "REJECTED"
  }
}

Expenses

Delivered only while expenses are switched on for the organization, with the same rule as time tracking when the module is switched off.

EventFires whenOwner needs
expense_report.submittedAn expense report was submitted. Sent when a report leaves draft. previous.status is the status before submission.expenses:read
expense_report.approvedAn expense report was approved. Sent when an approver approves a report, or when the organization's auto-approve threshold approves it on submission.expenses:read
expense_report.rejectedAn expense report was rejected.expenses:read
expense_report.paidAn expense report was marked as paid.expenses:read
{
  "id": "evt_01JABCDEFGHJKMNPQRSTVWXYZ",
  "type": "expense_report.submitted",
  "api_version": "v1",
  "created_at": "2026-10-12T17:04:11.512Z",
  "organization_id": "cmorg0000000000000000000",
  "data": {
    "object": {
      "id": "cmexp0000000000000000042",
      "report_number": "EXP-2026-0042",
      "title": "Vancouver client workshop",
      "description": "Two-day onboarding workshop with the Pacific region team.",
      "trip_purpose": "Customer onboarding",
      "report_type": "REIMBURSEMENT",
      "status": "SUBMITTED",
      "departure_date": "2026-09-14",
      "return_date": "2026-09-16",
      "currency": "CAD",
      "total_expenses": 432.6,
      "total_per_diems": 112.5,
      "total_mileage": 60.48,
      "total_tax": 20.6,
      "grand_total": 605.58,
      "submitted_by": {
        "id": "cmusr0000000000000000014",
        "first_name": "Lena",
        "last_name": "Fischer"
      },
      "approver": {
        "first_name": "Marcus",
        "id": "cmusr0000000000000000009",
        "last_name": "Okafor"
      },
      "department_id": "cmdep0000000000000000003",
      "project_id": null,
      "cost_center": "CC-410",
      "is_billable": false,
      "submitted_at": "2026-09-17T16:10:05.000Z",
      "approved_at": null,
      "approver_notes": null,
      "rejection_reason": null,
      "paid_at": null,
      "payment_method": null,
      "payment_reference": null,
      "created_at": "2026-09-12T18:22:30.000Z",
      "updated_at": "2026-09-17T16:10:05.000Z",
      "payable_to": null,
      "line_items": [
        {
          "id": "cmeli0000000000000000001",
          "date": "2026-09-14",
          "description": "Hotel, two nights",
          "vendor": "Harbourfront Suites",
          "expense_type": "ACCOMMODATION",
          "expense_class": "Sales",
          "currency": "CAD",
          "exchange_rate": 1,
          "amount_before_tax": 412,
          "gst": 20.6,
          "pst": 0,
          "hst": 0,
          "qst": 0,
          "gratuity": 0,
          "total": 432.6,
          "total_in_base_currency": 432.6,
          "receipt_id": "cmerc0000000000000000001",
          "notes": null
        }
      ],
      "per_diems": [
        {
          "id": "cmepd0000000000000000001",
          "date": "2026-09-15",
          "per_diem_type": "FULL_DAY",
          "quantity": 1,
          "rate": 112.5,
          "currency": "CAD",
          "gst": 0,
          "pst": 0,
          "total": 112.5,
          "total_in_base_currency": 112.5,
          "meal_provided": false,
          "notes": null
        }
      ],
      "mileage": [
        {
          "id": "cmeml0000000000000000001",
          "date": "2026-09-16",
          "description": "Client site visit",
          "round_trip": true,
          "distance": 84,
          "unit": "KILOMETERS",
          "rate_per_unit": 0.72,
          "currency": "CAD",
          "gst": 0,
          "total": 60.48,
          "total_in_base_currency": 60.48,
          "notes": null
        }
      ],
      "receipts": [
        {
          "id": "cmerc0000000000000000001",
          "file_name": "harbourfront-folio.pdf",
          "mime_type": "application/pdf",
          "size_bytes": 184220,
          "uploaded_at": "2026-09-17T16:02:41.000Z"
        }
      ]
    }
  },
  "previous": {
    "status": "DRAFT"
  }
}

Documents

EventFires whenOwner needs
document.signedA document assignment was signed. Sent each time a signer signs. signature_status becomes SIGNED once the last signer has signed; previous.signature_status is present when it changed.documents:read
document.declinedA document assignment was declined. Sent when a signer declines to sign.documents:read
{
  "id": "evt_01JABCDEFGHJKMNPQRSTVWXYZ",
  "type": "document.signed",
  "api_version": "v1",
  "created_at": "2026-10-12T17:04:11.512Z",
  "organization_id": "cmorg0000000000000000000",
  "data": {
    "object": {
      "id": "cmdoc0000000000000000007",
      "title": "Employment agreement",
      "description": "Permanent full-time agreement, Operations.",
      "category": "CONTRACT",
      "file_name": "employment-agreement.pdf",
      "mime_type": "application/pdf",
      "size_bytes": 248113,
      "version": 1,
      "owner": {
        "first_name": "Lena",
        "id": "cmusr0000000000000000014",
        "last_name": "Fischer"
      },
      "uploaded_by": {
        "first_name": "Priya",
        "id": "cmusr0000000000000000002",
        "last_name": "Raman"
      },
      "requires_signature": true,
      "signature_status": "SIGNED",
      "signing_mode": "SEQUENTIAL",
      "signature_deadline": "2026-10-01T06:59:59.000Z",
      "signed_at": "2026-09-22T17:45:03.000Z",
      "signed_by_id": "cmusr0000000000000000014",
      "is_private": false,
      "is_company_library": false,
      "folder_id": null,
      "tags": [
        "onboarding"
      ],
      "created_at": "2026-09-20T15:00:00.000Z",
      "updated_at": "2026-09-22T17:45:03.000Z",
      "signers": [
        {
          "assignment_id": "cmasg0000000000000000011",
          "user": {
            "first_name": "Priya",
            "id": "cmusr0000000000000000002",
            "last_name": "Raman"
          },
          "status": "SIGNED",
          "signing_order": 1,
          "due_date": "2026-10-01T06:59:59.000Z",
          "viewed_at": "2026-09-21T14:12:00.000Z",
          "signed_at": "2026-09-21T14:15:27.000Z",
          "declined_at": null
        },
        {
          "assignment_id": "cmasg0000000000000000012",
          "user": {
            "first_name": "Lena",
            "id": "cmusr0000000000000000014",
            "last_name": "Fischer"
          },
          "status": "SIGNED",
          "signing_order": 2,
          "due_date": "2026-10-01T06:59:59.000Z",
          "viewed_at": "2026-09-22T17:40:10.000Z",
          "signed_at": "2026-09-22T17:45:03.000Z",
          "declined_at": null
        }
      ]
    }
  },
  "previous": {
    "signature_status": "PENDING"
  }
}

Testing

EventFires whenOwner needs
webhook.testA test event sent on request to check an endpoint. Sent only when someone chooses "Send test event" or calls POST /webhooks/{id}/test. Use it to check that your endpoint verifies the signature and answers 2xx.Nothing
{
  "id": "evt_01JABCDEFGHJKMNPQRSTVWXYZ",
  "type": "webhook.test",
  "api_version": "v1",
  "created_at": "2026-10-12T17:04:11.512Z",
  "organization_id": "cmorg0000000000000000000",
  "data": {
    "object": {
      "id": "cmwhk0000000000000000000",
      "object": "webhook_endpoint",
      "message": "A test event, sent on request to check that this endpoint receives and verifies events."
    }
  }
}

Changes to events

New event types may be added at any time. An endpoint subscribed to Every event starts receiving them automatically, so ignore types you do not recognise. Changing the shape of an existing event's payload follows the same rules as API fields: additions are made in place, and anything breaking would come with a new API version. See Versioning & Changes.