On this page Getting started Keys & security Conventions Errors
Resources
Guest passes Bookings Services, items, rooms Staff & hours Time off
Guides
Typical flows Testing your integration What changed

Partner API

Connect a hotel PMS, a planning tool or your own software to a salon on massageplanner.app: create guest passes at check-in, book on behalf of guests, keep the catalog and the team's hours in sync.

Base URLhttps://<salon>.massageplanner.app/api/v1
AuthenticationBearer API key, issued by the salon
FormatJSON in and out, UTF-8
TimeISO 8601 with offset; prices in cents

Getting started

  1. Get a key from the salon. The salon owner creates it in their dashboard under API keys, ticks the permissions (scopes) your system needs and hands you the key once. It starts with mp_live_.
  2. Make your first call. Read the salon's massage menu — it needs the catalog:read scope.
    curl https://weedoo.massageplanner.app/api/v1/services \
      -H "Authorization: Bearer mp_live_YOUR_KEY"
    The same call in JavaScript and Python:
    // Node.js 18+ (fetch is built in)
    const res = await fetch('https://weedoo.massageplanner.app/api/v1/services', {
      headers: { Authorization: 'Bearer ' + process.env.MP_API_KEY },
    });
    const { services } = await res.json();
    # Python 3, requests
    import os, requests
    r = requests.get('https://weedoo.massageplanner.app/api/v1/services',
                     headers={'Authorization': 'Bearer ' + os.environ['MP_API_KEY']})
    services = r.json()['services']
  3. Read the answer. Every response is a JSON object; lists are wrapped in a named key.
    {
      "services": [
        {
          "extern_id": "classic-60",
          "name": "Classic Massage",
          "description": "Full-body relaxation with flowing Swedish strokes",
          "details": null,
          "duration_min": 60,
          "price_cents": 6500,
          "active": true,
          "sort_order": 0,
          "service_required_items": []
        }
      ]
    }
  4. Then follow a flow. Hotel check-in → guest pass; concierge booking → bookings; central catalog or planning system → catalog and staff. Typical flows shows the calls in order.
Replace weedoo with the salon's own subdomain. Each salon is a separate system with its own keys; a key never works on another salon.

Keys & security

Every request carries the key in the Authorization header. Nothing else is needed — no session, no signing.

Authorization: Bearer mp_live_…

Scopes

A key only does what the salon ticked when creating it. Ask for the smallest set that covers your integration; a call outside the key's scopes answers 403 insufficient_scope and names the scope it needed.

ScopeUnlocks
guest-passes:readLook up a guest pass
guest-passes:writeCreate, change, revoke a pass, resend its email
bookings:readList bookings in a date range
bookings:writeCreate, cancel, approve bookings
catalog:readRead services, items, rooms
catalog:writeCreate and change services, items, rooms and their links
staff:readRead the team, their skills, shifts and time off
staff:writeCreate and change team members, skills, shifts and time off

How the key is protected on our side

What we ask of you

All calls are live. There is no sandbox: a pass really emails the guest, a booking really takes the slot. See Testing your integration for how to try things safely.

Conventions

Errors

Errors are JSON with an error code you can switch on. Validation errors add a detail that names the field:

HTTP 400
{
  "error": "invalid_request",
  "detail": {
    "formErrors": [],
    "fieldErrors": { "guest_email": ["Invalid email"] }
  }
}
StatuserrorMeaning · what to do
400invalid_requestThe body or a parameter is wrong; detail says which. A referenced extern_id that does not exist (service, staff, room, item) also lands here, with the id in detail.
400reservation_ref_requiredThe salon accepts bookings with a reservation number only. Send reservation_ref, or book through a guest pass.
400guest_pass_outside_stayThe appointment is not between the pass's valid_from and valid_until.
400guest_pass_party_mismatchA couple pass books two people, any other pass one; expected tells you which.
401invalid_api_keyHeader missing, key malformed or revoked. Check the header, then ask the salon whether the key still exists.
403insufficient_scopeThe key lacks the scope in required. The salon adds it to the key (or creates a new key).
403plan_featureThe salon's plan does not include the partner API. Every current plan (Studio, Enterprise) does; this answers only for a legacy plan.
403guest_pass_invalidThe pass in guest_pass_extern_id is revoked, expired or used up.
404not_foundNo resource with that extern_id (or booking number). A cancel or approve on a booking that is not in the right status answers this too.
409extern_id_conflictYou already created a resource with this extern_id. Safe to ignore on a retry.
409slot_unavailableNobody qualified is free, no suitable room, the time is in the past or outside opening hours. Offer another time.
409guest_pass_free_exceededThe pass has fewer free massages left than the booking needs; remaining says how many.
400 / 409overlapping_shiftsShifts of one person overlap each other (400) or an existing shift (409). Touching boundaries such as 12:00/12:00 are fine.
409shyfter_managedThis person's hours come from Shyfter; change them there.
429rate_limitedOver the allowance: 300 requests a minute per calling address, 600 a minute per key (scope says which). Wait for Retry-After seconds and retry; spread bulk syncs out.
500internalSomething broke on our side and the salon's operator has been alerted. Retry after a moment; nothing half-done is left behind.

