Skip to content

Webhook event payloads

Create and update events use the same unexpanded resource representation as the corresponding retrieve endpoint. Relation events and true delete events use the smaller shapes documented below.

Payload versions

The payload version belongs to the registration, not to the event.

Registration Event names data follows
New, created through /api/v2/management/webhook-registrations/ Canonical, for example class.updated The V2 retrieve endpoint
Existing, created before payload versioning Historical, for example offer-update The V1 retrieve endpoint, unchanged

V1 registration creation is retired: POST on /api/v1/management/webhook-registrations/ returns 410 with code WHR-002. Existing registrations keep delivering V1 payloads and do not change on their own. This includes registrations that were created through a V2 route before payload versioning was introduced. To receive V2 payloads for an event you already follow, create a new registration for the V2 event, then disable the old one when your receiver handles the new shape. While both exist, send them to separate receiver URLs so each endpoint parses a single version. See existing V1 registrations.

Event names and historical terms

New registrations use canonical event names that match the REST resource: class.updated carries a Class, client.updated carries a Client, teacher.updated carries a Teacher. Existing V1 registrations keep inherited Back Office names (offer-update, member-update, associated_coach-update, tag_member-create). These are not REST resource names: the API has no /offers/, /members/ or /associated_coaches/ resource. Use the data model name when you name the object in your application. For example, offer-update carries a Class resource, not a separate Offer model. The historical V1 catalogue lists those names.

Resource mapping

Event family data resource Corresponding endpoint
appointment.created, appointment.updated, appointment.canceled Appointment GET /api/v2/management/appointments/{id}/
service.created, service.updated Service GET /api/v2/management/services/{id}/
venue.created, venue.updated, venue.deleted Venue GET /api/v2/management/venues/{id}/
class.created, class.updated Class GET /api/v2/management/classes/{id}/
booking.created, booking.updated, booking.deleted Booking GET /api/v2/management/bookings/{id}/
client.created, client.updated Client GET /api/v2/management/clients/{id}/
client.tag_added, client.tag_removed Client-tag relation Use data.client_id and data.tag_id directly
teacher.created, teacher.updated, teacher.deleted Teacher GET /api/v2/management/teachers/{id}/
invoice.created, invoice.updated Invoice GET /api/v2/management/invoices/{id}/
class_pass.created, class_pass.updated Class pass GET /api/v2/management/class-passes/{id}/
client_class_pass.created, client_class_pass.updated Client class pass GET /api/v2/management/client-class-passes/{id}/
product.created, product.updated Product GET /api/v2/management/products/{id}/

The webhook event catalogue shows the complete schema and example for every event.

Do not mix V1 and V2 identifiers

A V1 registration delivers V1 field names and IDs. Booking session_id is class_id in V2, and a class service_id or teacher_id from a V1 payload does not identify the same record as the field of the same name in V2. When you run both versions, do not store or compare IDs across them. If you stay on a V1 registration but work with V2, use the event as a trigger and fetch the resource from V2 by ID.

V1 payloads compared with V2

This table is for integrations that still receive V1 payloads from older registrations. Read it by resource. The left column is what a V1 registration delivers. The right column is what a V2 registration delivers, which is the same as the V2 REST response.

Resource V1 registration data V2 registration data
Booking session_id, session_start_at, client_pass_id, location_id class_id, class_start_at, client_class_pass_id, venue_id
Class is_cancelled, cancellable_until, is_bookable_by_members is_canceled, cancelable_until, is_bookable_by_clients
Class current_waiting_list_size, max_waiting_list_size current_waitlist_size, max_waitlist_size
Class service_id: internal scheduling record service_id: the service returned by /services/
Class teacher_id: the teacher running the class (the substitute when assigned) teacher_id: the main teacher. substitute_teacher_id is separate
Class No level_id level_id
Teacher first_name, last_name, phone firstname, lastname, phone_number
Client membership_id, default_billing_establishment_id, general_terms_*_accepted_date client_reference_number, default_billing_venue_id, general_terms_*_accepted_at

The migration guide lists every other renamed field.

Current-state events

Create and update events contain the resource's current state when the delivery is prepared. A consumer can usually process data directly. Fetch the retrieve endpoint when the integration needs a later state or when it deliberately expands related resources.

Invoice identifiers are UUID strings. invoice.updated also reports invoice-item creation, updates, and deletion; reassignment can notify both the old and new invoice. The payload includes the current grouped line_items, not the historical row mutation. Multiple row changes can produce multiple deliveries for the same invoice. Renaming what a line sells, such as a product, also emits invoice.updated. See Invoice events for the trigger boundaries; there is no invoice.deleted or invoice payment-transition event.

appointment.canceled records entry into a canceled status, but its payload is also read at delivery time and can reflect a later reactivation. An appointment without an addressable company client is omitted from V2 lists, returns 404 on retrieval, and is not delivered.

Class pass, client class pass, and product updates also cover changes to related data, such as pass access rules, shared-pass bookings and dates, or product stock and inherited tax. An update is a refresh notification, not a field-level difference: related changes can produce several notifications with identical data. Replace your stored resource state rather than adding credits or stock for every notification.

These families expose only created and updated. They do not signal that a payment succeeded or that a pass expired merely because its end date passed.

A client class pass without a client belonging to the class pass owner's company is not available through V2: lists omit it, detail requests return 404, and current-state webhook delivery is skipped. The underlying pass is not deleted or disabled by this rule.

def handle_class_update(payload):
    class_data = payload["data"]
    synchronize_class(
        class_id=class_data["id"],
        starts_at=class_data["starts_at"],
        capacity=class_data["max_capacity"],
    )

Delete and catalogue-removal events

True resource deletions cannot be serialized after the database row has disappeared. booking.deleted and teacher.deleted therefore contain only:

{
  "data": {
    "id": 123
  }
}

venue.deleted uses the same ID-only shape when a venue association is removed from the subscribing company's catalogue. The venue can still exist for another company. Reassigning an association emits removal for its previous company and creation for its new company.

client.tag_added and client.tag_removed identify the changed relation directly:

{
  "data": {
    "client_id": 123,
    "tag_id": 456
  }
}

In current-state resource payloads, expandable fields such as teacher_id, venue_id or client_id are identifiers. Use those identifiers with the relevant Public API endpoint when the related object is required. Relation and ID-only payloads have no expandable fields.

Legacy payloads

These rules apply only to registrations created through the Public API. Webhooks configured in Back Office Settings > Webhook use older payloads and are being retired. See Migrating from Back Office webhooks for the transition path. Do not carry legacy field names into a new Public API integration.