Documentation

Latchley Platform

Dashboard flows, REST API reference, and integration guide for the Latchley booking system. The API works for any booking domain — short-term rentals, golf simulators, clinics, gyms, co-working spaces, and more.

Overview

Latchley provides a universal booking backend. Hosts configure custom fields per property to collect whatever information their booking flow needs. External booking forms integrate via the REST API using per-property API keys.

FlowBest forHost effortGuest effort
Flow A — Invitation LinkHosts who want to verify guest details before granting accessLow — sends a link and approvesMedium — fills a short form
Flow B — Manual CreationHosts who already have all guest details and want instant code generationMedium — enters all detailsNone — just receives the code
REST APIThird-party booking platforms with their own booking form and flowNone — fully automatedNone — form is on your site

Flow A — Guest Invitation Link

Coming soon

Flow A is planned but not yet built. When live, the host will send a secure invitation link to the guest. The guest fills a short form; the host reviews and approves; Latchley generates a time-limited PIN and emails it to the guest automatically. The steps below describe the intended design.

StepWhoActionDetail
1HostClicks Create Code → selects Flow AFrom the main dashboard
2HostEnters guest email addressOptionally adds a personal message
3PlatformSends invitation email to guestEmail contains a secure unique link — valid for 48 hours
4GuestOpens link and fills out formName, phone number, booking confirmation reference, expected arrival time
5HostReceives notificationDashboard shows pending invitation — host reviews guest details
6HostApproves or declinesOne-click approve or decline from dashboard
7PlatformGenerates unique PINCalls Pi bridge, creates time-limited code for the confirmed dates
8GuestReceives confirmation emailContains PIN, property address, and instructions

Flow B — Manual Code Creation

Coming soon

Flow B is planned but not yet built. When live, the host will enter all guest details directly in the dashboard and generate a PIN immediately without waiting for guest input. The steps below describe the intended design.

StepWhoActionDetail
1HostClicks Create Code → selects Flow BFrom the main dashboard
2HostFills in guest detailsFirst name, last name, email address, phone number (optional)
3HostSets booking windowStart date and time, end date and time
4HostClicks Send CodeOne click — no further confirmation step required
5PlatformGenerates unique PIN instantlyCalls Pi bridge, creates time-limited code, stores in Supabase
6GuestReceives confirmation emailContains PIN, property address, and instructions

Active Codes Dashboard View

Coming soon

The access codes dashboard view is planned but not yet built. It will show active and upcoming PINs as cards and past codes in a table. The mockup below shows the intended design.

Active & Upcoming — intended card view

14
Active

847 291

14 Maple St

John Smith

01 Jun03 Jun

2 nights
Main House
AP
Upcoming

293 847

Apt 2B

Sarah Jones

05 Jun07 Jun

2 nights
Apartment
ST
Expiring today

651 029

Studio, BR

Emma Wilson

30 May31 May

1 night
Studio Flat

Booking System API

The Latchley Booking System API lets any external booking form collect reservations, check availability, and retrieve the form schema — all scoped to a specific property via an API key. It is industry-agnostic: a golf venue, a therapy clinic, a co-working space, and a holiday let all use the same endpoints and configure their own fields in the dashboard.

EndpointPurpose
GET /api/v1/field-definitionsGet the full form schema — core fields config + custom fields
GET /api/v1/resourcesList bookable resources (bays, rooms, slots) for this property
GET /api/v1/availabilityGet booked windows per resource for a given day; check a specific time window
POST /api/v1/bookingsSubmit a booking — validates fields, checks availability, fires confirmation email
GET /api/v1/bookingsList bookings (paginated) — for reading back into your own system

API keys are generated in the Latchley dashboard under Booking System → API Keys. Each key is scoped to a single property.

Authentication

Pass your API key as a Bearer token on every request.

Authorization: Bearer latchley_live_abc123xyz456...
Key prefixEnvironmentBehaviour
latchley_live_ProductionCreates real bookings and sends confirmation emails to guests
latchley_test_SandboxValidates the request fully but writes nothing — safe for development and testing

