Webhook events
Every event, when it is sent, and an example payload of each.
These are the events an endpoint can receive. You choose them per endpoint under Integrations → API → Webhooks; how deliveries, signatures and retries work is in Webhooks. Every example payload below comes from Schedulinq's own test of the payloads, so it has exactly the shape your endpoint receives.
The payload
Every message is an envelope:
type— the event, such asappointment.created;timestamp— when the change happened, in UTC;data— the appointment, invitation or team member as it is after the change, in the same shape the API reads it (see the reference).
data also carries:
metadata— your metadata,{}when there are none;version— a number that increases per appointment, invitation or team member. Keep the highest one you processed, and drop an event with a lower one. Versions can skip numbers;changedBy— who made the change.typeisUSER(a team member, withuserId),API_KEY(your own system, withapiKeyId),CUSTOMER,SYSTEM(Schedulinq itself),INTEGRATION(Teamleader) orCALENDAR_SYNC(a connected calendar);- on
appointment.rescheduledonly,previous: thestart,endanduserIdsbefore the change; - on
colleague.activated, theinvitationIdof the accepted invitation and that invitation's metadata.
A payload never holds a customer's name, e-mail address or phone number, and never a booking link.
Appointments
appointment.created
An appointment was booked: in the dashboard (also from a planning link), by the customer on the booking page or from an invitation, or from Teamleader or a connected calendar.
Example payload
{
"type": "appointment.created",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e09",
"status": "CONFIRMED",
"start": "2026-10-14T10:00:00+02:00",
"end": "2026-10-14T11:00:00+02:00",
"timeZone": "Europe/Amsterdam",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"locationMode": "AT_CUSTOMER",
"address": {
"line1": "Keizersgracht 100",
"postalCode": "1015 AA",
"city": "Amsterdam",
"country": "NL"
},
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 1,
"changedBy": {
"type": "API_KEY",
"apiKeyId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e0a"
}
}
}appointment.rescheduled
The start, the end or the team members changed. A customer who moves their own
appointment gets a new appointment: this event then arrives on the new id, with
rescheduledFromId naming the old one and previous its times. No separate
appointment.cancelled is sent for the old one.
Example payload
{
"type": "appointment.rescheduled",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e09",
"status": "CONFIRMED",
"start": "2026-10-14T10:00:00+02:00",
"end": "2026-10-14T11:00:00+02:00",
"timeZone": "Europe/Amsterdam",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"locationMode": "AT_CUSTOMER",
"address": {
"line1": "Keizersgracht 100",
"postalCode": "1015 AA",
"city": "Amsterdam",
"country": "NL"
},
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"rescheduledFromId": "00000000-0000-0000-0000-000000000001",
"lineageRootId": "00000000-0000-0000-0000-000000000001",
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 1,
"changedBy": {
"type": "CUSTOMER"
},
"previous": {
"start": "2026-10-13T14:00:00+02:00",
"end": "2026-10-13T15:00:00+02:00",
"userIds": [
"3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
]
}
}
}appointment.updated
Something else on the appointment changed, such as the service, the place or the meeting link.
Example payload
{
"type": "appointment.updated",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e09",
"status": "CONFIRMED",
"start": "2026-10-14T10:00:00+02:00",
"end": "2026-10-14T11:00:00+02:00",
"timeZone": "Europe/Amsterdam",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"locationMode": "AT_CUSTOMER",
"address": {
"line1": "Keizersgracht 100",
"postalCode": "1015 AA",
"city": "Amsterdam",
"country": "NL"
},
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 2,
"changedBy": {
"type": "USER",
"userId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
}
}
}appointment.cancelled
Cancelled by the customer, a team member, a connected calendar or Teamleader, or
because the customer was deleted. cancelledBy says by whom.
Example payload
{
"type": "appointment.cancelled",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e09",
"status": "CANCELLED",
"cancelledAt": "2026-10-01T10:30:00+02:00",
"cancelledBy": "STAFF",
"cancellationReason": "De klant heeft telefonisch afgezegd.",
"start": "2026-10-14T10:00:00+02:00",
"end": "2026-10-14T11:00:00+02:00",
"timeZone": "Europe/Amsterdam",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"locationMode": "AT_CUSTOMER",
"address": {
"line1": "Keizersgracht 100",
"postalCode": "1015 AA",
"city": "Amsterdam",
"country": "NL"
},
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 2,
"changedBy": {
"type": "USER",
"userId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
}
}
}appointment.completed
Finished, also when its invoice was issued.
Example payload
{
"type": "appointment.completed",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e09",
"status": "COMPLETED",
"start": "2026-10-14T10:00:00+02:00",
"end": "2026-10-14T11:00:00+02:00",
"timeZone": "Europe/Amsterdam",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"locationMode": "AT_CUSTOMER",
"address": {
"line1": "Keizersgracht 100",
"postalCode": "1015 AA",
"city": "Amsterdam",
"country": "NL"
},
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 2,
"changedBy": {
"type": "USER",
"userId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
}
}
}appointment.deleted
Archived. A read of the appointment answers 404 from then on.
Example payload
{
"type": "appointment.deleted",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e09",
"status": "COMPLETED",
"start": "2026-10-14T10:00:00+02:00",
"end": "2026-10-14T11:00:00+02:00",
"timeZone": "Europe/Amsterdam",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"locationMode": "AT_CUSTOMER",
"address": {
"line1": "Keizersgracht 100",
"postalCode": "1015 AA",
"city": "Amsterdam",
"country": "NL"
},
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 2,
"changedBy": {
"type": "USER",
"userId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
}
}
}When a team member uses Reschedule, the appointment is cancelled with
cancelledBy: STAFF_RESCHEDULE and an invitation.created follows: the customer gets an
invitation to pick a new time. Their booking later sends invitation.booked and
appointment.created, and the new appointment carries rescheduledFromId.
There is no appointment.no_show: nothing in Schedulinq marks an appointment as a
no-show.
Invitations
invitation.created
An invitation to book was sent: from the dashboard, through the API, after a Reschedule, or by a subscription.
Example payload
{
"type": "invitation.created",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e08",
"status": "PENDING",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"appointmentIds": [],
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 1,
"changedBy": {
"type": "API_KEY",
"apiKeyId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e0a"
}
}
}invitation.booked
The customer booked through the invitation. appointmentIds lists the appointments,
earliest first.
Example payload
{
"type": "invitation.booked",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e08",
"status": "USED",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"appointmentIds": [
"3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e09"
],
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 1,
"changedBy": {
"type": "CUSTOMER"
}
}
}invitation.expired
The invitation passed its end date without a booking.
Example payload
{
"type": "invitation.expired",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e08",
"status": "EXPIRED",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"appointmentIds": [],
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 1,
"changedBy": {
"type": "SYSTEM"
}
}
}invitation.cancelled
The invitation was withdrawn in the dashboard, or its customer was deleted.
Example payload
{
"type": "invitation.cancelled",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e08",
"status": "CANCELLED",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"appointmentIds": [],
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 1,
"changedBy": {
"type": "USER",
"userId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
}
}
}invitation.deleted
The invitation was archived. A read of it answers 404 from then on.
Example payload
{
"type": "invitation.deleted",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e08",
"status": "PENDING",
"service": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
"name": "Offertebezoek"
},
"colleagues": [
{
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"name": "Sanne de Vries"
}
],
"customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
},
"appointmentIds": [],
"createdAt": "2026-10-01T10:30:00+02:00",
"updatedAt": "2026-10-01T10:30:00+02:00",
"version": 1,
"changedBy": {
"type": "USER",
"userId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
}
}
}Changing an open invitation in the dashboard sends no event.
Team members
These fire for every team member, not only for the ones your system invited. For the
others, metadata is {}.
colleague.activated
Someone accepted their invitation and is now a team member. invitationId and
metadata are those of the invitation, so you can match them to the request that
created it.
Example payload
{
"type": "colleague.activated",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "00000000-0000-0000-0000-000000000001",
"status": "ACTIVE",
"firstName": "Piet",
"lastName": "Bakker",
"name": "Piet Bakker",
"email": "piet+ov@loodgieter-bakker.example",
"admin": false,
"schedulable": true,
"calendarConnected": false,
"customFields": {},
"createdAt": "2026-10-01T08:30:00Z",
"version": 1,
"changedBy": {
"type": "USER",
"userId": "00000000-0000-0000-0000-000000000001"
},
"invitationId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e05",
"metadata": {
"campaignId": "C-42",
"dealId": "D-123"
}
}
}colleague.calendar_connected
The team member now has a connected calendar whose busy times Schedulinq reads:
calendarConnected changed to true.
Example payload
{
"type": "colleague.calendar_connected",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"status": "ACTIVE",
"firstName": "Sanne",
"lastName": "de Vries",
"name": "Sanne de Vries",
"email": "sanne@installatiebedrijf-jansen.example",
"admin": true,
"schedulable": true,
"calendarConnected": true,
"customFields": {},
"createdAt": "2026-10-01T08:30:00Z",
"version": 1,
"changedBy": {
"type": "USER",
"userId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
}
}
}colleague.calendar_disconnected
The team member no longer has such a calendar. When Schedulinq lost access by itself,
changedBy is SYSTEM; that is noticed within a few hours.
Example payload
{
"type": "colleague.calendar_disconnected",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03",
"status": "ACTIVE",
"firstName": "Sanne",
"lastName": "de Vries",
"name": "Sanne de Vries",
"email": "sanne@installatiebedrijf-jansen.example",
"admin": true,
"schedulable": true,
"calendarConnected": false,
"customFields": {},
"createdAt": "2026-10-01T08:30:00Z",
"version": 2,
"changedBy": {
"type": "SYSTEM"
}
}
}Test message
ping
Sent only by Send test message on the endpoint's page. You cannot subscribe to it.
Example payload
{
"type": "ping",
"timestamp": "2026-10-01T08:30:00Z",
"data": {
"endpointId": "00000000-0000-0000-0000-000000000001"
}
}Who triggers what
| Where it happens | Events |
|---|---|
| Dashboard: create an appointment | appointment.created |
| Dashboard: edit an appointment | appointment.rescheduled when the start, end or team members change, otherwise appointment.updated |
| Dashboard: drag in the calendar | appointment.rescheduled |
| Dashboard: finish | appointment.completed |
| Dashboard: cancel | appointment.cancelled |
| Dashboard: Reschedule | appointment.cancelled and invitation.created |
| Dashboard: archive an appointment | appointment.deleted |
| Dashboard: set, change or clear a meeting link | appointment.updated |
| Dashboard: send an invitation | invitation.created |
| Dashboard: withdraw an invitation | invitation.cancelled |
| Dashboard: archive an invitation | invitation.deleted |
| Dashboard: delete a customer | appointment.cancelled per future appointment, invitation.cancelled per open invitation |
| Dashboard: merge two customers | appointment.updated per moved appointment |
| Dashboard: issue an invoice | appointment.completed per appointment it completes |
| API: send an invitation | invitation.created |
| Customer: book on the booking page | appointment.created |
| Customer: book an invitation | invitation.booked and appointment.created per appointment |
| Customer: cancel through their link | appointment.cancelled |
| Customer: reschedule through their link | appointment.rescheduled on the new appointment |
| A subscription's next invitation | invitation.created |
| An invitation's end date passes | invitation.expired |
| A cancel moves the later visits of a booking up | appointment.rescheduled per visit, changedBy SYSTEM |
| Dashboard: book several appointments | appointment.created per appointment |
| Teamleader | appointment.created; appointment.rescheduled when the time or team members change, otherwise appointment.updated; appointment.cancelled |
| Connected calendar: the meeting link is created or removed | appointment.updated |
| Connected calendar: the event is moved | appointment.rescheduled |
| Connected calendar: one of several team members is removed | appointment.rescheduled |
| Connected calendar: the last team member is removed, or the event is deleted | appointment.cancelled |
| Someone accepts a team invitation | colleague.activated |
| A team member's calendar is connected or lost | colleague.calendar_connected or colleague.calendar_disconnected |