API referenceAppointments

Appointments

8 operations. Every schema and example on this page is generated from the platform contract.

The caller's appointments.

Always scoped to the signed-in customer, ownership is part of the query, never a filter applied after fetching. There is no way to ask for somebody else's bookings.

Parameters

store_idstring · uuid, nullablequeryoptional

Narrow to one store. Omit for every store this customer has booked with.

include_pastbooleanqueryoptional

Include appointments that have already happened. Defaults to upcoming only.

limitintegerqueryoptional

Response, 200

appointmentsarray of objectrequired
14 child fields
idstringrequired
store_idstringrequired
unit_idstringrequired
service_idstring, nullablerequired
starts_atstringrequired
ends_atstringrequired
statusstringrequired
payment_statusstringrequired
price_pencenumberrequired
deposit_pencenumberrequired
currencystringrequired

Every amount carries its currency, never inferred from the caller.

party_sizenumberrequired
childrennumberrequired

How many of the party are children (age 12 and under). 0 for a normal appointment.

high_chairsnumberrequired

High chairs requested (a request, not a seat).

curl -G "https://www.membber.com/api/v1/appointments" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "appointments": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "store_id": "6659c139-0000-4000-8000-d0c500000066",
      "unit_id": "eeebf9b6-0000-4000-8000-d0c5000000ee",
      "service_id": "993232e5-0000-4000-8000-d0c500000099",
      "starts_at": "<starts_at>",
      "ends_at": "<ends_at>",
      "status": "<status>",
      "payment_status": "<payment_status>",
      "price_pence": 1500,
      "deposit_pence": 1500,
      "currency": "GBP",
      "party_size": 1,
      "children": 1,
      "high_chairs": 1
    }
  ]
}

Book an appointment.

Converts a hold into a booking IN PLACE, so the slot is never free for an instant between paying and being booked. Double-booking is prevented by a database constraint rather than a check, so a lost race returns STALE_SLOT and nothing is written. Price and policy are SNAPSHOTTED onto the appointment: a later change to either never rewrites what the customer agreed to. Send an Idempotency-Key, retrying is safe and will not produce a second booking.

Request body

store_idstring · uuidrequired

The store being booked.

unit_idstring · uuidrequired

The staff member / room / table.

service_idstring · uuid, nullableoptional

The service booked. Its price and duration are SNAPSHOTTED onto the appointment.

starts_atstringrequired

ISO instant the appointment starts.

ends_atstringrequired

ISO instant the appointment ends.

hold_tokenstring, nullableoptional

The hold taken for this slot. Strongly recommended: without it the slot is unprotected between the availability read and this call.

guest_emailstring · email, nullableoptional

Required when booking without a signed-in customer, somebody has to be reachable.

guest_namestring, nullableoptional

Who the appointment is for, when there is no customer record.

party_sizeintegeroptional

People in the party (adults + children). 1 for a normal appointment. This is the occupancy that seats the table.

childrenintegeroptional

Of the party, how many are children (age 12 and under). Counted within party_size, not on top of it. The venue reads this to prepare.

high_chairsintegeroptional

High chairs requested. A request, not a seat, never changes the party size. At most one per child.

preferred_area_idstring · uuid, nullableoptional

The dining AREA the guest asked for (Level-1 seating), e.g. a window table. A preference the host tries to honour, never the assigned table, that stays unit_id. Null for no-preference and every non-restaurant booking; a stale or foreign area is dropped to null server-side.

price_penceintegeroptional

Agreed price in the smallest currency unit. Snapshotted, a later price change never rewrites it.

premium_price_penceintegeroptional

ASSERTION of the flat premium-table upgrade the client showed (e.g. 800 = "+£8"). The server resolves the true figure from the table and refuses a mismatch (PREMIUM_CHANGED), then charges it UPFRONT with the deposit. Omit or 0 for an ordinary table.

accepted_min_spend_penceintegeroptional

