Skip to content

Migrate from V1 to V2

V1 retires on January 15, 2027

The legacy V1 routes outside /api/v1/management/ are removed on November 30, 2026. Migrate before these dates.

Use this guide to move a REST integration from V1 to V2. It lists every route, field, filter and ID that changes, and the order in which to check them. It does not cover webhook payloads or MCP: see Webhooks and MCP.

Before you switch

Moving to V2 requires changes to URLs, field names and stored ID mappings. Start with services and teachers: some IDs keep the same field name but refer to different records, so requests succeed and return the wrong data.

What does not change: the base URL, authentication headers, tenant headers, the pagination envelope, rate limits and the error shape. See Getting started and Pagination and filtering.

Confirm that V2 responds in your environment before you switch:

curl -X GET \
  -H 'X-Api-Key: YOUR_API_KEY' \
  -H 'X-Client-ID: YOUR_CLIENT_ID' \
  -H 'X-Company-ID: YOUR_COMPANY_ID' \
  'https://YOUR_ENV_DOMAIN/api/v2/monitoring/health/ping/'

Replace YOUR_ENV_DOMAIN with public.staging.bsport.io (staging) or public.production.bsport.io (production). See Environments.

The comparison in this guide uses the V1 schema and the V2 schema.

Routes

Use /api/v2/management/ for V2. Three paths also change. Read the table from left to right to find the V2 route and what to update.

V1 route V2 route Migration action
/management/passes/ /management/class-passes/ Update list and detail URLs and generated resource names.
/management/client-passes/ /management/client-class-passes/ Update list and detail URLs and the enable and disable action URLs.
/monitoring/health/anon_ping/ /monitoring/health/anon-ping/ Update anonymous health-check URLs if you use them.

V2 has no /passes/ or /client-passes/ aliases.

The legacy routes under /api/v1/ have no direct V2 copies. They are removed on November 30, 2026. Read the table to find the Management endpoint that replaces each one.

Legacy V1 route Replacement in V2
/bookings/ /management/bookings/
/members/ /management/clients/
/contracts/ /management/subscriptions/
/billing_plans/ /management/client-subscriptions/
/payment_packs/ /management/client-class-passes/
/consumer_payment_packs/ /management/client-class-passes/

Field names also change between the legacy and Management responses. Check the V2 reference for each resource and use the tables below for the renamed fields.

Service and teacher IDs

Class services

On classes, service_id changes from an internal scheduling record ID to a public service ID, returned by /api/v2/management/services/. The service_id filter uses the new ID too.

Reload services and classes for each company to rebuild your mappings. Reusing a V1 number can select an unrelated V2 service. Legacy classes without a linked service return service_id: null.

Teachers

Read the table by surface. Each row shows what the ID meant in V1, what it means in V2, and what to change.

Surface V1 V2 Required adjustment
Class teacher_id and filter Effective teacher: the substitute when assigned, otherwise the main teacher. Already a company-specific teacher ID. Main teacher. substitute_teacher_id is separate. Use current_teacher_id to filter the teacher actually running a class. For display, prefer the substitute when present.
Appointment teacher_id Platform-wide coach ID. Company-specific teacher ID returned by /teachers/. Rebuild cached teacher references. Do not reuse the V1 number.
Booking teacher_id, substitute_teacher_id and teacher_ids filter Coach IDs. Company-specific teacher IDs returned by /teachers/. Update joins and filters to the V2 Teachers ID space.

Teacher IDs are company-specific: the same person can have different IDs in two companies. Allow null main-teacher IDs on legacy classes and bookings. IDs returned by the Teachers endpoint itself are unchanged.

Classes, appointments and bookings

Classes and appointments

Read the table by resource. Each row is one rename.

Resource V1 V2
Classes and appointments is_cancelled is_canceled
Classes and appointments cancellable_until cancelable_until
Appointments cancelled_at canceled_at
Appointments, response and list filter service_id appointment_service_id
Appointments, request body and response slot_id appointment_slot_id
Classes is_bookable_by_members is_bookable_by_clients
Classes current_waiting_list_size current_waitlist_size
Classes max_waiting_list_size max_waitlist_size

