Reference

Endpoint reference

Every endpoint in v1, organised by resource. All paths are relative to the base URL https://rest.setora.co.uk/v1.

Locations, services & staff

Read-only resources that describe what a shop sells, where, and who delivers it. These endpoints are the starting point for any booking flow.

GET/locations

List the locations belonging to the authenticated organisation. Returns active locations only.

Scope required · read

Response fields

NameDescription
  • id

    Unique identifier for the location.

  • slug

    URL-safe identifier used in API paths (e.g. /locations/soho/…).

  • name

    Display name of the location.

  • timezone

    IANA timezone identifier. All availability slots for this location are returned in this timezone.

  • street_address

    Street-level address line.

  • city

    City or town.

  • postal_code

    Postal or ZIP code.

  • country

    Two-letter country code.

json200 OK
{
  "data": [
    {
      "id": "4f8d3a91-2b6c-4e7f-9a01-1c5d8e2f6b3a",
      "slug": "soho",
      "name": "Soho",
      "timezone": "Europe/London",
      "street_address": "12 Berwick Street",
      "city": "London",
      "postal_code": "W1F 0PT",
      "country": "GB"
    }
  ]
}
GET/locations/{slug}/services

List the services bookable at a location, including pricing, duration, deposit requirement, and which staff can deliver each service. Pricing and duration may differ from the org-wide default if a per-location override is set.

Scope required · read

Response fields

NameDescription
  • id

    Unique identifier for the service.

  • name

    Display name of the service.

  • duration_minutes

    Length of the service in minutes.

  • price

    Contains amount (integer, minor units) and currency (ISO 4217). May differ from the org-wide default if a per-location override is set.

  • deposit

    Deposit required to confirm a booking. null when no deposit is needed. Contains amount and currency.

  • category

    Service category label set by the shop.

  • online_bookable

    Whether the service is available for online booking. Services with false can only be booked in-person.

  • staff

    Staff members who can deliver this service. Each object contains id (UUID) and name.

json200 OK
{
  "data": [
    {
      "id": "2a7c9e34-58b1-41f6-8d92-6e4a3b0c7d15",
      "name": "Haircut",
      "duration_minutes": 30,
      "price": { "amount": 2500, "currency": "GBP" },
      "deposit": { "amount": 500, "currency": "GBP" },   // null when no deposit
      "category": "Hair",
      "online_bookable": true,
      "staff": [
        { "id": "9b3e7d2c-4a1f-48e6-bc52-7f0a9d3b1e84", "name": "Kai" },
        { "id": "c1d4f8a2-6e3b-49d7-a085-2b8c5f1e7d39", "name": "Jordan" }
      ]
    }
  ]
}
GET/locations/{slug}/staff

List active staff at a location. Each staff member returns the services they can deliver, so callers can filter availability accordingly.

Scope required · read

Response fields

NameDescription
  • id

    Unique identifier for the staff member.

  • name

    Display name of the staff member.

  • service_ids

    UUIDs of the services this staff member can deliver. Use these to filter availability.

json200 OK
{
  "data": [
    {
      "id": "9b3e7d2c-4a1f-48e6-bc52-7f0a9d3b1e84",
      "name": "Kai",
      "service_ids": ["2a7c9e34-58b1-41f6-8d92-6e4a3b0c7d15", "e8b1d4f2-3a9c-47e5-bd06-4c7f2a1e8b39"]
    }
  ]
}

Availability

Real-time slot lookup. Backed by the same engine as the public booking portal, so deposit rules and lead-time windows are honoured automatically.

GET/locations/{slug}/availability

Return bookable slots for one or more services at a location. Slots are returned in the location's local timezone with a UTC offset.

Scope required · read

NameDescription
  • service_ids

    required

    One or more service IDs. Determines total slot length and which staff are eligible. For multi-service bookings, pass all service IDs.

  • date

    required

    Local date to start the search from, in the location's timezone.

  • days

    Number of days to search forward from date. Defaults to 1.

  • staff_id

    Restrict results to a specific staff member. Omit for any-staff availability.

bashExample request
curl https://rest.setora.co.uk/v1/locations/soho/availability \
  -G \
  --data-urlencode "service_ids=2a7c9e34-58b1-41f6-8d92-6e4a3b0c7d15" \
  --data-urlencode "date=2026-05-12" \
  --data-urlencode "days=3" \
  -H "Authorization: Bearer sk_live_..."

Response fields