ASSERTION of the minimum spend the diner agreed to (credited to the bill). Recorded as the agreement; NEVER charged. The server refuses a mismatch (PREMIUM_CHANGED) so the diner always agrees to the figure they saw.

deposit_penceintegeroptional

Deposit due now. Greater than zero leaves the appointment pending_payment until it is settled.

currencystringoptional

ISO currency for the amounts above. Defaults to the store's.

Response, 200

appointment_idstringrequired

The booking.

statusstringrequired

'confirmed', or 'pending_payment' when a deposit is still due.

starts_atstringrequired

ISO instant it starts.

ends_atstringrequired

ISO instant it ends, what the customer was told, not how long the unit is occupied.

payment_next_actionstring, nullableoptional

'collect_deposit' when a deposit must be taken before this booking is safe; null when nothing is owed now.

curl -X POST "https://www.membber.com/api/v1/appointments" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "unit_id": "eeebf9b6-0000-4000-8000-d0c5000000ee",
    "starts_at": "<starts_at>",
    "ends_at": "<ends_at>"
  }'
Response, 200
{
  "appointment_id": "0d727dbb-0000-4000-8000-d0c50000000d",
  "status": "<status>",
  "starts_at": "<starts_at>",
  "ends_at": "<ends_at>",
  "payment_next_action": "<payment_next_action>"
}

Bookable slots for a service.

Computed from the unit's working patterns in the STORE's timezone, at the store's own slot increment, honouring lead time and booking horizon. Every slot returned has been checked against the same single authority the booking call uses, so a slot listed here is genuinely bookable, but it is still a snapshot: another customer can take one between this read and your booking, which is exactly what STALE_SLOT reports.

auth none, publicop getAppointmentAvailabilityrate limitederrors

Parameters

store_idstring · uuidqueryrequired

The store to read availability for.

service_idstring · uuidqueryrequired

The service being booked, its duration and buffers shape the slots.

from_datestringqueryrequired

First date to search, YYYY-MM-DD, in the STORE's timezone.

to_datestringqueryrequired

Last date to search, YYYY-MM-DD. Clamped to the store's booking horizon.

unit_idstring · uuid, nullablequeryoptional

Restrict to one staff member / room / table. Omit to see every unit that offers the service.

party_sizeintegerqueryoptional

People in the party. For a restaurant this filters tables to the party's seat band and scales the turn time. Omit or 1 for a normal appointment. (Coerced: it arrives as a query string.)

Response, 200

slotsarray of objectrequired

Every slot that is genuinely bookable right now, soonest first.

10 child fields
unit_idstringrequired

Which staff member / room / table this slot belongs to.

starts_atstringrequired

ISO instant the APPOINTMENT starts (what the customer is told).

ends_atstringrequired

ISO instant the APPOINTMENT ends. The unit may be occupied longer if the service has buffers.

area_idstring, nullableoptional
area_namestring, nullableoptional
area_sortnumber, nullableoptional
premium_price_pencenumber, nullableoptional

A flat upgrade to guarantee this exact table (e.g. 800 = "+£8"). 0/null when the table is not premium this way.

min_spend_pencenumber, nullableoptional

A minimum spend to hold this table, credited to the bill (e.g. 4000 = "£40 min spend"). Never charged upfront.

seats_minnumber, nullableoptional

Smallest party the table seats, lets the client label it.

seats_maxnumber, nullableoptional

Largest party the table seats.

generated_atstringrequired

When this list was computed. A slot list is a snapshot of a moving thing, compare this before committing, and expect STALE_SLOT if it is old.

curl -G "https://www.membber.com/api/v1/appointments/availability" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066" \
  --data-urlencode "service_id=993232e5-0000-4000-8000-d0c500000099" \
  --data-urlencode "from_date=<from_date>" \
  --data-urlencode "to_date=<to_date>"