Guest passes guest-passes:readguest-passes:write

A guest pass is a personal booking link for one hotel guest (or couple). On that link the guest's details are pre-filled, the reservation number is locked in, and the salon knows who is booking. The link works from the moment you create it — guests can book before they arrive — until valid_until (checkout). Appointments themselves can only fall between valid_from (check-in) and valid_until.

POST/guest-passescreate a pass; emails the guest their link + QR code
GET/guest-passes/{extern_id}read a pass, including how many free massages are used
PATCH/guest-passes/{extern_id}change any field, e.g. extend valid_until on late checkout
POST/guest-passes/{extern_id}/revokekill the link, e.g. early checkout
POST/guest-passes/{extern_id}/emailsend the guest their link + QR again

Fields

FieldTypeMeaning
extern_idrequiredstringYour id for this pass, e.g. the check-in or reservation id
guest_namerequiredstringShown on the booking page and to the therapist
guest_emailrequiredemailReceives the link + QR and later the booking confirmations
guest_phonestringOptional
reservation_refstringYour reservation number; copied onto every booking made with the pass
valid_fromrequiredtimestampCheck-in: first moment an appointment may start
valid_untilrequiredtimestampCheckout: the link stops working and no appointment may start after it
included_in_packagebooleanDefault false. Marks the stay as a package; see below
hide_pricingbooleanDefault false. No prices at all on the guest's page
is_couplebooleanDefault false. Opens the page in couple mode; the pass then books two people
free_selectioninteger 0–20Default 0. Massages the guest may book for free through the link
send_emailbooleanCreate only. false = do not email the guest; your system delivers booking_url itself

Example: create a pass at check-in

POST /api/v1/guest-passes
Authorization: Bearer mp_live_…
Content-Type: application/json

{
  "extern_id": "pms-checkin-49821",
  "guest_name": "Nesrin Yilmaz",
  "guest_email": "nesrin@example.com",
  "guest_phone": "+32 470 00 00 00",
  "reservation_ref": "HTL-2026-777",
  "valid_from": "2026-09-05T00:00:00Z",
  "valid_until": "2026-09-08T23:59:59Z"
}

Response 201. emailed tells you whether the guest got the email; booking_url is the link to show or print:

{
  "guest_pass": {
    "extern_id": "pms-checkin-49821",
    "guest_name": "Nesrin Yilmaz",
    "guest_email": "nesrin@example.com",
    "guest_phone": "+32 470 00 00 00",
    "reservation_ref": "HTL-2026-777",
    "valid_from": "2026-09-05T00:00:00+00:00",
    "valid_until": "2026-09-08T23:59:59+00:00",
    "revoked_at": null,
    "included_in_package": false,
    "hide_pricing": false,
    "is_couple": false,
    "free_selection": 0,
    "free_used": 0,
    "used_at": null,
    "created_at": "2026-09-05T13:02:11.481+00:00",
    "booking_url": "https://weedoo.massageplanner.app/g/60778b98c1…"
  },
  "emailed": true
}

The QR code of the link is also available as an image: GET https://<salon>.massageplanner.app/api/public/pass/{token}/qr.png, where {token} is the last part of booking_url. Handy for a printed welcome card.

Example: late checkout, then early departure

PATCH /api/v1/guest-passes/pms-checkin-49821
{ "valid_until": "2026-09-09T14:00:00Z" }

POST /api/v1/guest-passes/pms-checkin-49821/revoke
→ { "revoked": true }

Package passes: massages included in the stay

