Reference

Conventions

The cross-cutting rules that apply to every endpoint: data format, IDs, timezones, idempotency, errors, and rate limits.

Format & content type

Requests and responses are JSON, UTF-8 encoded. Every request that sends a body must include Content-Type: application/json. Responses always include an X-Request-Id header. Quote it when you contact support.

Identifiers

Resource IDs for clients, services, staff, and locations are UUIDs (RFC 4122, lowercase). Bookings use a short, human-readable reference (for example SET-A1B2C3). Locations also expose a unique slug used in URL paths. Treat all identifiers as opaque values.

json
{
  "data": {
    "reference": "SET-A1B2C3",
    "location_slug": "soho",
    "client_id": "5d9f2a18-7c4e-46b3-a821-3e0b4c8d1f72"
  }
}

Timestamps & timezones

Booking and availability times are returned in the location's local timezone, formatted as ISO 8601 with a UTC offset (for example, 2026-05-12T09:30:00+01:00).

System metadata such as created_at and cancelled_at is returned in UTC (Z suffix). The location's IANA timezone is included on every location resource so you can convert if you need to.

Money

Monetary amounts are integers in the smallest currency unit (pence for GBP) alongside an ISO 4217 currency code. Never round on the client. Always do arithmetic in the integer minor unit.

json
{ "amount": 2500, "currency": "GBP" }   // £25.00

Pagination

List endpoints return a data array and, when more pages are available, a next_cursor string. Pass it back as cursor to fetch the next page. Cursors are opaque and time-bound, so don't store them.

json
{
  "data": [ /* … */ ],
  "next_cursor": "eyJrIjoxLCJ0IjoxNzM…"
}

Idempotency

POST /bookings accepts an Idempotency-Key header. Setora stores the response for the key for 24 hours. Replay the same key to receive the same response without creating a duplicate booking. This is essential for AI agents and any client that might retry on a network blip.

bash
curl https://rest.setora.co.uk/v1/bookings \
  -X POST \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: 6d0e2f88-b7e5-4d6a-9f77-0b2bb2c4e9d1" \
  -H "Content-Type: application/json" \
  -d '{ /* … */ }'
  • Use a UUID v4 (or any sufficiently unique string) per logical booking attempt, not per HTTP request.
  • If you replay a key with a different request body, the API returns 409 conflict with code idempotency_key_reused.
  • Keys older than 24 hours are forgotten. After that window, the same key may be reused for a new request.

Errors

Errors share a single envelope. The HTTP status describes the category; error.code is the machine-readable identifier you should branch on.

jsonError envelope
{
  "error": {
    "code": "conflict",
    "message": "The selected slot is no longer available.",
    "param": "starts_at"
  }
}
CodeWhen
  • invalid_request

    Malformed JSON or a parameter failed validation.

  • missing_api_key

    No Authorization header was sent.

  • invalid_api_key

    The API key is unknown, malformed, or has been revoked.

  • insufficient_scope

    The key is valid but its scope does not allow this action.

  • not_found

    The resource does not exist or is not visible to your org.

  • conflict

    The slot is no longer available, or a cancellation/reschedule violates the shop's policy window.

  • idempotency_key_reused

    The Idempotency-Key was replayed with a different request body.

  • rate_limited

    Too many requests for this key. Honour the Retry-After header.

  • server_error

    Something went wrong on Setora's side. Safe to retry idempotent requests.

Rate limits

The default ceiling is 120 requests per minute per API key. Limits apply per key, not per IP. Bursting is allowed within the window. Once exhausted, requests return 429 with code rate_limited.

Need more than the default? Email hello@setora.co.uk with your projected request volume and we can raise the ceiling on a per-key basis.

Every response includes the following headers:

  • X-RateLimit-Limit · the ceiling for the current window
  • X-RateLimit-Remaining · requests left in the window
  • X-RateLimit-Reset · UNIX timestamp when the window resets
  • Retry-After · only on 429, seconds to wait

Versioning

The API is versioned in the URL path (/v1). We will not make backwards-incompatible changes within a major version. Additive changes (new endpoints, new optional fields, new error codes) can land at any time, so design clients to tolerate unknown fields.

Webhooks are not yet supported but are coming soon, alongside per-resource scopes. Breaking changes, if they ever happen, will ship behind a new /v2 path with a 12-month deprecation window for v1.