Reference
Setora API 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
Scope required · read
Response fields
| Name | Type | Description |
|---|---|---|
id | string · UUID | Unique identifier for the location. |
slug | string | URL-safe identifier used in API paths (e.g. /locations/soho/…). |
name | string | Display name of the location. |
timezone | string · IANA | IANA timezone identifier. All availability slots for this location are returned in this timezone. |
street_address | string | Street-level address line. |
city | string | City or town. |
postal_code | string | Postal or ZIP code. |
country | string · ISO 3166-1 | Two-letter country code. |
{
"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
Scope required · read
Response fields
| Name | Type | Description |
|---|---|---|
id | string · UUID | Unique identifier for the service. |
name | string | Display name of the service. |
duration_minutes | integer | Length of the service in minutes. |
price | object | Contains amount (integer, minor units) and currency (ISO 4217). May differ from the org-wide default if a per-location override is set. |
deposit | object | null | Deposit required to confirm a booking. null when no deposit is needed. Contains amount and currency. |
category | string | Service category label set by the shop. |
online_bookable | boolean | Whether the service is available for online booking. Services with false can only be booked in-person. |
staff | array | Staff members who can deliver this service. Each object contains id (UUID) and name. |
{
"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
Scope required · read
Response fields
| Name | Type | Description |
|---|---|---|
id | string · UUID | Unique identifier for the staff member. |
name | string | Display name of the staff member. |
service_ids | string[] | UUIDs of the services this staff member can deliver. Use these to filter availability. |
{
"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 Booking Page, so deposit rules and lead-time windows are honoured automatically.
GET/locations/{slug}/availability
Scope required · read
| Name | Type | Description |
|---|---|---|
service_idsrequired | string · comma-separated UUIDs | One or more service IDs. Determines total slot length and which staff are eligible. For multi-service bookings, pass all service IDs. |
daterequired | string · YYYY-MM-DD | Local date to start the search from, in the location's timezone. |
days | integer · 1 – 14 | Number of days to search forward from date. Defaults to 1. |
staff_id | string | Restrict results to a specific staff member. Omit for any-staff availability. |
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
| Name | Type | Description |
|---|---|---|
location_slug | string | The location these slots belong to. |
timezone | string · IANA | IANA timezone of the location. All slot times are expressed in this timezone. |
service_ids | string[] | The service IDs that were queried. |
duration | object | Total slot duration. Contains minutes (integer) and display (human-readable string). |
slots | array | Available time slots. Each contains starts_at, ends_at (ISO 8601 with UTC offset), and staff_id (UUID of the assigned staff member). |
{
"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
Scope required · read
| Name | Type | Description |
|---|---|---|
phone | string · E.164 | The full phone number including country code. UK numbers should be sent as +44…, not 0… |
email | string | Email address to search for. At least one of phone or email is required. |
Response fields
| Name | Type | Description |
|---|---|---|
id | string · UUID | Unique identifier for the client. |
first_name | string | Client's first name. |
last_name | string | Client's last name. |
phone | string · E.164 | Phone number including country code. |
email | string | Email address. |
marketing_consent | boolean | Whether the client has opted in to marketing communications. |
created_at | string · ISO 8601 | When the client record was created (UTC). |
{
"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"
}
}{
"error": {
"code": "not_found",
"message": "No client found matching the provided phone or email."
}
}POST/clients
Scope required · read_write
| Name | Type | Description |
|---|---|---|
first_namerequired | string | First name. 1 – 60 characters. |
last_namerequired | string | Last name. 1 – 60 characters. |
phone | string · E.164 | Phone number, used for SMS reminders. At least one of phone or email is required. |
email | string | Email address, used for email reminders and receipts. At least one of phone or email is required. |
marketing_consent | boolean | Defaults to false. Only set to true if the client has actively opted in. |
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"
}'{
"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"
}
}{
"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"
}
}Bookings
Create, fetch, reschedule and cancel bookings. POST /bookings supports an Idempotency-Key for safe retries.
| Booking statuses | Description |
|---|---|
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. |
pending_confirmation | Slot is held while the customer completes a required step on the Booking Page (deposit, Booking Protection card setup, or both). The hold expires after 15 minutes if not finalised. See pending_confirmation.requirements on the booking response. |
confirmed | All required steps completed (or none 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, and the Booking Protection no-show fee may apply if the shop has it enabled. |
cancelled | The booking was cancelled. Whether the deposit was refunded depends on the shop's cancellation policy window. A late cancellation may trigger the Booking Protection late-cancel fee if the shop has it enabled. |
expired | A pending_confirmation hold timed out. The slot is released, no cancellation email is sent, and no charge is taken. Expired bookings do not block availability. |
POST/bookings
Scope required · read_write
| Name | Type | Description |
|---|---|---|
location_slugrequired | string | The slug of the location the booking is for. |
service_idsrequired | string[] | One or more service IDs. Each must be bookable online at the location. |
staff_id | string | Optional. Omit to use any-staff assignment. Must be eligible for all requested services. |
client_idrequired | string | An existing client. Use POST /clients first if needed. |
starts_atrequired | string · ISO 8601 | Slot start time. Must match a slot returned by /availability. |
notes | string | Optional. Free text shown to staff in the Hub. |
checkout_success_url | string | Optional. Where to redirect the customer after a successful deposit payment. Supports {booking_reference} and {location_slug} placeholders. Only used when a deposit is required. Defaults to the Setora confirmation page if omitted. |
checkout_cancel_url | string | Optional. Where to redirect the customer if they abandon the deposit checkout. Only used when a deposit is required. Defaults to the Booking Page if omitted. |
When a deposit is required, Setora creates a Stripe hosted checkout session and returns a payment_url. Direct your customer to that URL to collect payment. Once paid, Stripe confirms payment via webhook and the booking status moves to confirmed. The customer is then redirected to your checkout_success_url. Use the {booking_reference} placeholder in your URL so you can identify which booking was paid, for example https://yourapp.com/bookings/confirmed?ref={booking_reference}.
Response fields
| Name | Type | Description |
|---|---|---|
reference | string | Human-readable booking reference. |
status | string | Current booking status. See the status table above for all possible values. |
deposit | object | Present when a deposit is required. Contains amount (minor units) and currency. |
payment_url | string · 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. |
notes | string | null | Free text passed at creation. Shown to staff in the Hub. |
created_at | string · ISO 8601 | When the booking was created (UTC). |
updated_at | string · ISO 8601 | When the booking was last modified (UTC). |
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",
"checkout_success_url": "https://yourapp.com/bookings/confirmed?ref={booking_reference}",
"checkout_cancel_url": "https://yourapp.com/bookings/new"
}'{
"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_...",
"notes": null,
"created_at": "2026-04-30T15:01:22Z",
"updated_at": "2026-04-30T15:01:22Z"
}
}GET/bookings/{reference}
Scope required · read
Response fields
| Name | Type | Description |
|---|---|---|
reference | string | Human-readable booking reference (e.g. SET-A1B2C3). |
status | string | Current booking status. See the status table above. |
location_slug | string | The location the booking is at. |
service_ids | string[] | Services included in this booking. |
staff_id | string · UUID | The assigned staff member. |
client_id | string · UUID | The client who made the booking. |
starts_at | string · ISO 8601 | Appointment start time in the location's local timezone. |
ends_at | string · ISO 8601 | Appointment end time in the location's local timezone. |
notes | string | null | Free text passed at creation. Shown to staff in the Hub. |
pending_confirmation | object | null | Present when status is pending_confirmation. Contains requirements (array of strings, e.g. 'deposit', 'protection_setup') and expires_at (ISO 8601). Cleared once the hold is finalised or expires. |
late_cancellation_fee | object | null | Read-only Booking Protection state for the late cancellation fee. See the Booking Protection section below for the object shape. null when the shop does not have Booking Protection enabled for this booking. |
no_show_fee | object | null | Read-only Booking Protection state for the no-show fee. Same shape as late_cancellation_fee. |
created_at | string · ISO 8601 | When the booking was created (UTC). |
updated_at | string · ISO 8601 | When the booking was last modified (UTC). |
{
"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",
"notes": null,
"pending_confirmation": null,
"late_cancellation_fee": {
"status": null,
"currency": "GBP",
"fee_percentage": 50,
"amount_minor": 1250,
"deposit_credit_minor": 0,
"charge_amount_minor": 1250,
"payment_method_saved": true,
"failure_reason": null,
"processed_at": null
},
"no_show_fee": {
"status": null,
"currency": "GBP",
"fee_percentage": 100,
"amount_minor": 2500,
"deposit_credit_minor": 0,
"charge_amount_minor": 2500,
"payment_method_saved": true,
"failure_reason": null,
"processed_at": null
},
"created_at": "2026-04-30T15:01:22Z",
"updated_at": "2026-04-30T15:02:08Z"
}
}PATCH/bookings/{reference}
Scope required · read_write
| Name | Type | Description |
|---|---|---|
starts_atrequired | string · ISO 8601 | The new slot start time. |
staff_id | string | Optional. Reassign to a different eligible staff member. |
POST/bookings/{reference}/cancel
Scope required · read_write
| Name | Type | Description |
|---|---|---|
reason | string | Optional. Free text shown to staff. Useful when an AI agent records the client's stated reason. |
Response fields
| Name | Type | Description |
|---|---|---|
reference | string | The booking reference. |
status | string | Always "cancelled" on success. |
cancelled_at | string · ISO 8601 | When the cancellation was processed (UTC). |
deposit_refunded | boolean | Whether the deposit was refunded, based on the shop's cancellation policy window. |
{
"data": {
"reference": "SET-A1B2C3",
"status": "cancelled",
"cancelled_at": "2026-05-11T18:14:00Z",
"deposit_refunded": false
}
}Booking Protection
Read-only fields on the booking resource describing Booking Protection state. v1 does not provide endpoints to create card setup flows.
BookingProtectionFee fields
The late_cancellation_fee and no_show_fee objects share the same shape. Either may be null when Booking Protection is not enabled for the shop or the booking.
| Name | Type | Description |
|---|---|---|
status | string | null | One of charged, waived, deposit_covered, failed, or null. null means no action has been taken yet (booking is still active or was completed normally). |
currency | string · ISO 4217 | Currency for the fee amounts. |
fee_percentage | number | null | Percentage of the booking's service total used to calculate the fee (e.g. 50 for 50%). Snapshotted from the policy at booking creation. |
amount_minor | integer | Calculated fee in minor currency units, before any deposit credit. Capped at the booking's service total. |
deposit_credit_minor | integer | Portion of amount_minor covered by a retained deposit. Zero if no deposit was retained. |
charge_amount_minor | integer | Net amount Setora attempted (or would attempt) to charge the saved card: amount_minor minus deposit_credit_minor. |
payment_method_saved | boolean | Whether a payment method is currently saved on the booking that could be charged. |
failure_reason | string | null | Stripe failure reason when status is failed. null otherwise. |
processed_at | string · ISO 8601 | null | When the fee was charged, waived, or marked deposit_covered. null while no action has been taken. |
pending_confirmation fields
Present on the booking response when status is pending_confirmation. Cleared (null) once the hold is finalised or expires.
| Name | Type | Description |
|---|---|---|
requirements | string[] | Outstanding steps the customer needs to complete on the Booking Page. Possible values include 'deposit' and 'protection_setup'. Both may appear together. |
expires_at | string · ISO 8601 | When the hold expires (UTC). After this point the booking moves to expired and the slot is released. |
{
"data": {
"reference": "SET-A1B2C3",
"status": "pending_confirmation",
"pending_confirmation": {
"requirements": ["protection_setup"],
"expires_at": "2026-05-12T09:45:00Z"
},
"late_cancellation_fee": {
"status": null,
"currency": "GBP",
"fee_percentage": 50,
"amount_minor": 1250,
"deposit_credit_minor": 0,
"charge_amount_minor": 1250,
"payment_method_saved": false,
"failure_reason": null,
"processed_at": null
},
"no_show_fee": {
"status": null,
"currency": "GBP",
"fee_percentage": 100,
"amount_minor": 2500,
"deposit_credit_minor": 0,
"charge_amount_minor": 2500,
"payment_method_saved": false,
"failure_reason": null,
"processed_at": null
}
}
}