Response, 200
{
  "slots": [
    {
      "unit_id": "eeebf9b6-0000-4000-8000-d0c5000000ee",
      "starts_at": "<starts_at>",
      "ends_at": "<ends_at>",
      "area_id": "d381ba0d-0000-4000-8000-d0c5000000d3",
      "area_name": "<area_name>",
      "area_sort": 1,
      "premium_price_pence": 1500,
      "min_spend_pence": 1500,
      "seats_min": 1,
      "seats_max": 1
    }
  ],
  "generated_at": "<generated_at>"
}

Cancel an appointment (preview first).

Call without `confirm` to see exactly what cancelling costs before committing to it, then again with `confirm: true`. The fee is evaluated against the policy SNAPSHOTTED on the booking, so a shop tightening its terms next week cannot retroactively charge someone who booked under the old ones. Idempotent, cancelling twice is a no-op, never a second charge.

Request body

appointment_idstring · uuidrequired

The appointment to cancel.

reasonstring, nullableoptional

Why, for the record and the merchant.

confirmbooleanoptional

false/omitted returns a PREVIEW of what cancelling costs and changes nothing. true performs it. A fee discovered AFTER the fact is the most complained-about thing in booking apps.

Response, 200

appointment_idstringrequired
cancelledbooleanrequired

false when this was a preview.

fee_pencenumberrequired

What cancelling costs, from the policy the customer AGREED to, not the shop's current one.

refund_pencenumberrequired

What comes back of anything already paid. Never more than was taken.

currencystringrequired
within_free_windowbooleanrequired

true when it is still early enough to cancel free.

cancel_window_hoursnumberrequired

The window that applied to THIS booking.

curl -X POST "https://www.membber.com/api/v1/appointments/cancel" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "appointment_id": "0d727dbb-0000-4000-8000-d0c50000000d"
  }'
Response, 200
{
  "appointment_id": "0d727dbb-0000-4000-8000-d0c50000000d",
  "cancelled": true,
  "fee_pence": 1500,
  "refund_pence": 1500,
  "currency": "GBP",
  "within_free_window": true,
  "cancel_window_hours": 1
}

Collect the deposit on a booking.

Creates a Stripe PaymentIntent on the store's connected account for the deposit a pending_payment booking owes, carrying the uniform 1.5% platform fee every member transaction does. IDEMPOTENT per appointment, retrying returns the SAME PaymentIntent, never a second charge. The booking flips to confirmed only when the deposit succeeds, reconciled server-side by the Connect webhook; a deposit that lands after the booking has lapsed is refunded automatically rather than kept.

Request body

appointment_idstring · uuidrequired

The pending_payment appointment whose deposit is being collected.

Response, 200

payment_intent_idstringrequired

The Stripe PaymentIntent collecting the deposit.

client_secretstringrequired

Confirm this PaymentIntent client-side (Apple Pay / card) to pay the deposit.

ephemeral_keystring, nullablerequired

Short-lived key so the payment sheet can show the customer's saved cards. Null if one could not be minted, the sheet still works without it.

stripe_customer_idstring, nullablerequired

The platform-account customer the deposit is billed to.

connected_account_idstringrequired

The store's connected account the funds settle to.

amount_penceintegerrequired

The deposit due now, in the smallest currency unit.

currencystringrequired

ISO currency of the deposit.

curl -X POST "https://www.membber.com/api/v1/appointments/deposit-intent" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "appointment_id": "0d727dbb-0000-4000-8000-d0c50000000d"
  }'
Response, 200
{
  "payment_intent_id": "0b6642a5-0000-4000-8000-d0c50000000b",
  "client_secret": "<client_secret>",
  "ephemeral_key": "<ephemeral_key>",
  "stripe_customer_id": "847b302a-0000-4000-8000-d0c500000084",
  "connected_account_id": "231b6f63-0000-4000-8000-d0c500000023",
  "amount_pence": 1500,
  "currency": "GBP"
}

Reserve a slot for a few minutes while paying.