When the guest's package includes massages, add these fields. free_selection is how many massages the guest may book free of charge through the link: the booking page tells them how many are left and shows prices as "included". Each booked massage spends one; when the last one is booked the link stops working and used_at is set. Cancelling a booking gives the ticket back. hide_pricing hides prices altogether; is_couple opens the page in couple mode and makes the pass book two people at a time.

POST /api/v1/guest-passes
{
  "extern_id": "pms-checkin-49822",
  "guest_name": "Lotte & Jan Peeters",
  "guest_email": "lotte@example.com",
  "reservation_ref": "HTL-2026-778",
  "valid_from": "2026-09-05T00:00:00Z",
  "valid_until": "2026-09-08T23:59:59Z",
  "included_in_package": true,
  "hide_pricing": true,
  "is_couple": true,
  "free_selection": 2
}

Two rules follow from the pass, on the guest's link and on bookings you make with guest_pass_extern_id: a booking that needs more free massages than are left answers 409 guest_pass_free_exceeded with remaining; a couple pass must book two people and any other pass one, otherwise 400 guest_pass_party_mismatch with expected.

Lifecycle. A pass is usable while revoked_at and used_at are null and now is before valid_until. Reading it back with GET shows free_used, so your PMS can display "1 of 2 massages booked".

Bookings bookings:readbookings:write

A booking is one appointment for one or two people (a couple in one room). You book on behalf of a guest — for example from the reception desk — and the guest gets the same confirmation email as when booking online.

POST/bookings1 member = solo, 2 members = couple
GET/bookings?from=…&to=…bookings starting in the range (default: yesterday to two weeks ahead)
POST/bookings/{ref}/cancelcancel; free tickets go back to the pass
POST/bookings/{ref}/approveconfirm a booking that is pending

{ref} is your extern_id, or the numeric booking number the salon sees. A booking is pending, confirmed or cancelled. It comes back pending when the salon must first move portable equipment into the room; it holds its slot either way, and the salon (or you, with approve) confirms it.

Fields

FieldTypeMeaning
starts_atrequiredtimestampStart of the appointment; must be a bookable time (see below)
membersrequiredarray of 1–2One entry per person
members[].service_extern_idrequiredstringWhich massage
members[].customerrequiredobjectname, email (both required), phone
members[].staff_extern_idstringPreferred therapist; omitted, any qualified therapist who is free
members[].staff_gender"female" · "male"The guest wants a woman or a man; any qualified therapist of that gender. Ignored when staff_extern_id is given
members[].extern_idstringYour id for this person's line, if you track them separately
extern_idstringYour id for the whole booking; lets you cancel by it later
guest_pass_extern_idstringBook on behalf of a pass you created: stay window, party size and free tickets apply; the reservation number is taken from the pass
reservation_refstringReservation number, when not booking through a pass
room_extern_idstringA specific room; omitted, any suitable room
notesstringAnything the therapist should know (health, wishes); shown in the salon's agenda

Example: one person, any therapist

POST /api/v1/bookings
{
  "extern_id": "pms-book-1201",
  "starts_at": "2026-09-06T13:00:00Z",
  "reservation_ref": "HTL-2026-777",
  "members": [
    {
      "service_extern_id": "classic-60",
      "customer": { "name": "Nesrin Yilmaz", "email": "nesrin@example.com" }
    }
  ]
}

Response 201:

{
  "booking": {
    "booking_no": 42,
    "status": "confirmed",
    "starts_at": "2026-09-06T13:00:00+00:00",
    "ends_at": "2026-09-06T14:00:00+00:00",
    "manage_token": "1d02f5…",
    "group_id": "afc2…"
  }
}

manage_token is the guest's own cancel link: https://<salon>.massageplanner.app/b/{manage_token}. It is in their confirmation email; you rarely need it.

Example: a couple, through their package pass, with a preferred therapist

POST /api/v1/bookings
{
  "extern_id": "pms-book-1202",
  "starts_at": "2026-09-06T15:00:00Z",
  "guest_pass_extern_id": "pms-checkin-49822",
  "notes": "Jan: lower back sensitive",
  "members": [
    {
      "service_extern_id": "hotstone-90",
      "staff_extern_id": "therapist-ayla",
      "customer": { "name": "Lotte Peeters", "email": "lotte@example.com" }
    },
    {
      "service_extern_id": "classic-60",
      "customer": { "name": "Jan Peeters", "email": "jan@example.com" }
    }
  ]
}