NameDescription
  • location_slug

    The location these slots belong to.

  • timezone

    IANA timezone of the location. All slot times are expressed in this timezone.

  • service_ids

    The service IDs that were queried.

  • duration

    Total slot duration. Contains minutes (integer) and display (human-readable string).

  • slots

    Available time slots. Each contains starts_at, ends_at (ISO 8601 with UTC offset), and staff_id (UUID of the assigned staff member).

json200 OK
{
  "data": {
    "location_slug": "soho",
    "timezone": "Europe/London",
    "service_ids": ["2a7c9e34-58b1-41f6-8d92-6e4a3b0c7d15"],
    "duration": { "minutes": 30, "display": "30 mins" },
    "slots": [
      {
        "starts_at": "2026-05-12T09:00:00+01:00",
        "ends_at": "2026-05-12T09:30:00+01:00",
        "staff_id": "9b3e7d2c-4a1f-48e6-bc52-7f0a9d3b1e84"
      },
      {
        "starts_at": "2026-05-12T09:30:00+01:00",
        "ends_at": "2026-05-12T10:00:00+01:00",
        "staff_id": "9b3e7d2c-4a1f-48e6-bc52-7f0a9d3b1e84"
      },
      {
        "starts_at": "2026-05-13T11:00:00+01:00",
        "ends_at": "2026-05-13T11:30:00+01:00",
        "staff_id": "c1d4f8a2-6e3b-49d7-a085-2b8c5f1e7d39"
      }
    ]
  }
}

Clients

Look up a returning client by phone or email, or create a new client record before booking.

GET/clients

Look up a client by phone or email. At least one parameter is required. Returns at most one client per match within an organisation.

Scope required · read

NameDescription
  • phone

    The full phone number including country code. UK numbers should be sent as +44…, not 0…

  • email

    Email address to search for. At least one of phone or email is required.

Response fields

NameDescription
  • id

    Unique identifier for the client.

  • first_name

    Client's first name.

  • last_name

    Client's last name.

  • phone

    Phone number including country code.

  • email

    Email address.

  • marketing_consent

    Whether the client has opted in to marketing communications.

  • created_at

    When the client record was created (UTC).

json200 OK · client found
{
  "data": {
    "id": "5d9f2a18-7c4e-46b3-a821-3e0b4c8d1f72",
    "first_name": "Alex",
    "last_name": "Carter",
    "phone": "+447700900123",
    "email": "alex@example.com",
    "marketing_consent": false,
    "created_at": "2025-11-04T10:22:00Z"
  }
}
json404 Not Found · no match
{
  "error": {
    "code": "not_found",
    "message": "No client found matching the provided phone or email."
  }
}
POST/clients

Create a new client. At least one of phone or email is required. If a matching phone or email already exists in the organisation, the existing client is returned rather than a duplicate created.

Scope required · read_write

NameDescription
  • first_name

    required

    First name. 1 – 60 characters.

  • last_name

    required

    Last name. 1 – 60 characters.

  • phone

    Phone number, used for SMS reminders. At least one of phone or email is required.

  • email

    Email address, used for email reminders and receipts. At least one of phone or email is required.

  • marketing_consent

    Defaults to false. Only set to true if the client has actively opted in.

bashExample request
curl https://rest.setora.co.uk/v1/clients \
  -X POST \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Alex",
    "last_name": "Carter",
    "phone": "+447700900123",
    "email": "alex@example.com"
  }'
json201 Created
{
  "data": {
    "id": "5d9f2a18-7c4e-46b3-a821-3e0b4c8d1f72",
    "first_name": "Alex",
    "last_name": "Carter",
    "phone": "+447700900123",
    "email": "alex@example.com",
    "marketing_consent": false,
    "created_at": "2026-05-01T12:00:00Z"
  }
}

Bookings

Create, fetch, reschedule and cancel bookings. POST /bookings supports an Idempotency-Key for safe retries.

Booking statuses
  • pending_payment

    Booking created but a deposit is required. The client should be directed to the payment_url. The booking will move to confirmed once the deposit is paid.

  • confirmed

    Deposit paid (or no deposit required). The booking is live and visible to staff in the Hub.

  • completed

    The appointment has been fulfilled. Set by staff in the Hub after the service is delivered.

  • no_show

    The client did not attend. Set by staff in the Hub. The deposit is not refunded.

  • cancelled

    The booking was cancelled. Whether the deposit was refunded depends on the shop's cancellation policy window.

POST/bookings

Create a booking. If the service requires a deposit, the response includes a hosted payment_url. The booking is held in pending state until the deposit is paid; pass an Idempotency-Key for safe retries.