On appointments, appointment_service_id keeps the old service_id value and appointment_slot_id keeps the old slot_id value. Classes keep the name service_id, with the different ID meaning described above. V2 also adds level_id and substitute_teacher_id to classes.

Bookings

Read the table by surface. Each row is one change.

Surface V1 V2
Create payload, response and list filter client_pass_id client_class_pass_id
Create payload session_ids alias Use class_ids. It is required and non-empty.
Response session_id, session_start_at aliases Use class_id, class_start_at.
Response location_id venue_id
List filters session_id, session_ids class_id, class_ids
List filters session_started_after_date, session_started_before_date, taking a date class_start_after, class_start_before, taking a timestamp. A plain date is read as midnight in the company's time zone.
List filters has_session_ended, is_workshop_session has_class_ended, is_workshop_class
List filter location_ids venue_ids
List filter meta_activity_ids service_ids
Ordering class_start_date, session_start_date class_start_at

The activity_ids filter is removed. To use service_ids, first resolve the public services that correspond to your old activity IDs.

Rename expand[] paths as well, for example client_pass_id -> client_class_pass_id, and parse expanded objects with their V2 field names.

Clients, notes, tags and teachers

Client creation

Read the table from left to right to convert a V1 creation body.

V1 input V2 input
membership client_reference
membership.membership_id client_reference.client_reference_number
phone.phone_number Top-level phone_number string
address.zipcode address.zip_code

Creation requires client_reference.joined_date. Marketing-consent fields also belong inside client_reference.

A V2 creation request looks like this (synthetic data):

{
  "firstname": "Alex",
  "lastname": "Example",
  "client_reference": {
    "joined_date": "2026-10-06",
    "client_reference_number": "external-123",
    "is_email_accepted": false
  },
  "phone_number": "+33612345678",
  "address": {"zip_code": "75001"}
}

Client responses and nested notes

Read the table from left to right. Each row is one field.

V1 V2
membership_id client_reference_number
default_billing_establishment_id default_billing_venue_id
default_establishment_billing_location_id Removed. No like-for-like field is published.
client_account Removed. client_account_balance is the client's balance.
general_terms_and_conditions_accepted_date general_terms_and_conditions_accepted_at
general_terms_of_use_accepted_date general_terms_of_use_accepted_at
notes[].member notes[].client_id
notes[].date notes[].creation_date
notes[].editable notes[].is_editable
notes[].highlighted notes[].is_highlighted
Client ordering credit_account_balance client_account_balance

Both date fields, birth_date and joined_date, use YYYY-MM-DD. The V2 schema declares joined_date non-nullable: check legacy records when you update generated client types.

V2 adds PATCH /clients/{id}/. Omitted fields stay unchanged. An address object updates only its supplied fields, and address: null leaves the address intact. Email can be set at creation but is rejected in PATCH. V1 has no PUT operation, so there is nothing to migrate.

Other field changes and validation

Read the table by resource.

Resource V1 V2
Client-note response and ordering created_on creation_date
Tags and tag groups date_created created_at
Teachers first_name, last_name, phone firstname, lastname, phone_number
Tag creation Optional, nullable free-form color Required lowercase #rrggbb string. Null is not accepted.

Tag and untag actions and tag-group references require positive IDs from the authenticated company.

Class passes and client class passes

Class-pass response mapping

Read the table from left to right. Each row maps a V1 /passes/ field to its V2 /class-passes/ field.

V1 field V2 field
meta_activity_ids service_ids
establishment_ids venue_ids
applies_for_payroll is_used_for_payroll
grants_door_access is_door_access_granted
no_show_penalty_active is_no_show_penalty_active
pass_category_id class_pass_category_id
should_allow_guest_pass is_guest_allowed
tags_on_client_pass_creation on_purchase_tag_ids
theoretical_margin_value estimated_revenue_amount
linked_private_pass_id linked_appointment_pass_id
validity_daterange validity_period
bookkeeping_account_id Removed. No replacement in this response.

