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.
| Flow | Best for | Host effort | Guest effort |
|---|---|---|---|
| Flow A — Invitation Link | Hosts who want to verify guest details before granting access | Low — sends a link and approves | Medium — fills a short form |
| Flow B — Manual Creation | Hosts who already have all guest details and want instant code generation | Medium — enters all details | None — just receives the code |
| REST API | Third-party booking platforms with their own booking form and flow | None — fully automated | None — form is on your site |
Flow A — Guest Invitation Link
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.
| Step | Who | Action | Detail |
|---|---|---|---|
| 1 | Host | Clicks Create Code → selects Flow A | From the main dashboard |
| 2 | Host | Enters guest email address | Optionally adds a personal message |
| 3 | Platform | Sends invitation email to guest | Email contains a secure unique link — valid for 48 hours |
| 4 | Guest | Opens link and fills out form | Name, phone number, booking confirmation reference, expected arrival time |
| 5 | Host | Receives notification | Dashboard shows pending invitation — host reviews guest details |
| 6 | Host | Approves or declines | One-click approve or decline from dashboard |
| 7 | Platform | Generates unique PIN | Calls Pi bridge, creates time-limited code for the confirmed dates |
| 8 | Guest | Receives confirmation email | Contains PIN, property address, and instructions |
Flow B — Manual Code Creation
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.
| Step | Who | Action | Detail |
|---|---|---|---|
| 1 | Host | Clicks Create Code → selects Flow B | From the main dashboard |
| 2 | Host | Fills in guest details | First name, last name, email address, phone number (optional) |
| 3 | Host | Sets booking window | Start date and time, end date and time |
| 4 | Host | Clicks Send Code | One click — no further confirmation step required |
| 5 | Platform | Generates unique PIN instantly | Calls Pi bridge, creates time-limited code, stores in Supabase |
| 6 | Guest | Receives confirmation email | Contains PIN, property address, and instructions |
Active Codes Dashboard View
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
847 291
14 Maple St
John Smith
01 Jun — 03 Jun
293 847
Apt 2B
Sarah Jones
05 Jun — 07 Jun
651 029
Studio, BR
Emma Wilson
30 May — 31 May
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.
| Endpoint | Purpose |
|---|---|
GET /api/v1/field-definitions | Get the full form schema — core fields config + custom fields |
GET /api/v1/resources | List bookable resources (bays, rooms, slots) for this property |
GET /api/v1/availability | Get booked windows per resource for a given day; check a specific time window |
POST /api/v1/bookings | Submit a booking — validates fields, checks availability, fires confirmation email |
GET /api/v1/bookings | List 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 prefix | Environment | Behaviour |
|---|---|---|
latchley_live_ | Production | Creates real bookings and sends confirmation emails to guests |
latchley_test_ | Sandbox | Validates 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
}
]
}| Field | Description |
|---|---|
core_fields | Per-property config for the three fixed contact fields. Render each enabled field; enforce required ones on submit. |
custom_fields | Ordered 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.
| type | Render as | Value format | Validation |
|---|---|---|---|
text | Single-line text input | string | Any non-empty string when required |
textarea | Multi-line text area | string | Any non-empty string when required |
number | Number input | number or string | Must parse as a finite number |
date | Date picker | YYYY-MM-DD string | Must be a parseable date |
time | Time picker | HH:MM string (24h) | Must match HH:MM, e.g. 09:30 or 14:00 |
dropdown | Select / radio group | string | Must be one of the values in options[] |
checkbox | Checkbox / boolean toggle | boolean | Must 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
| Parameter | Format | Required | Description |
|---|---|---|---|
date | YYYY-MM-DD | Yes | The day to check |
resource_id | UUID | No | Filter to a single resource |
slot_start | ISO 8601 | No | Proposed window start — include with slot_end to get a window check |
slot_end | ISO 8601 | No | Proposed 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
}| Field | Description |
|---|---|
property_hours | Operating hours config for this day of week — open, close (HH:MM), and closed flag |
property_closed | True if this specific date has been manually blocked in the dashboard (one-off closure) |
resource.blocked | True 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
| Field | Type | Required | Description |
|---|---|---|---|
guest_name | string | Per property config | Guest full name |
guest_email | string | Per property config | Guest email — confirmation email sent here |
guest_phone | string | Per property config | Guest phone number |
slot_start | ISO 8601 | No | Booking window start (UTC). Required together with slot_end to enable availability checking. |
slot_end | ISO 8601 | No | Booking window end (UTC). Must be after slot_start. |
resource_id | UUID | No | Specific resource to book. If omitted, the API auto-assigns the first free resource. |
custom_data | object | No | Key/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
| Parameter | Default | Max | Description |
|---|---|---|---|
page | 1 | — | Page number (1-based) |
limit | 20 | 100 | Results 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" }| HTTP | Error Code | Description |
|---|---|---|
| 400 | MISSING_FIELDS | A required core field is missing or invalid |
| 400 | INVALID_SLOT | slot_end is not after slot_start, timestamps are malformed, or the slot falls outside the property's operating hours |
| 401 | INVALID_API_KEY | API key missing, invalid, or revoked |
| 409 | SLOT_UNAVAILABLE | The 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 |
| 409 | INVALID_RESOURCE | The resource_id supplied does not belong to this property |
| 422 | VALIDATION_ERROR | A custom field value failed type or options validation |
| 429 | RATE_LIMIT_EXCEEDED | More than 100 requests in the current hour window |
| 500 | SERVER_ERROR | Internal error — contact support@latchley.io |
Rate Limits & Best Practices
| Limit | Value | Notes |
|---|---|---|
| Requests per hour | 100 | Per API key. Contact support for higher limits. |
| Bookings per page | 100 | Maximum value for the limit parameter on GET /v1/bookings. |
| Timestamps | UTC | All 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
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