Holds a slot so nobody else can take it mid-payment. Ten minutes by default, long enough for a card security check, short enough that an abandoned checkout does not sterilise a busy slot. Idempotent per hold_token. Returns STALE_SLOT if the slot went first, or HOLD_LIMIT (with Retry-After) if this unit already has too many live holds for that day, which is the anti-squatting guard.

Request body

unit_idstring · uuidrequired

The staff member / room / table to reserve.

starts_atstringrequired

ISO instant the appointment would start.

ends_atstringrequired

ISO instant the appointment would end.

hold_tokenstringrequired

Caller-generated idempotency key. The SAME token returns the SAME hold rather than taking a second slot, so a retry or a double tap can never consume two times.

Response, 200

hold_idstringrequired

The reservation.

expires_atstringrequired

ISO instant the hold lapses, 10 minutes by default (founder decision D10). Show this to the customer rather than guessing it.

reusedbooleanrequired

true when this token already had a live hold and it was returned unchanged (idempotent replay).

curl -X POST "https://www.membber.com/api/v1/appointments/holds" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "unit_id": "eeebf9b6-0000-4000-8000-d0c5000000ee",
    "starts_at": "<starts_at>",
    "ends_at": "<ends_at>",
    "hold_token": "<hold_token>"
  }'
Response, 200
{
  "hold_id": "4112d09b-0000-4000-8000-d0c500000041",
  "expires_at": "<expires_at>",
  "reused": true
}

The Apple Wallet pass for a booking.

Returns the signed .pkpass for one of the caller's own appointments, base64-encoded. Scoped to the signed-in customer: ownership is checked against the row, and a booking that is not yours is indistinguishable from one that does not exist. A cancelled booking answers GONE rather than a pass, the card it had has been evicted.

auth customer:readop getAppointmentWalletPassrate limitederrors

Parameters

appointment_idstring · uuidqueryrequired

The booking to make a pass for. Must belong to the caller.

Response, 200

pkpass_base64stringrequired

The signed .pkpass file, base64-encoded. Decode to bytes and hand to PassKit.

serial_numberstringrequired

The pass serial. Stable per appointment, so a reschedule updates the SAME card rather than adding a second one.

curl -G "https://www.membber.com/api/v1/appointments/pass" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "appointment_id=0d727dbb-0000-4000-8000-d0c50000000d"
Response, 200
{
  "pkpass_base64": "<pkpass_base64>",
  "serial_number": "<serial_number>"
}

Move an appointment.

Secures the NEW slot before releasing the OLD one, so if the destination has gone the original booking survives untouched, you are never left with nothing. Returns STALE_SLOT if the new time went first, or OUTSIDE_POLICY_WINDOW if it is too close to the appointment to change online.

Request body

appointment_idstring · uuidrequired

The appointment to move.

unit_idstring · uuidrequired

Where it is moving to, may be the same staff member / room / table.

starts_atstringrequired

ISO instant of the new start.

ends_atstringrequired

ISO instant of the new end.

hold_tokenstring, nullableoptional

A hold taken on the NEW slot. Recommended for the same reason as booking: without it the destination is unprotected.

Response, 200

appointment_idstringrequired
starts_atstringrequired
ends_atstringrequired
unit_idstringrequired
moved_in_placebooleanrequired

true when the booking was nudged within the same unit (the block moved rather than being re-taken).

curl -X POST "https://www.membber.com/api/v1/appointments/reschedule" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "appointment_id": "0d727dbb-0000-4000-8000-d0c50000000d",
    "unit_id": "eeebf9b6-0000-4000-8000-d0c5000000ee",
    "starts_at": "<starts_at>",
    "ends_at": "<ends_at>"
  }'
Response, 200
{
  "appointment_id": "0d727dbb-0000-4000-8000-d0c50000000d",
  "starts_at": "<starts_at>",
  "ends_at": "<ends_at>",
  "unit_id": "eeebf9b6-0000-4000-8000-d0c5000000ee",
  "moved_in_place": true
}
WhatsApp
Book a Call
Start Free