A 401 INVALID_API_KEY response means the key is missing, invalid, or revoked. A 429 RATE_LIMIT_EXCEEDED means you have sent more than 100 requests in the current hour window.

GET /api/v1/field-definitions — Form Schema

Returns everything your booking form needs to render itself: which of the three core contact fields are enabled and required for this property, plus the ordered list of custom fields. Call this once on page load and build your form from the response.

GET https://your-latchley-domain.com/api/v1/field-definitions
Authorization: Bearer latchley_live_abc123xyz456

Response (200)

{
  "ok": true,
  "core_fields": {
    "guest_name":  { "enabled": true, "required": true  },
    "guest_email": { "enabled": true, "required": true  },
    "guest_phone": { "enabled": true, "required": false }
  },
  "custom_fields": [
    {
      "field_key":  "booking_date",
      "label":      "Booking date",
      "type":       "date",
      "required":   true,
      "options":    null,
      "sort_order": 0
    },
    {
      "field_key":  "session_length",
      "label":      "Session length",
      "type":       "dropdown",
      "required":   true,
      "options":    ["30 min", "1 hour", "2 hours"],
      "sort_order": 1
    },
    {
      "field_key":  "special_requests",
      "label":      "Special requests",
      "type":       "textarea",
      "required":   false,
      "options":    null,
      "sort_order": 2
    }
  ]
}
FieldDescription
core_fieldsPer-property config for the three fixed contact fields. Render each enabled field; enforce required ones on submit.
custom_fieldsOrdered array of custom fields defined in the dashboard. Render in sort_order. See Field Types Reference for how to render each type.

Field Types Reference

The type field on each custom field tells you how to render the input and what value format to send on submission.

typeRender asValue formatValidation
textSingle-line text inputstringAny non-empty string when required
textareaMulti-line text areastringAny non-empty string when required
numberNumber inputnumber or stringMust parse as a finite number
dateDate pickerYYYY-MM-DD stringMust be a parseable date
timeTime pickerHH:MM string (24h)Must match HH:MM, e.g. 09:30 or 14:00
dropdownSelect / radio groupstringMust be one of the values in options[]
checkboxCheckbox / boolean togglebooleanMust be exactly true or false (not a string)

Checkbox note: Send true or false as JSON booleans, not the strings "true" / "false". The API will reject string booleans.

GET /api/v1/resources — Bookable Resources

Returns the active bookable units for this property — golf bays, treatment rooms, appointment slots, courts, etc. Only needed if your booking form lets the guest pick a specific resource. If you do not show resource selection, the API will auto-assign a free one on submit.

GET https://your-latchley-domain.com/api/v1/resources
Authorization: Bearer latchley_live_abc123xyz456

Response (200)

{
  "ok": true,
  "resources": [
    { "id": "uuid-bay-1", "name": "Bay 1", "sort_order": 0 },
    { "id": "uuid-bay-2", "name": "Bay 2", "sort_order": 1 },
    { "id": "uuid-bay-3", "name": "Bay 3", "sort_order": 2 }
  ]
}

If the property has no resources configured, the array is empty and the API will not apply availability checking to bookings.

GET /api/v1/availability — Live Availability

Returns the full availability picture for a given day: the property's operating hours, whether the date is blocked, and each resource's booked windows and blocked status. Use this to render a live availability calendar on your booking form.

Query parameters

ParameterFormatRequiredDescription
dateYYYY-MM-DDYesThe day to check
resource_idUUIDNoFilter to a single resource
slot_startISO 8601NoProposed window start — include with slot_end to get a window check
slot_endISO 8601NoProposed window end

Example — fetch a full day

GET /api/v1/availability?date=2026-07-15
Authorization: Bearer latchley_live_abc123xyz456

Response (200)