Both people are placed in one room at the same start time; each may have a different massage and therapist. The two free tickets of the pass are spent, so the guests pay nothing.

Which times can be booked?

A time is bookable when it lies on the salon's slot grid, is at least the salon's lead time away, falls inside a shift of a qualified therapist (or the one you named), a room with the right equipment is free, and no time off blocks it. If not, the answer is 409 slot_unavailable and nothing is stored. There is no availability call in the partner API yet: when your system needs to show free times before booking, tell the salon and we will add one.

Example: list, cancel, approve

GET /api/v1/bookings?from=2026-09-06T00:00:00Z&to=2026-09-07T00:00:00Z

{
  "bookings": [
    {
      "booking_no": 42,
      "extern_id": "pms-book-1201",
      "reservation_ref": "HTL-2026-777",
      "status": "confirmed",
      "starts_at": "2026-09-06T13:00:00+00:00",
      "ends_at": "2026-09-06T14:00:00+00:00",
      "rooms": { "name": "Room 1", "extern_id": "room-1" },
      "guest_pass_extern_id": null,
      "included_package": false,
      "persons": 1,
      "total_cents": 6500,
      "bookings": [
        {
          "guest_name": "Nesrin Yilmaz",
          "status": "confirmed",
          "extern_id": null,
          "services": { "name": "Classic Massage", "extern_id": "classic-60", "price_cents": 6500 }
        }
      ]
    }
  ]
}

POST /api/v1/bookings/pms-book-1201/cancel
→ { "cancelled": true }

POST /api/v1/bookings/42/approve
→ { "approved": true }

For package bookings included_package is true and every price reads 0: there is nothing to settle. guest_pass_extern_id tells you which stay it belongs to.

Waiting list

When a day has no free time, guests can leave their details on the booking page; the moment a matching time opens (a cancellation, an added shift) everyone waiting for that day is emailed and the first booking wins. Read-only for now, so a concierge screen can see who is hoping for a time:

GET/waitlistopen entries from today on, oldest firstbookings:read
GET /api/v1/waitlist

{
  "waitlist": [
    {
      "date": "2026-09-14",
      "time_from": "14:00",
      "time_to": null,
      "services": [ { "extern_id": "classic-60", "name": "Classic Massage" } ],
      "staff_gender": "female",
      "guest_name": "Nesrin Yilmaz",
      "guest_email": "nesrin@example.com",
      "guest_phone": null,
      "notes": null,
      "status": "open",
      "notified_at": null,
      "created_at": "2026-09-12T09:41:07.113+00:00"
    }
  ]
}

status is open or notified (a time was emailed, not booked yet). A booking made for that email on that day closes the entry.

Services, items, rooms catalog:readcatalog:write

The catalog is what the booking page offers and what the engine needs to place a booking: services (massage types), items (equipment such as a hot-stone heater or a massage chair) and rooms. A service can require items; a room has fixed items and can forbid items. Portable items travel between rooms; that is what makes a booking pending until the salon moved it.

GET/services POST/services PATCH/services/{extern_id} DELETE/services/{extern_id}
PUT/services/{extern_id}/required-itemsreplaces the list of items the service needs
GET/items POST/items PATCH/items/{extern_id} DELETE/items/{extern_id}
GET/rooms POST/rooms PATCH/rooms/{extern_id} DELETE/rooms/{extern_id}
PUT/rooms/{extern_id}/fixed-itemsitems that are always in this room PUT/rooms/{extern_id}/restrictionsitems that may never be brought in

Service fields

FieldTypeMeaning
extern_idrequiredstringYour product code
namerequiredstringShown on the booking page
descriptionstringOne line under the name on the booking page; keep it around 90 characters
detailsstringLonger explanation; opens from an "i" beside the name. Line breaks are kept
duration_minrequiredinteger 5–600Length of the appointment
price_centsrequiredintegerDisplay price
activebooleanDefault true; false hides it from the booking page
sort_orderintegerPosition on the booking page, lowest first

