Skip to content

Reference

API reference

Every endpoint, with the parameters it takes, the scope it needs, a real response — and a console that sends the request for you. Base URL https://www.lovingbooking.com/api/v1.

Generated from the spec, answered by the sandbox

The paths, parameters and scopes below are read from the same OpenAPI document served at /api/v1/openapi.json, so this page cannot describe an endpoint the API no longer has. Point Redoc, Swagger UI or Postman at that URL to explore it in your own tooling.

Every response and every console below runs against the sandbox — four fixture businesses, no database, nothing stored. Switch the business in any console to see the same endpoint answer for a yoga studio, a consultancy, a nail salon or a barbershop.

GET/meany key

The business, the key and its remaining quota

The first call to make. Names the tenant, timezone, currency, exact scopes and current plan, and gives the date and time on the business’s own clock. plan.trialEndsAt is the UTC expiry for a cardless Pro trial, otherwise null. Requires no scope of its own; the account must still have API access.

Call this first. It is the only way to learn the timezone your date parameters are read in and the currency prices are quoted in — switch the business in the console above and watch both change. clock.today is today’s date on the business’s own clock; work out “tomorrow” from it, not from your server’s date.

curl https://www.lovingbooking.com/api/v1/me \
  -H "Authorization: Bearer lb_sk_YOUR_KEY"