{
  "ok": true,
  "property_hours": {
    "open":   "09:00",
    "close":  "22:00",
    "closed": false
  },
  "property_closed": false,
  "resources": [
    {
      "id":      "uuid-bay-1",
      "name":    "Bay 1",
      "blocked": false,
      "booked_windows": [
        { "slot_start": "2026-07-15T09:00:00Z", "slot_end": "2026-07-15T11:00:00Z" }
      ]
    },
    {
      "id":      "uuid-bay-2",
      "name":    "Bay 2",
      "blocked": true,
      "booked_windows": []
    }
  ],
  "window_check": null
}
FieldDescription
property_hoursOperating hours config for this day of week — open, close (HH:MM), and closed flag
property_closedTrue if this specific date has been manually blocked in the dashboard (one-off closure)
resource.blockedTrue if this resource has been individually blocked on this date in the dashboard

A date is fully unavailable when property_hours.closed is true or property_closed is true. A resource is unavailable when its blocked flag is true. Your form should exclude those from the slot picker — the booking API will also enforce them server-side.

Example — check a specific window

GET /api/v1/availability?date=2026-07-15&slot_start=2026-07-15T14:00:00Z&slot_end=2026-07-15T16:00:00Z
Authorization: Bearer latchley_live_abc123xyz456

Window check in response

{
  "ok": true,
  "property_hours":  { "open": "09:00", "close": "22:00", "closed": false },
  "property_closed": false,
  "resources": [ ... ],
  "window_check": {
    "slot_start":        "2026-07-15T14:00:00Z",
    "slot_end":          "2026-07-15T16:00:00Z",
    "total_resources":   3,
    "active_resources":  2,
    "free_count":        1,
    "available":         true
  }
}

active_resources is the count excluding blocked resources. free_count is active resources with no conflicting booking. available is false if the property is closed or free_count is zero. The authoritative check still runs on POST /api/v1/bookings — always handle 409.

POST /api/v1/bookings — Submit Booking

Creates a booking. Validates all core and custom fields, checks resource availability if slot times are provided, assigns a resource automatically, and fires a confirmation email to the guest.

Request body

FieldTypeRequiredDescription
guest_namestringPer property configGuest full name
guest_emailstringPer property configGuest email — confirmation email sent here
guest_phonestringPer property configGuest phone number
slot_startISO 8601NoBooking window start (UTC). Required together with slot_end to enable availability checking.
slot_endISO 8601NoBooking window end (UTC). Must be after slot_start.
resource_idUUIDNoSpecific resource to book. If omitted, the API auto-assigns the first free resource.
custom_dataobjectNoKey/value pairs for each custom field, keyed by field_key. Required custom fields must be present.

slot_start / slot_end vs date/time custom fields

Use slot_start and slot_end when your booking occupies a time window that must not overlap with another booking on the same resource — golf bays, treatment rooms, appointment slots. The API will enforce availability and return 409 SLOT_UNAVAILABLE if the window is taken.

Use date or time custom fields when you need to record a date or time for display purposes only — e.g. a preferred check-in time, a delivery date — and you do not need the platform to enforce conflicts. Custom fields are stored in custom_data and validated for format but never checked against other bookings.

Example — golf simulator with slot

POST https://your-latchley-domain.com/api/v1/bookings
Authorization: Bearer latchley_live_abc123xyz456
Content-Type: application/json

{
  "guest_name":  "Sarah Jones",
  "guest_email": "sarah@example.com",
  "guest_phone": "+447700900123",
  "slot_start":  "2026-07-15T14:00:00Z",
  "slot_end":    "2026-07-15T16:00:00Z",
  "custom_data": {
    "session_length":    "2 hours",
    "special_requests":  "Left-handed clubs please",
    "terms_accepted":    true
  }
}

Example — simple booking without slot times

POST https://your-latchley-domain.com/api/v1/bookings
Authorization: Bearer latchley_live_abc123xyz456
Content-Type: application/json

{
  "guest_name":  "James Miller",
  "guest_email": "james@example.com",
  "custom_data": {
    "appointment_date": "2026-07-20",
    "appointment_time": "10:30",
    "service":          "Initial Consultation"
  }
}

