Skip to main content
Edit a confirmed booking from the admin API, one concern at a time: resource and dates, then fields, then add-ons and sub-bookings. These /bookings/{booking_id}/edit/* actions are the per-booking counterpart of rewriting a whole order through Editing an Existing Order. For creating bookings, see Admin Booking Creation. For status changes, check-in, and cancellation, see Admin Booking Lifecycle. Customer self-service edits live in Customer Booking Lifecycle.

When to use which API

The admin edit actions sit next to the public edit-* routes but under edit/ so the two surfaces do not collide. They require the admin guard, update on bookings, and read on the booking’s resource.

Prerequisites

  • A valid Bearer token with admin access — see Authentication
  • Organization context (?o={organization_id})
  • A confirmed booking (not reserved) that belongs to the active organization

Shared behaviour

Every admin edit action:
  • Applies immediately. There is no preview / precalculate step and no cancel-edit-booking counterpart. A 200 means the change is persisted.
  • Ignores customer edit gates. Service settings such as canEditPeriod and canEditServiceOptions do not apply. Admins are not bound by the customer reschedule deadline.
  • Parks price corrections as an unlocked draft invoice on the order (tmp_invoice_id). The already issued invoice is left untouched. A later edit replaces the previous draft rather than merging into it. Issue the draft explicitly when you are ready — see Invoices.
  • Does not run the customer pay-now flow. The customer is not sent a payment link as part of the edit.
  • Rejects reserved bookings with HTTP 400 and cannot_modify_reserved_booking. Carts and still-reserved orders must go through checkout, not this API.
Each call is self-contained. Walk the steps below in UI order (resource → date → fields → add-ons / sub-bookings), or call only the action you need.

Step 1 — Resource, service, and dates

Request path documentation:
  • POST /api/v1/bookings/{booking_id}/edit/base-data
Availability is not enforced on this admin action. The booking can move onto a resource that is not wired to the booked service.

Resource scope

Duration when the service changes

Changing service_id does update the booking duration on the backend — but through end-interval snapping, not by copying min_duration blindly:
  • The requested start_date / end_date are resolved against the new service’s validated end intervals.
  • The last interval whose end is still <= the requested end wins. If none fit (for example a 60-minute window on a 90-minute fixed service), the first interval of the new service is used.
  • charged_duration follows the new service’s pricing rules: a fixed-duration service stays one charged interval; a flexible service uses ceil(window / calculation_interval).
  • Blocker padding (blocker_start_date / blocker_end_date) comes from the new service’s padding_before / padding_after, unless you send those params to override them.
To keep a window the new service would not validate, send override_duration: true or an explicit duration.

Service configuration and sequences

  • A single-service service_id map changes the booking’s service.
  • Adding a second service on a resource with create_booking_sequence turns the booking into a sequence parent: is_sequence is true, the parent carries no service_id, and one sequenced child is created per selected service. The parent span is snapped to the first child’s start and the last child’s end.
  • Dropping back to one service collapses the sequence: is_sequence is cleared, the remaining service is restored on the booking, and the sequenced children are removed.
  • Quota usage follows the children while the booking is a sequence, then returns to the parent when it collapses. Prepaid (currency) grants settle against the balance instead of charging twice.

Extras and sub-bookings on a base-data edit

edit/base-data does not take an add-on or sub-booking payload. Existing extras follow the booking:
  • Date change, same service — extras are re-attached on the new slot. Combined placeholders that only exist on the rebuilt booking are ignored instead of failing the request.
  • Resource change, same service — leftover extra rows stay on the original booking as-is.
  • Service change — extras belong to the old service. Leftover sub-bookings are canceled so the correction draft can refund them.
To add, change, or remove extras after a base-data edit, call edit/service-options (step 3). The response is the refreshed Booking. Pass include (for example resource.parent) to sideload the switched-to resource.

Step 2 — Fields

Customer, participants, and custom fields are separate actions. They do not go through edit/base-data.

Reassign the customer

  • When the booking is not personalized (its customer matches the order customer), the change also updates the order customer.
  • When the booking is personalized, only this booking is reassigned.
  • Sub-bookings and sequenced children adopt the new customer as well.

Sync participants

This is a sync of the participants you send: matching emails are updated in place. Participants missing from the payload are kept — the action never deletes attendees.

Custom fields

Booking custom fields are not on /edit/*. Load the standalone admin form, collect values, then apply them:
The apply endpoint persists the submitted custom_entry_map values, deletes entries that were cleared to "" / null, and returns the booking plus a rebuilt meta.form_default. Payload shape and persistence rules are in Admin UI Forms. You can also PATCH /api/v1/bookings/{booking_id} with custom_entry_map if you already know the field UUIDs.

Step 3 — Add-ons and sub-bookings

Request path documentation:
  • POST /api/v1/bookings/{booking_id}/edit/service-options
Unlike the customer edit-service-options action, an admin may attach:
  • an add-on the organization owns that is not wired to the booked service
  • a sub-resource / sub-service pair that is not registered as a sub-resource of the booked service
Items that belong to another organization are ignored (the request still returns 200; those rows are not written). Quantity 0 on an add-on removes it in admin org context. That override still wins when always_apply is true — the API does not floor an admin 0 back to min_quantity. Customer checkout is different: omitted or zero quantity is floored to min_quantity when always_apply is true, and skipped when always_apply is false. See Add-On. The replacement set is applied immediately and a correction draft is parked on the order when the issued invoice would otherwise change. For the checkout-time add_ons_by_service / sub_bookings_by_service grouping (quantity > 1 at create time), see Add-Ons and Sub-Bookings. This admin action takes a flat list for the one booking you are editing.

Whole-order alternative

When the change is not “this one booking” but the order as a whole — extra bookings, standalone add-ons, booking passes, or removals — read the order back as a config and post the complete desired state:
A booking entry with booking_id updates that booking; an entry without one is added; an omitted booking is soft-deleted. Sending only the bookings you changed will remove every booking you left out. That flow, including invoice regeneration, serialization, and notify_customer rules, is documented in Editing an Existing Order.
POST /api/v1/orders/calculate always prices the config as a new order. It ignores order_id. After an edit, read meta.calculation from the from-config response, or rely on the draft correction parked by the per-booking /edit/* actions.

Common errors