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:
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:
Unexpanded related resources¶
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.