Success response (201)

{
  "ok": true,
  "booking": {
    "id":                "uuid",
    "booking_reference": "LTH-X7KP2M",
    "property_id":       "uuid",
    "guest_name":        "Sarah Jones",
    "guest_email":       "sarah@example.com",
    "guest_phone":       "+447700900123",
    "slot_start":        "2026-07-15T14:00:00Z",
    "slot_end":          "2026-07-15T16:00:00Z",
    "resource_id":       "uuid-bay-1",
    "custom_data": {
      "session_length":   "2 hours",
      "special_requests": "Left-handed clubs please",
      "terms_accepted":   true
    },
    "created_at": "2026-07-10T11:22:33Z",
    "updated_at": "2026-07-10T11:22:33Z"
  }
}

Test key response (201)

{
  "ok":      true,
  "test":    true,
  "message": "Test mode: request validated successfully. No booking was created.",
  "booking": { ... }
}

Auto-assignment: When you pass slot_start/slot_end without resource_id, the API picks the lowest-sort-order free resource and records it on the booking. Pass resource_id to book a specific resource.

Confirmation email: Sent automatically to guest_email on every successful live booking. Includes booking reference, slot window (if any), and all submitted custom field values.

Race condition: If two requests submit for the same slot simultaneously, one will receive a 409 SLOT_UNAVAILABLE. Always handle this and prompt the user to pick another time.

GET /api/v1/bookings — List Bookings

Returns a paginated list of bookings for the property. Useful for syncing bookings into your own system or displaying a booking history.

GET https://your-latchley-domain.com/api/v1/bookings?page=1&limit=20
Authorization: Bearer latchley_live_abc123xyz456
ParameterDefaultMaxDescription
page1Page number (1-based)
limit20100Results per page

Response (200)

{
  "ok": true,
  "bookings": [ { ... }, { ... } ],
  "pagination": {
    "page":  1,
    "limit": 20,
    "total": 84,
    "pages": 5
  }
}

Error Codes

All error responses follow the same shape:

{ "ok": false, "error": "ERROR_CODE", "message": "Human-readable description" }
HTTPError CodeDescription
400MISSING_FIELDSA required core field is missing or invalid
400INVALID_SLOTslot_end is not after slot_start, timestamps are malformed, or the slot falls outside the property's operating hours
401INVALID_API_KEYAPI key missing, invalid, or revoked
409SLOT_UNAVAILABLEThe property is closed on the requested date or day of week; the requested resource is blocked or already booked; or all resources are full when auto-assigning
409INVALID_RESOURCEThe resource_id supplied does not belong to this property
422VALIDATION_ERRORA custom field value failed type or options validation
429RATE_LIMIT_EXCEEDEDMore than 100 requests in the current hour window
500SERVER_ERRORInternal error — contact support@latchley.io

Rate Limits & Best Practices

LimitValueNotes
Requests per hour100Per API key. Contact support for higher limits.
Bookings per page100Maximum value for the limit parameter on GET /v1/bookings.
TimestampsUTCAll slot_start and slot_end values must be UTC ISO 8601.
  • Call GET /v1/field-definitions once on page load and cache it — it only changes when the property owner edits their fields in the dashboard.
  • Call GET /v1/availability when the user picks a date, not on every keystroke — it triggers a database query.
  • Always handle 409 SLOT_UNAVAILABLE gracefully. Show a 'that slot was just taken — please choose another time' message rather than a generic error.
  • Use a latchley_test_ key during development — it runs full validation and returns a realistic response without writing any data or sending emails.
  • The booking_reference in the response (e.g. LTH-X7KP2M) is the guest-facing identifier. Store it if you need to correlate Latchley bookings with your own records.

Webhooks

Planned

A future release will add webhook support so Latchley can push booking events to your platform — new booking created, booking updated, slot conflict resolved. Webhook endpoint configuration will be available in the host dashboard. Contact support@latchley.io to register early interest.

Questions? Email support@latchley.io