Response
{
  "data": {
    "business": {
      "id": 1,
      "name": "Still Point Yoga",
      "slug": "still-point-yoga",
      "timezone": "Asia/Singapore",
      "currency": "SGD",
      "bookingPageUrl": "/book/still-point-yoga"
    },
    "clock": {
      "now": "2026-09-09T16:00:00.000Z",
      "nowLocal": "2026-09-10T00:00:00+08:00",
      "today": "2026-09-10",
      "timezone": "Asia/Singapore"
    },
    "key": {
      "id": 0,
      "scopes": [
        "catalog:read",
        "availability:read",
        "bookings:read",
        "bookings:write",
        "customers:read"
      ]
    },
    "plan": {
      "name": "Pro",
      "trialEndsAt": null,
      "trialDaysRemaining": null
    },
    "quota": {
      "limit": 10000,
      "used": 1,
      "remaining": 9999,
      "resetsAt": "daily at 00:00 UTC"
    },
    "notes": {
      "payments": "LovingBooking does not process booking payments. Prices are what a booking is WORTH, not money received; there is no card rail or capture in this API, and it takes no deposit: one a business asks for by its own QR goes to the business directly and is checked by hand."
    }
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
GET/api/sandbox/v1/me

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Press Send. Nothing here reaches a real business, and nothing you type is stored.

GET/servicescatalog:read

Bookable services

Defaults to what a customer could book: active and not hidden. include=all widens it to everything the owner sees, and every row says which it is. Each row carries an options array of { id, name, durationMinutes, priceCents } — empty for a service with one length and one price. Where it is not empty, durationMinutes and priceCents on the row itself describe the FIRST option, so nothing an older client reads has changed.

maxCapacity above 1 is a group service: several people can hold the same start time. hidden means off the public booking page but still bookable by the owner. isActive false cannot be booked at all: a create naming it is a 422.

ParameterInTypeNotes
includequerystringSet to all to include inactive and hidden services. One of: all.
curl https://www.lovingbooking.com/api/v1/services \
  -H "Authorization: Bearer lb_sk_YOUR_KEY"
Response
{
  "data": [
    {
      "id": 1010,
      "name": "Morning Vinyasa",
      "description": "A 60-minute flow for all levels. Mats provided.",
      "durationMinutes": 60,
      "priceCents": 3200,
      "currency": "SGD",
      "categoryId": null,
      "locationId": null,
      "maxCapacity": 12,
      "bufferBeforeMinutes": 0,
      "bufferAfterMinutes": 0,
      "isActive": true,
      "hidden": false,
      "options": []
    },
    {
      "id": 1011,
      "name": "Private session (1:1)",
      "description": "One to one, shaped around your body and your week.",
      "durationMinutes": 75,
      "priceCents": 14000,
      "currency": "SGD",
      "categoryId": null,
      "locationId": null,
      "maxCapacity": 1,
      "bufferBeforeMinutes": 0,
      "bufferAfterMinutes": 15,
      "isActive": true,
      "hidden": false,
      "options": []
    }
  ],
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
GET/api/sandbox/v1/services

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Query

all — adds inactive and hidden services

Press Send. Nothing here reaches a real business, and nothing you type is stored.

GET/locationscatalog:read

Active venues

latitude and longitude are null unless the venue has been geocoded, which most have not. Read null as unknown, never as 0,0.

curl https://www.lovingbooking.com/api/v1/locations \
  -H "Authorization: Bearer lb_sk_YOUR_KEY"
Response
{
  "data": [
    {
      "id": 1001,
      "name": "River Valley studio",
      "address": "12 River Valley Road, #02-05",
      "city": "Singapore",
      "state": null,
      "postalCode": "238253",
      "country": "SG",
      "phone": "+6561234567",
      "latitude": null,
      "longitude": null
    }
  ],
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
GET/api/sandbox/v1/locations

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Press Send. Nothing here reaches a real business, and nothing you type is stored.

GET/staffcatalog:read

Bookable team members

Names, titles and photos, plus where each person works (locationIds) and what they perform (serviceIds). Staff email and phone are personal data and are deliberately not exposed by any current scope.

locationIds and serviceIds say where each person works and what they perform — a booking needs a team member whose lists include its venue and its service. Email and phone are deliberately absent: staff contact details are personal data about someone who never agreed to your integration, and no current scope exposes them.

curl https://www.lovingbooking.com/api/v1/staff \
  -H "Authorization: Bearer lb_sk_YOUR_KEY"
Response
{
  "data": [
    {
      "id": 1020,
      "name": "Amara Sim",
      "title": "Senior teacher",
      "bio": null,
      "photoUrl": null,
      "locationIds": [
        1001
      ],
      "serviceIds": [
        1010,
        1011
      ],
      "performsAllServices": true
    },
    {
      "id": 1021,
      "name": "Josh Tan",
      "title": "Teacher",
      "bio": null,
      "photoUrl": null,
      "locationIds": [
        1001
      ],
      "serviceIds": [
        1010,
        1011
      ],
      "performsAllServices": true
    }
  ],
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
GET/api/sandbox/v1/staff

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Press Send. Nothing here reaches a real business, and nothing you type is stored.

GET/availabilityavailability:read

Open start times for a day, or a run of days

Computed with the same rules as the public booking page: working hours, date overrides, buffers, booking notice, existing appointments, teaching sessions and connected-calendar blocks.

Each slot names the team members free at that start (staffIds), so a time can be booked without asking again per person.

This is a snapshot, not a reservation. Nothing is held. The time is checked again when the booking is created, so a slot read here can still be refused a minute later. Handle that.

When no serviceId is given the first active service is used and serviceAssumed is true — the times are real, but they are for a service you did not name.

With days (1 to 14) the answer covers that many consecutive dates: the top-level fields still describe the first one and days lists them all.

An empty slots is an answer, not an error. dayStatus says why: open means staffed but full — or past the public booking window, which beyondPublicBookingHorizon flags; closed_override is a day off; no_hours_for_day means nobody works that weekday. A date that has passed, or a business with nothing to sell, answers with slots: [] and a reason instead.

Each slot carries staffIds, the team members free at that start: send the slot’s startsAt and one of those ids to POST /bookings. A day with no slots still returns 200. dayStatus tells you why: open means staffed but full (or past the public booking window, which beyondPublicBookingHorizon flags), closed_override means a day off, no_hours_for_day means nobody works that weekday. Try a Sunday at the barbershop.

ParameterInTypeNotes
daterequiredquerystringYYYY-MM-DD, read in the business's timezone.
daysqueryintegerHow many consecutive dates to cover, starting at date. At most 14.
serviceIdqueryintegerThe service to find times for. Omit and the first active one is assumed.
staffIdqueryintegerOnly this team member. Omit to search everyone who performs the service.
locationIdqueryintegerOnly this venue.
curl https://www.lovingbooking.com/api/v1/availability?date=2026-09-10&serviceId=1010 \
  -H "Authorization: Bearer lb_sk_YOUR_KEY"
Response
{
  "data": {
    "date": "2026-09-10",
    "dayName": "Thursday",
    "timezone": "Asia/Singapore",
    "service": {
      "id": 1010,
      "name": "Morning Vinyasa",
      "durationMinutes": 60,
      "maxCapacity": 12
    },
    "location": {
      "id": 1001,
      "name": "River Valley studio"
    },
    "staff": [
      {
        "id": 1020,
        "name": "Amara Sim"
      },
      {
        "id": 1021,
        "name": "Josh Tan"
      }
    ],
    "serviceAssumed": false,
    "slots": [
      {
        "startsAt": "2026-09-09T23:00:00.000Z",
        "startsAtLocal": "2026-09-10T07:00:00+08:00",
        "label": "7:00 AM",
        "spotsLeft": 4,
        "staffIds": [
          1020,
          1021
        ]
      },
      {
        "startsAt": "2026-09-10T01:30:00.000Z",
        "startsAtLocal": "2026-09-10T09:30:00+08:00",
        "label": "9:30 AM",
        "spotsLeft": 12,
        "staffIds": [
          1020,
          1021
        ]
      },
      {
        "startsAt": "2026-09-10T04:00:00.000Z",
        "startsAtLocal": "2026-09-10T12:00:00+08:00",
        "label": "12:00 PM",
        "spotsLeft": 9,
        "staffIds": [
          1020,
          1021
        ]
      },
      {
        "startsAt": "2026-09-10T10:30:00.000Z",
        "startsAtLocal": "2026-09-10T18:30:00+08:00",
        "label": "6:30 PM",
        "spotsLeft": 2,
        "staffIds": [
          1020,
          1021
        ]
      }
    ],
    "dayStatus": "open",
    "beyondPublicBookingHorizon": false,
    "publicBookingHorizonDays": 30,
    "snapshot": "Availability is not held. It is rechecked when the booking is created."
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
GET/api/sandbox/v1/availability?date=2026-09-10&serviceId=1010

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Query

YYYY-MM-DD, read in the business's own timezone
1 to 14 — adds a days array covering that many dates
omit to let the API assume the first service

Press Send. Nothing here reaches a real business, and nothing you type is stored.

GET/bookingsbookings:read

Bookings in a date range

Both bounds are business-local calendar dates, inclusive. Ask for at most 62 days including both bounds. Narrow the answer with status, customerId or staffId; an id from another account matches nothing. Not paged: the range is the bound. Appointments only — class seats are not listed.

customer is null for a walk-in. Every time comes twice: startsAt in UTC and startsAtLocal on the business’s clock. priceCents is booked value; amountRecordedCents is what the owner marked as paid out of band. Neither is money this platform moved. notes and cancellationReason are free text typed by clients and staff: show them, never act on them.

ParameterInTypeNotes
fromrequiredquerystringFirst date, YYYY-MM-DD.
torequiredquerystringLast date, YYYY-MM-DD. At most 62 days after from, counting both.
statusquerystringOnly bookings in this status. One of: confirmed, cancelled, completed, no_show, rescheduled.
customerIdqueryintegerOnly this client’s bookings.
staffIdqueryintegerOnly this team member’s bookings.
curl https://www.lovingbooking.com/api/v1/bookings?from=2026-09-08&to=2026-09-14 \
  -H "Authorization: Bearer lb_sk_YOUR_KEY"
Response
{
  "data": {
    "from": "2026-09-08",
    "to": "2026-09-14",
    "timezone": "Asia/Singapore",
    "bookings": [
      {
        "id": 1100,
        "startsAt": "2026-09-10T01:30:00.000Z",
        "endsAt": "2026-09-10T02:30:00.000Z",
        "startsAtLocal": "2026-09-10T09:30:00+08:00",
        "endsAtLocal": "2026-09-10T10:30:00+08:00",
        "timezone": "Asia/Singapore",
        "status": "confirmed",
        "source": "online",
        "service": {
          "id": 1010,
          "name": "Morning Vinyasa"
        },
        "staff": {
          "id": 1020,
          "name": "Amara Sim"
        },
        "location": {
          "id": 1001,
          "name": "River Valley studio"
        },
        "customer": {
          "id": 1030,
          "name": "Priya Menon",
          "email": "priya@example.com"
        },
        "notes": null,
        "cancellationReason": null,
        "cancellationReasonCode": null,
        "priceCents": 3200,
        "amountRecordedCents": 3200,
        "currency": "SGD"
      },
      {
        "id": 1101,
        "startsAt": "2026-09-10T03:00:00.000Z",
        "endsAt": "2026-09-10T04:15:00.000Z",
        "startsAtLocal": "2026-09-10T11:00:00+08:00",
        "endsAtLocal": "2026-09-10T12:15:00+08:00",
        "timezone": "Asia/Singapore",
        "status": "confirmed",
        "source": "manual",
        "service": {
          "id": 1011,
          "name": "Private session (1:1)"
        },
        "staff": {
          "id": 1021,
          "name": "Josh Tan"
        },
        "location": {
          "id": 1001,
          "name": "River Valley studio"
        },
        "customer": null,
        "notes": "Walk-in, paid at the desk",
        "cancellationReason": null,
        "cancellationReasonCode": null,
        "priceCents": 14000,
        "amountRecordedCents": 14000,
        "currency": "SGD"
      },
      {
        "id": 1102,
        "startsAt": "2026-09-10T08:00:00.000Z",
        "endsAt": "2026-09-10T09:00:00.000Z",
        "startsAtLocal": "2026-09-10T16:00:00+08:00",
        "endsAtLocal": "2026-09-10T17:00:00+08:00",
        "timezone": "Asia/Singapore",
        "status": "cancelled",
        "source": "online",
        "service": {
          "id": 1010,
          "name": "Morning Vinyasa"
        },
        "staff": {
          "id": 1020,
          "name": "Amara Sim"
        },
        "location": {
          "id": 1001,
          "name": "River Valley studio"
        },
        "customer": {
          "id": 1031,
          "name": "Daniel Ong",
          "email": "daniel@example.com"
        },
        "notes": null,
        "cancellationReason": "Asked to move to next week",
        "cancellationReasonCode": "customer_requested",
        "priceCents": 3200,
        "amountRecordedCents": 0,
        "currency": "SGD"
      }
    ]
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
GET/api/sandbox/v1/bookings?from=2026-09-08&to=2026-09-14

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Query

at most 62 days after from
confirmed, cancelled, completed, no_show, rescheduled
only this client’s bookings
only this team member’s bookings

Press Send. Nothing here reaches a real business, and nothing you type is stored.

POST/bookingsbookings:write

Create a booking

Books an appointment and, unless told otherwise, emails the client a confirmation.

The service, team member, location and client must all be on this account (otherwise 404), switched on and sold together — the team member has to perform that service at that location (otherwise 422). The time is then checked against the diary: other bookings, buffers, working hours, blocked time, classes being taught and connected calendars. Any of those is a 409 whose details.warnings lists every problem found and whose details.forceable is true; resend with force: true only if the owner has decided to book over it. A one-to-one double-booking and a taken room are refused whatever force says.

Pass either customerId for an existing client or customer to add one. Pass neither for a walk-in with no client attached. A customer whose email is already on file attaches the booking to that client and leaves their stored name and phone unchanged; customerCreated in the answer says which happened.

Not idempotent: a retry after a lost response can book a second place in a group service. Read the day back before retrying.

Returns 201 with the booking. Change startsAt to a time that is already taken and you get the 409 with details.reason, details.forceable and its warnings, and nothing written; resend with force: true only if the owner has decided to book over it. An id that is not on the account is a 404, ids that do not go together are a 422, and neither is forceable. A customer whose email is already on file is matched to that client, whose stored name and phone are left alone.

Body fieldTypeNotes
serviceIdrequiredintegerFrom /services.
staffIdrequiredintegerA team member who performs the service at the location: one of a slot’s staffIds.
locationIdrequiredintegerFrom /locations.
startsAtrequiredstring (date-time)ISO 8601 with an offset. A slot’s startsAt or startsAtLocal can be sent back as it is. A time with no offset is refused.
customerIdintegerAn existing client. Omit to use customer.
customerobjectA new client. An email already on file attaches the booking to that client instead, unchanged.
customer.namestringAt most 200 characters.
customer.emailstring (email)
customer.phonestringAt most 40 characters.
notesstringAt most 2000 characters.
sendConfirmationEmailbooleanEmail the client a confirmation. Defaults to true for a future booking, false for one that has already started.
forcebooleanBook over a refusal whose details.forceable is true. Never overrides a 404, a one-to-one double-booking or a room. Default false.
curl https://www.lovingbooking.com/api/v1/bookings \
  -X POST \
  -H "Authorization: Bearer lb_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "serviceId": 1010,
  "staffId": 1020,
  "locationId": 1001,
  "startsAt": "2026-09-09T23:00:00.000Z",
  "customer": {
    "name": "Priya Menon",
    "email": "priya@example.com"
  }
}'
Response
{
  "data": {
    "id": 1103,
    "booking": {
      "id": 1103,
      "startsAt": "2026-09-09T23:00:00.000Z",
      "endsAt": "2026-09-10T00:00:00.000Z",
      "startsAtLocal": "2026-09-10T07:00:00+08:00",
      "endsAtLocal": "2026-09-10T08:00:00+08:00",
      "timezone": "Asia/Singapore",
      "status": "confirmed",
      "source": "manual",
      "service": {
        "id": 1010,
        "name": "Morning Vinyasa"
      },
      "staff": {
        "id": 1020,
        "name": "Amara Sim"
      },
      "location": {
        "id": 1001,
        "name": "River Valley studio"
      },
      "customer": {
        "id": 1030,
        "name": "Priya Menon",
        "email": "priya@example.com"
      },
      "notes": null,
      "cancellationReason": null,
      "cancellationReasonCode": null,
      "priceCents": 3200,
      "amountRecordedCents": 0,
      "currency": "SGD"
    },
    "customerCreated": false
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored. No booking was created and no email was sent."
}
Try itSandbox
POST/api/sandbox/v1/bookings

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Press Send. Nothing here reaches a real business, and nothing you type is stored.

GET/bookings/{id}bookings:read

One booking

A single booking by id, in the shape /bookings lists it. An id on another account answers 404, exactly like one that does not exist.

The same object /bookings lists, for one id — what to read back after a write. An id on another account is a 404, exactly like one that does not exist.

ParameterInTypeNotes
idrequiredpathintegerThe booking’s id.
curl https://www.lovingbooking.com/api/v1/bookings/1100 \
  -H "Authorization: Bearer lb_sk_YOUR_KEY"
Response
{
  "data": {
    "id": 1100,
    "startsAt": "2026-09-10T01:30:00.000Z",
    "endsAt": "2026-09-10T02:30:00.000Z",
    "startsAtLocal": "2026-09-10T09:30:00+08:00",
    "endsAtLocal": "2026-09-10T10:30:00+08:00",
    "timezone": "Asia/Singapore",
    "status": "confirmed",
    "source": "online",
    "service": {
      "id": 1010,
      "name": "Morning Vinyasa"
    },
    "staff": {
      "id": 1020,
      "name": "Amara Sim"
    },
    "location": {
      "id": 1001,
      "name": "River Valley studio"
    },
    "customer": {
      "id": 1030,
      "name": "Priya Menon",
      "email": "priya@example.com"
    },
    "notes": null,
    "cancellationReason": null,
    "cancellationReasonCode": null,
    "priceCents": 3200,
    "amountRecordedCents": 3200,
    "currency": "SGD"
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
GET/api/sandbox/v1/bookings/1100

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Press Send. Nothing here reaches a real business, and nothing you type is stored.

POST/bookings/{id}/cancelbookings:write

Cancel a booking

A status change, not a delete: the row stays so history and reporting remain true. Releases the seat, restores a package session, removes the booking from connected calendars and tells the assigned team member. The client is emailed unless notifyCustomer is false. A booking that is already cancelled answers 409.

A status change, not a delete: the row stays so history and reporting remain true. It releases the seat, restores a package session, clears connected calendars and emails the client and the team member. reason is required and must be one of customer_requested, schedule_conflict, booked_by_mistake, other; anything else is a 422 that lists them. Send notifyCustomer: false when you send the client your own message. Send it twice and the second one is a 409.

ParameterInTypeNotes
idrequiredpathintegerThe booking’s id.
Body fieldTypeNotes
reasonrequiredstringWhy. Required — the console requires one too. Anything else is a 422 that lists these. One of: customer_requested, schedule_conflict, booked_by_mistake, other.
notestringA short note kept with the cancellation. At most 500 characters.
notifyCustomerbooleanEmail the client the cancellation. Turn it off when you send your own message. The assigned team member is told either way. Default true.
curl https://www.lovingbooking.com/api/v1/bookings/1100/cancel \
  -X POST \
  -H "Authorization: Bearer lb_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "customer_requested",
  "note": "Rescheduling to next week"
}'
Response
{
  "data": {
    "id": 1100,
    "status": "cancelled",
    "booking": {
      "id": 1100,
      "startsAt": "2026-09-10T01:30:00.000Z",
      "endsAt": "2026-09-10T02:30:00.000Z",
      "startsAtLocal": "2026-09-10T09:30:00+08:00",
      "endsAtLocal": "2026-09-10T10:30:00+08:00",
      "timezone": "Asia/Singapore",
      "status": "cancelled",
      "source": "online",
      "service": {
        "id": 1010,
        "name": "Morning Vinyasa"
      },
      "staff": {
        "id": 1020,
        "name": "Amara Sim"
      },
      "location": {
        "id": 1001,
        "name": "River Valley studio"
      },
      "customer": {
        "id": 1030,
        "name": "Priya Menon",
        "email": "priya@example.com"
      },
      "notes": null,
      "cancellationReason": "Rescheduling to next week",
      "cancellationReasonCode": "customer_requested",
      "priceCents": 3200,
      "amountRecordedCents": 3200,
      "currency": "SGD"
    },
    "notifyCustomer": true
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
POST/api/sandbox/v1/bookings/1100/cancel

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Press Send. Nothing here reaches a real business, and nothing you type is stored.

POST/bookings/{id}/reschedulebookings:write

Move a booking

Moves a confirmed booking to a new start, and optionally to another team member, keeping its id, service, location, length and price. Connected calendars are updated and the assigned team member is told; the client is emailed their new time unless notifyCustomer is false.

The new time passes the same checks as a new booking, with this booking itself left out of them, and force means the same thing.

Only a confirmed booking moves (otherwise 409 status_refused). One paid with a pass session, or one that is a part of a visit with several services, is refused with 422 and has to be moved in the console.

Safe to repeat: asking for the time and person the booking already has answers 200 with changed: false and writes nothing.

Moves the booking and keeps its id — one email to the client instead of a cancellation and a confirmation, and no gap in which the new time can be lost. Add staffId to hand it to someone else. Only a confirmed booking moves; one paid with a pass session, or one part of a multi-service visit, is a 422 and has to be moved in the console. Asking for the time it already has answers changed: false.

ParameterInTypeNotes
idrequiredpathintegerThe booking’s id.
Body fieldTypeNotes
startsAtrequiredstring (date-time)The new start. ISO 8601 with an offset, as on create.
staffIdintegerMove it to this team member as well. Omit to keep the same person.
notifyCustomerbooleanEmail the client their new time. Default true.
forcebooleanAs on create. Default false.
curl https://www.lovingbooking.com/api/v1/bookings/1100/reschedule \
  -X POST \
  -H "Authorization: Bearer lb_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "startsAt": "2026-09-09T23:00:00.000Z"
}'
Response
{
  "data": {
    "id": 1100,
    "changed": true,
    "previousStartsAt": "2026-09-10T01:30:00.000Z",
    "booking": {
      "id": 1100,
      "startsAt": "2026-09-09T23:00:00.000Z",
      "endsAt": "2026-09-10T00:00:00.000Z",
      "startsAtLocal": "2026-09-10T07:00:00+08:00",
      "endsAtLocal": "2026-09-10T08:00:00+08:00",
      "timezone": "Asia/Singapore",
      "status": "confirmed",
      "source": "online",
      "service": {
        "id": 1010,
        "name": "Morning Vinyasa"
      },
      "staff": {
        "id": 1020,
        "name": "Amara Sim"
      },
      "location": {
        "id": 1001,
        "name": "River Valley studio"
      },
      "customer": {
        "id": 1030,
        "name": "Priya Menon",
        "email": "priya@example.com"
      },
      "notes": null,
      "cancellationReason": null,
      "cancellationReasonCode": null,
      "priceCents": 3200,
      "amountRecordedCents": 3200,
      "currency": "SGD"
    },
    "notifyCustomer": true
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
POST/api/sandbox/v1/bookings/1100/reschedule

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Press Send. Nothing here reaches a real business, and nothing you type is stored.

GET/customerscustomers:read

Search clients

Personal data about people who booked with this business. query matches a name, an email, or — given four or more digits — a phone number, ignoring spaces and punctuation. With no query, returns the most recently active clients first.

Personal data about people who booked with the business. query matches a name, an email or — with four or more digits — a phone number. With no query, returns the most recently active clients rather than everyone.

ParameterInTypeNotes
queryquerystringPart of a name, an email address or a phone number.
limitqueryintegerPage size. Capped at 100, not refused, when larger.
offsetqueryintegerRows to skip.
curl https://www.lovingbooking.com/api/v1/customers?query=priya&limit=20 \
  -H "Authorization: Bearer lb_sk_YOUR_KEY"
Response
{
  "data": [
    {
      "id": 1030,
      "name": "Priya Menon",
      "email": "priya@example.com",
      "phone": "+6591234567"
    }
  ],
  "pagination": {
    "limit": 20,
    "offset": 0,
    "hasMore": false
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}
Try itSandbox
GET/api/sandbox/v1/customers?query=priya&limit=20

Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000

Query

name, email or phone; omit for the most recently active

Press Send. Nothing here reaches a real business, and nothing you type is stored.