Scope required · read_write

NameDescription
  • location_slug

    required

    The slug of the location the booking is for.

  • service_ids

    required

    One or more service IDs. Each must be bookable online at the location.

  • staff_id

    Optional. Omit to use any-staff assignment. Must be eligible for all requested services.

  • client_id

    required

    An existing client. Use POST /clients first if needed.

  • starts_at

    required

    Slot start time. Must match a slot returned by /availability.

  • notes

    Optional. Free text shown to staff in the Hub.

Response fields

NameDescription
  • reference

    Human-readable booking reference.

  • status

    Current booking status. See the status table above for all possible values.

  • deposit

    Present when a deposit is required. Contains amount (minor units) and currency.

  • payment_url

    Present when a deposit is required. A hosted checkout page the client should be directed to in order to pay the deposit. The booking remains in pending_payment until payment completes.

bashExample request
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 '{
    "location_slug": "soho",
    "service_ids": ["2a7c9e34-58b1-41f6-8d92-6e4a3b0c7d15"],
    "client_id": "5d9f2a18-7c4e-46b3-a821-3e0b4c8d1f72",
    "starts_at": "2026-05-12T09:30:00+01:00"
  }'
json201 Created · deposit required
{
  "data": {
    "reference": "SET-A1B2C3",
    "status": "pending_payment",
    "location_slug": "soho",
    "service_ids": ["2a7c9e34-58b1-41f6-8d92-6e4a3b0c7d15"],
    "staff_id": "9b3e7d2c-4a1f-48e6-bc52-7f0a9d3b1e84",
    "client_id": "5d9f2a18-7c4e-46b3-a821-3e0b4c8d1f72",
    "starts_at": "2026-05-12T09:30:00+01:00",
    "ends_at": "2026-05-12T10:00:00+01:00",
    "deposit": { "amount": 500, "currency": "GBP" },
    "payment_url": "https://pay.setora.co.uk/checkout/cs_live_..."
  }
}
GET/bookings/{reference}

Fetch a single booking by reference, including current status and any associated payment URL.

Scope required · read

Response fields

NameDescription
  • reference

    Human-readable booking reference (e.g. SET-A1B2C3).

  • status

    Current booking status. See the status table above.

  • location_slug

    The location the booking is at.

  • service_ids

    Services included in this booking.

  • staff_id

    The assigned staff member.

  • client_id

    The client who made the booking.

  • starts_at

    Appointment start time in the location's local timezone.

  • ends_at

    Appointment end time in the location's local timezone.

  • created_at

    When the booking was created (UTC).

  • updated_at

    When the booking was last modified (UTC).

json200 OK
{
  "data": {
    "reference": "SET-A1B2C3",
    "status": "confirmed",
    "location_slug": "soho",
    "service_ids": ["2a7c9e34-58b1-41f6-8d92-6e4a3b0c7d15"],
    "staff_id": "9b3e7d2c-4a1f-48e6-bc52-7f0a9d3b1e84",
    "client_id": "5d9f2a18-7c4e-46b3-a821-3e0b4c8d1f72",
    "starts_at": "2026-05-12T09:30:00+01:00",
    "ends_at": "2026-05-12T10:00:00+01:00",
    "created_at": "2026-04-30T15:01:22Z",
    "updated_at": "2026-04-30T15:02:08Z"
  }
}
PATCH/bookings/{reference}

Reschedule a booking. The new start must be a valid slot for the same service at the same location. Cancellation policy windows are enforced, and late changes return a 409.

Scope required · read_write

NameDescription
  • starts_at

    required

    The new slot start time.

  • staff_id

    Optional. Reassign to a different eligible staff member.

POST/bookings/{reference}/cancel

Cancel a booking. The shop's cancellation policy controls whether a deposit is refunded. The endpoint never mutates the deposit on its own; it only triggers the policy.

Scope required · read_write

NameDescription
  • reason

    Optional. Free text shown to staff. Useful when an AI agent records the client's stated reason.

Response fields

NameDescription
  • reference

    The booking reference.

  • status

    Always "cancelled" on success.

  • cancelled_at

    When the cancellation was processed (UTC).

  • deposit_refunded

    Whether the deposit was refunded, based on the shop's cancellation policy window.

json200 OK
{
  "data": {
    "reference": "SET-A1B2C3",
    "status": "cancelled",
    "cancelled_at": "2026-05-11T18:14:00Z",
    "deposit_refunded": false
  }
}