Inside validity_period, rename lower to start_date, upper to end_date, and empty to is_empty. Keep reading bounds to determine whether the end date is included. The object can be null.

Rename the list filter category_id to class_pass_category_id. The rename meta_activity_ids -> service_ids preserves the existing IDs: both refer to the same record.

Client class passes

Read the table from left to right.

V1 V2
pass_id class_pass_id
pass_template_instance_id class_pass_template_instance_id
master_client_pass_id master_client_class_pass_id
linked_private_client_pass_id linked_client_appointment_pass_id
List filters pass_id, pass_ids class_pass_id, class_pass_ids
List filter client_id Use client_ids, including for a single client.
Enable and disable query parameter spread_to_shared_passes should_spread_to_shared_passes

V2 declares purchased_date and is_disabled read-only. Use the enable and disable actions to change state: PATCH is not supported. Rename expansions too: expand[]=pass_id becomes expand[]=class_pass_id.

Subscriptions

V2 keeps /subscriptions/ and /client-subscriptions/ for reads. New subscription writes and pause operations are not part of V2. Read the table by surface.

Surface V1 V2
Subscription response contract_template_id subscription_template_id
Subscription response contract_terms subscription_terms
Subscription response contract_terms_pdf_link subscription_terms_pdf_link
Subscription response tags_on_first_billing on_first_billing_tag_ids
Both subscription resources, response and benefit_type filter pass class_pass

Replace pass with class_pass in response parsing and in benefit_type filters. appointment_pass and pack are unchanged.

New in V2

The V2 objects with no V1 equivalent need no migration. Read Other objects added in V2 for what each one is, and V2 workflows and field conventions for how to use them.

When you adopt invoices, check the amount units: every amount, nested line items included, is a decimal string in major currency units. For communications, a retry can send duplicates and processed does not mean delivered.

Webhooks and MCP

This guide covers the REST API. MCP and webhooks follow different rules.

MCP. MCP tools use the V2 contract, with V2 field names and IDs. You do not migrate anything on the MCP side, but the tool list is the V2 one. See the MCP overview.

Webhooks. The payload version belongs to the registration:

  • An existing registration keeps delivering V1 payloads and historical event names such as offer-update. Its data keeps V1 field names and IDs, for example booking session_id and class service_id. It does not change on its own. This includes registrations that were created through a V2 route before payload versioning was introduced.
  • A new registration is always created through /api/v2/management/webhook-registrations/. Creating one through V1 returns 410 with code WHR-002. It delivers V2 payloads with canonical event names such as class.updated, and its data matches the V2 response of the same resource, for example booking class_id.

To move an existing integration to V2 payloads, create a new registration for the V2 event, then disable the old one once your receiver handles the new shape. Use a separate receiver URL for each version while both exist, because a registration cannot be switched from V1 to V2. See Webhook event payloads and existing V1 registrations.

Migration checklist

Work through the steps in order.

  1. List the routes, fields, filters, ordering values and expansions your integration uses, including scheduled exports.
  2. Update them using the tables above. Regenerate your API client from the V2 schema if you use one, and check required fields, nullable IDs and each operation's error responses.
  3. Reload service and teacher mappings per company. Test classes with substitutes and legacy records with null references.
  4. Compare reads on test data. Check filter results: an obsolete parameter can leave a query broader than intended.
  5. Test writes outside production, including invalid IDs, tag colors, omitted fields and explicit nulls. V1 and V2 share data, so sending the same write to both can create duplicates.
  6. Plan webhooks separately: existing registrations keep V1 payloads, and new ones use V2. Create the V2 registration next to the V1 one, compare the deliveries, then disable the V1 registration.
  7. Switch one flow at a time. Keep the V1 mappings available for rollback. Returning to V1 does not undo writes already made through V2.