Write the texts in English: the booking page shows them to guests in English, Dutch and French, translated automatically. The salon can correct a translation in its dashboard; your next change of the English text replaces the automatic translation of that text (and drops the salon's correction of it).

Item fields

FieldTypeMeaning
extern_idrequiredstring
namerequiredstring
portablerequiredbooleantrue = can be carried into any room that does not forbid it
portable_quantityintegerHow many portable units exist; bookings needing more at the same time are refused
activeboolean

Room fields

FieldTypeMeaning
extern_idrequiredstring
namerequiredstring
capacityrequired1 or 22 = a couple can be treated together here
activeboolean

Example: a hot-stone massage that needs a heater

POST /api/v1/items
{ "extern_id": "stone-heater", "name": "Hot Stone Heater", "portable": true, "portable_quantity": 1 }

POST /api/v1/services
{
  "extern_id": "hotstone-90",
  "name": "Hot Stone Massage",
  "description": "Warmed basalt stones melt deep muscle tension",
  "details": "Smooth, heated basalt stones are placed along the back and used in long, slow strokes.\n\nGood for stiff shoulders and cold days. Not suitable during pregnancy.",
  "duration_min": 90,
  "price_cents": 9500
}

PUT /api/v1/services/hotstone-90/required-items
[ { "item_extern_id": "stone-heater", "quantity": 1 } ]

Example: a couples room with a fixed shower, no aroma diffuser

POST /api/v1/rooms
{ "extern_id": "room-2", "name": "Room 2", "capacity": 2 }

PUT /api/v1/rooms/room-2/fixed-items
[ { "item_extern_id": "shower", "quantity": 1 } ]

PUT /api/v1/rooms/room-2/restrictions
[ "aroma-diffuser" ]

Example: change a price, hide a service, bring it back

PATCH /api/v1/services/hotstone-90
{ "price_cents": 9900 }

DELETE /api/v1/services/hotstone-90
→ { "deactivated": true }

PATCH /api/v1/services/hotstone-90
{ "active": true }

Staff & hours staff:readstaff:write

Team members, what they can do (skills = services), and when they work (shifts). Availability comes from shifts: no shift, no bookings. Shifts are dated — there is no weekly pattern — so a planning system sends the actual roster.

GET/staff POST/staff PATCH/staff/{extern_id} DELETE/staff/{extern_id}
PUT/staff/{extern_id}/skillsarray of service extern_ids, replaces the set
GET/staff/{extern_id}/shifts?from=…&to=…default: last week to 60 days ahead
POST/staff/{extern_id}/shiftsadd dated shifts (up to 500 per call)
DELETE/staff/{extern_id}/shifts?from=…&to=…remove the shifts you added in a range

Staff fields

FieldTypeMeaning
extern_idrequiredstringYour employee id
display_namerequiredstringShown to guests as the therapist's name
emailemailFor the personal calendar link and absence notices
gender"female" · "male"Guests may ask for a woman or a man; only staff marked here are offered under such a preference. Null = not set
activebooleanfalse = not bookable

Example: a new therapist with two skills

POST /api/v1/staff
{ "extern_id": "therapist-ayla", "display_name": "Ayla", "email": "ayla@example.com" }

PUT /api/v1/staff/therapist-ayla/skills
[ "classic-60", "hotstone-90" ]

Example: a week of shifts, including a split day

Send as many shifts as you like in one call. Several per day are fine as long as they do not overlap; a shift that overlaps one already stored answers 409 overlapping_shifts.

POST /api/v1/staff/therapist-ayla/shifts
[
  { "starts_at": "2026-09-14T08:00:00+02:00", "ends_at": "2026-09-14T12:00:00+02:00" },
  { "starts_at": "2026-09-14T14:00:00+02:00", "ends_at": "2026-09-14T18:00:00+02:00" },
  { "starts_at": "2026-09-15T09:00:00+02:00", "ends_at": "2026-09-15T17:00:00+02:00" }
]

→ 201
{
  "shifts": [
    { "id": "…", "starts_at": "2026-09-14T06:00:00+00:00", "ends_at": "2026-09-14T10:00:00+00:00", "source": "manual" },
    …
  ]
}

Example: replace next week's roster

The simplest sync: clear the range, then send the new shifts. Bookings already made in that week stay; the salon sees a warning in its agenda if a booking ends up outside a shift.

DELETE /api/v1/staff/therapist-ayla/shifts?from=2026-09-14T00:00:00Z&to=2026-09-21T00:00:00Z
→ { "removed": 5 }

POST /api/v1/staff/therapist-ayla/shifts
[ … next week … ]

GET /staff returns every team member with their skills and their shifts from a week ago onwards; source is manual for shifts sent by you or the salon and shyfter for shifts synced from Shyfter. People whose hours come from Shyfter answer 409 shyfter_managed to shift writes.

Time off staff:readstaff:write

A block of time when a person is away, a room is out of use, or the whole salon is closed. Bookings cannot be made inside it; bookings that already exist inside it are reported back so someone can call the guests.

GET/time-offevery entry, oldest first
POST/time-off
DELETE/time-off/{extern_id}

Fields

FieldTypeMeaning
extern_idrequiredstring
staff_extern_idstringThe person; leave out together with room_extern_id to close the whole salon
room_extern_idstringThe room, e.g. maintenance
starts_at, ends_atrequiredtimestampA whole day is midnight to the next midnight in the salon's offset
kindenumsick, holiday, training, personal, closed, other. Default other, or closed for a salon closure. Shown in the salon's agenda
reasonstringFree text for the salon

Example: sick day with a booking inside it

POST /api/v1/time-off
{
  "extern_id": "leave-2026-09-10-ayla",
  "staff_extern_id": "therapist-ayla",
  "starts_at": "2026-09-10T00:00:00+02:00",
  "ends_at": "2026-09-11T00:00:00+02:00",
  "kind": "sick"
}

→ 201
{
  "time_off": { "extern_id": "leave-2026-09-10-ayla", "kind": "sick", "starts_at": "2026-09-09T22:00:00+00:00", "ends_at": "2026-09-10T22:00:00+00:00", … },
  "affected": [
    {
      "bookingNo": 57,
      "startsAt": "2026-09-10T08:00:00.000Z",
      "endsAt": "2026-09-10T09:00:00.000Z",
      "roomName": "Room 1",
      "status": "confirmed",
      "members": [
        { "serviceName": "Classic Massage", "staffName": "Ayla", "customerName": "Nesrin Yilmaz", "customerEmail": "nesrin@example.com", "customerPhone": null }
      ]
    }
  ]
}

The salon is emailed the same affected list. Moving or cancelling those bookings is a decision for a person; the API does not do it for you.

Example: close the salon for a public holiday

POST /api/v1/time-off
{ "extern_id": "closed-2026-12-25", "starts_at": "2026-12-25T00:00:00+01:00", "ends_at": "2026-12-26T00:00:00+01:00", "kind": "closed", "reason": "Christmas" }

Typical flows

Hotel PMS: from check-in to checkout

1 · Check-in POST /guest-passes with the reservation number, check-in and checkout as valid_from/valid_until, package fields if the rate includes massages. The guest gets the email; print the QR on the welcome card if you like.
2 · During the stay The guest books on their link — nothing to do. Reception books for them with POST /bookings and guest_pass_extern_id. GET /bookings per day feeds the housekeeping or concierge screen.
3 · Changes Late checkout: PATCH the pass with a later valid_until. Room change or new email: PATCH again. Guest cannot find the email: POST …/email.
4 · Checkout Nothing to do — the link stops at valid_until. Early departure: POST …/revoke. Package used up: GET the pass and read free_used for the folio.

Central catalog: keep prices and the menu in one place

Nightly For each product: POST /services; on 409 extern_id_conflict send PATCH /services/{extern_id} with the current fields. Products gone from your system: DELETE (hides them, keeps history).
Once Items and rooms rarely change; set them up by hand in the dashboard or once through the API, then only keep services in sync.

Planning tool: push the roster

Weekly, when the roster is published For each employee: DELETE …/shifts?from&to for the week, then POST …/shifts with the new shifts. Absences go to POST /time-off with the right kind.
Watch the affected list on time off and the salon's agenda warnings: the API never moves a guest's appointment on its own.

Testing your integration

There is no separate sandbox; every call reaches the real salon. This keeps the integration honest, and with a few habits it is perfectly safe:

Stuck? Send the salon the request you made (without the key), the response and the time; they can pass it on and we can see the call in our logs.

What changed

DateChange
2026-09-09Services: details (longer explanation behind the "i" on the booking page). Name, description and details are translated automatically into Dutch and French.
2026-09-12Waiting list on the booking page; GET /waitlist (read-only) for partners.
2026-09-11Rate limits on the partner API: 300 requests a minute per address, 600 per key; 429 rate_limited with Retry-After.
2026-09-09Staff: gender; bookings: members[].staff_gender lets a guest ask for a woman or a man.
2026-09-09This page: field tables, response examples, flows and the testing checklist.