Skip to content

Getting started

Sandbox

Four demo businesses with public keys, so you can make a real request right now and decide whether this is worth integrating before you make an account.

Runs as-is
curl https://www.lovingbooking.com/api/sandbox/v1/me \
  -H "Authorization: Bearer lb_sk_demo_yoga0000000000000000000000000000000000"
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.

The four businesses

The same nine endpoints mean very different things depending on who is selling what. A studio fills a twelve-person class; a consultancy sells ninety minutes of one person’s attention and is shut at weekends; a salon runs two chairs back to back; a barbershop closes Sundays and Mondays and sits ten hours ahead of UTC. Each demo business also has its own timezone and its own currency, because those are the two things an integration gets wrong first.

Still Point YogaYoga studio

Group classes: maxCapacity is 12, so several bookings share one start time and spotsLeft counts down instead of the slot disappearing.

Timezone
Asia/Singapore
Currency
SGD
Key
lb_sk_demo_yoga0000000000000000000000000000000000

Harbour & Kite AdvisoryConsultancy

Long one-to-one appointments, no weekend hours, and prices in GBP — where a hard-coded currency symbol or a five-day assumption breaks first.

Timezone
Europe/London
Currency
GBP
Key
lb_sk_demo_consultancy000000000000000000000000000

Gilded Nail BarNail salon

Two chairs, short back-to-back appointments with buffers, and a negative UTC offset — the fixture where the local clock and the ISO instant fall on opposite sides of noon.

Timezone
America/New_York
Currency
USD
Key
lb_sk_demo_nails000000000000000000000000000000000

Anchor & BladeBarbershop

Closed Sundays and Mondays, and far enough east that even a 9 AM appointment carries the PREVIOUS day’s UTC date. Read this one before grouping bookings by startsAt.slice(0, 10).

Timezone
Australia/Sydney
Currency
AUD
Key
lb_sk_demo_barber00000000000000000000000000000000

These keys are public

These keys are public on purpose. They reach fixtures only: no database, no real business, and nothing you send is stored.

They are shaped exactly like a real key — lb_sk_ plus 43 URL-safe characters — so the validation, the header and the config field you write against the sandbox are the same code you will run against your own. The demo_ in clear at the front means a key in a log or a screenshot is instantly identifiable as the harmless one.

What is real and what is not

The sandbox is served from /api/sandbox/v1 — the same paths as /api/v1, the same envelope, the same status codes and the same error.code values. What differs is what sits behind it.

  • No database. The handler imports fixtures and nothing else. It is a separate route rather than a flag on the real API, so no combination of parameters can reach a real business — it could not read one if it were asked to.
  • Writes do not write. POST /bookings returns a 201 and an id, and creates nothing. No calendar moves, no email is sent.
  • Time is frozen. Every fixture sits on 2026-09-10, a Thursday, so an example in this documentation returns the same thing today and next year. Ask for a different date and you will get the shape of that day — a closed Monday, a day past the booking horizon — with no slots on it.
  • Rate limited. 60 requests a minute per IP, which is a console limit, not the API’s. The real limits are on Errors and limits.

Practising the failures

The responses a developer most needs to have seen are the ones that go wrong, and guessing which inputs produce them is not practice. Each of these is reachable on purpose.

Send thisAnd you get
Any request with a key that is not on this page401 unauthorized — and the refusal lists the keys that do work.
GET /availability with no date422 invalid_request, with details naming the parameter.
A serviceId belonging to one of the other three businesses404 not_found. The API will not confirm that another tenant’s record exists.
POST /bookings at 2026-09-10T01:30:00.000Z409 conflict with the warnings. Resend it with force: true and it goes through.
Cancelling the same booking twice409 conflict. Cancelling is refused rather than silently repeated, so it is safe to retry.
GET /bookings over a range longer than 62 days422 invalid_request, naming the maximum.
The documented 409
{
  "error": {
    "code": "conflict",
    "message": "That time cannot be booked: Already booked at this time (appointment #1100)",
    "details": {
      "reason": "staff_busy",
      "forceable": true,
      "parameter": "startsAt",
      "warnings": [
        {
          "kind": "staff_busy",
          "message": "Already booked at this time (appointment #1100)",
          "conflictingAppointmentId": 1100
        }
      ]
    }
  },
  "sandbox": "Sandbox data. These are fixtures for a made-up business: nothing here is a real booking, and nothing you send is stored."
}

The one that catches everybody

The barbershop is in Sydney, ten hours ahead of UTC. Its 9:00 AM appointment on 2026-09-10 carries an ISO instant on the previous calendar date, because that is what 9:00 AM in Sydney is. Every timestamp this API returns is UTC; every date parameter is read in the business’s own timezone. Those are not the same clock, and an integration that groups bookings by startsAt.slice(0, 10) quietly loses a day’s work for every business far enough east.

Anchor & Blade, one Thursday
{
  "timezone": "Australia/Sydney",
  "requestedDate": "2026-09-10",
  "firstBooking": {
    "startsAt": "2026-09-09T23:00:00.000Z",
    "localTime": "9:00 AM Thursday"
  }
}

Moving to the real API

Two changes and nothing else: swap /api/sandbox/v1 for /api/v1, and swap the demo key for one the business owner minted in Settings → API and MCP. Ids, currencies and timezones will be theirs rather than these, which is why GET /me is the first call to make. The quickstart walks the rest.