{"openapi":"3.1.0","info":{"title":"BarberFlow API","version":"1.0.0","description":"Book appointments at a BarberFlow barbershop from the shop's own website. BarberFlow stays the system of record: bookings made here appear in the shop's calendar and get the same confirmations and reminders as any other booking. Every request sends the shop's publishable key as `Authorization: Bearer bf_pk_…`; the key is safe in browser code and only works from the websites the shop allows. Bookings are pay in person; for card payment, send customers to the `bookingUrl` the API returns. Rate limits apply per visitor IP address and per API key, and failed key checks are limited to 30 a minute per IP. `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds) come from the endpoint's main limit, the first one listed for it; a 429 carries `Retry-After`."},"servers":[{"url":"https://api.barberflow.com/v1"}],"paths":{"/business":{"get":{"operationId":"getBusiness","summary":"Get the business","description":"The shop behind the API key: name, logo, contact details, cancellation policy, payment options and the hosted booking link. API bookings are pay in person, so they need `payment.payInPerson` true and `payment.payInPersonRequiresCard` false; otherwise send customers to `bookingUrl`.\n\nRate limit: 120 requests a minute per visitor IP address; 600 requests a minute per API key.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Business"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/locations":{"get":{"operationId":"listLocations","summary":"List locations","description":"Every location taking online bookings, with its address, time zone, weekly hours, verification setting and hosted booking link. When `address.hidden` is true the shop keeps its street address private.\n\nRate limit: 120 requests a minute per visitor IP address; 600 requests a minute per API key.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationList"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/locations/{locationId}/services":{"get":{"operationId":"listServices","summary":"List services at a location","description":"The bookable services at a location. Each entry is one option of a service (for example Haircut, Senior barber); its `id` is what `serviceIds` takes everywhere. Add-ons are nested under the service they extend and can be added to `serviceIds` with it. `priceCents` is the listed price: shop pricing rules can change it, so the booking carries the final price. Only services with `allowPayInPerson` can be booked through this API.\n\nRate limit: 120 requests a minute per visitor IP address; 600 requests a minute per API key.","security":[{"apiKey":[]}],"parameters":[{"name":"locationId","in":"path","required":true,"description":"An opaque id.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceList"}}}},"400":{"description":"`VALIDATION_ERROR`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LOCATION_NOT_BOOKABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/locations/{locationId}/team":{"get":{"operationId":"listTeam","summary":"List the team at a location","description":"Barbers taking online bookings at a location. With `serviceIds`, only barbers who perform every listed service come back.\n\nRate limit: 120 requests a minute per visitor IP address; 600 requests a minute per API key.","security":[{"apiKey":[]}],"parameters":[{"name":"locationId","in":"path","required":true,"description":"An opaque id.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},{"name":"serviceIds","in":"query","required":false,"description":"Only list barbers who perform every one of these services.","schema":{"examples":["cm1haircut,cm1beard"],"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}(,[A-Za-z0-9_-]{1,64}){0,9}$"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamList"}}}},"400":{"description":"`VALIDATION_ERROR`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LOCATION_NOT_BOOKABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/locations/{locationId}/availability":{"get":{"operationId":"getAvailability","summary":"Get open times","description":"Open start times for the listed services over up to 14 days, day by day in the location's time zone. Without `barberId`, every barber who can do the services is merged and each slot lists the barbers free at that time; pick one of them as `barberId` when booking. Days past the shop's booking horizon come back empty. Send the customer token too when the customer has verified, so the times fit any service lengths the shop set for that customer.\n\nRate limit: 60 requests a minute per visitor IP address; 240 requests a minute per API key.","security":[{"apiKey":[]},{"apiKey":[],"customerToken":[]}],"parameters":[{"name":"locationId","in":"path","required":true,"description":"An opaque id.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},{"name":"serviceIds","in":"query","required":true,"description":"Between 1 and 10 service ids, comma-separated. Service ids are the `id` of entries from the services endpoint.","schema":{"examples":["cm1haircut,cm1beard"],"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}(,[A-Za-z0-9_-]{1,64}){0,9}$"}},{"name":"barberId","in":"query","required":false,"description":"One barber. Leave it out to merge every barber who can do the services.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},{"name":"from","in":"query","required":false,"description":"First day, YYYY-MM-DD. Defaults to today at the location.","schema":{"examples":["2026-10-20"],"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Last day, YYYY-MM-DD, at most 13 days after `from`. Defaults to `from` plus 13 days.","schema":{"examples":["2026-10-20"],"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Availability"}}}},"400":{"description":"`VALIDATION_ERROR`, `INVALID_RANGE`, `RANGE_TOO_LARGE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`, `CUSTOMER_TOKEN_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LOCATION_NOT_BOOKABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`BARBER_NOT_AT_STORE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/locations/{locationId}/availability/next":{"get":{"operationId":"getNextAvailability","summary":"Get the next opening","description":"The earliest open start time for the listed services with any compatible barber (or the one in `barberId`), searched up to the shop's booking horizon, or `null` when nobody has one. Send the customer token too when the customer has verified, so the time fits any service lengths the shop set for that customer.\n\nRate limit: 60 requests a minute per visitor IP address; 240 requests a minute per API key.","security":[{"apiKey":[]},{"apiKey":[],"customerToken":[]}],"parameters":[{"name":"locationId","in":"path","required":true,"description":"An opaque id.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},{"name":"serviceIds","in":"query","required":true,"description":"Between 1 and 10 service ids, comma-separated. Service ids are the `id` of entries from the services endpoint.","schema":{"examples":["cm1haircut,cm1beard"],"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}(,[A-Za-z0-9_-]{1,64}){0,9}$"}},{"name":"barberId","in":"query","required":false,"description":"One barber. Leave it out for the earliest barber.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NextSlotOrNull"}}}},"400":{"description":"`VALIDATION_ERROR`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`, `CUSTOMER_TOKEN_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LOCATION_NOT_BOOKABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`BARBER_NOT_AT_STORE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/verifications":{"post":{"operationId":"startVerification","summary":"Send a one-time code","description":"Texts or emails the customer a one-time code. Use it before booking when the location's `bookingVerification` is PHONE or EMAIL (the `method` must match it), or at any location to sign a customer in so they can see and manage their bookings. The code expires after 5 minutes.\n\nRate limit: 20 codes every 10 minutes per visitor IP address, shared with the BarberFlow booking page; 5 codes every 10 minutes per phone number or email, shared with the BarberFlow booking page.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartVerificationRequest"}}}},"responses":{"201":{"description":"Created","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"400":{"description":"`VALIDATION_ERROR`, `INVALID_JSON`, `INVALID_BODY`, `VERIFICATION_METHOD_MISMATCH`, `VERIFICATION_INVALID_PHONE`, `VERIFICATION_EMAIL_NOT_ALLOWED`, `VERIFICATION_PHONE_OPTED_OUT`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LOCATION_NOT_BOOKABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`, `VERIFICATION_SEND_FAILED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/verifications/{id}/confirm":{"post":{"operationId":"confirmVerification","summary":"Confirm a one-time code","description":"Checks the code and returns a customer token for this shop, valid for 30 days with this API key. Send it as `X-Customer-Token` to book at locations that verify, and to read the customer's profile and bookings. It never signs the customer in to BarberFlow itself.\n\nRate limit: 30 code checks a minute per visitor IP address; 10 attempts a minute per verification.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"An opaque id.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmVerificationRequest"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerSession"}}}},"400":{"description":"`VALIDATION_ERROR`, `INVALID_JSON`, `INVALID_BODY`, `VERIFICATION_CODE_INVALID`, `VERIFICATION_CODE_EXPIRED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`, `VERIFICATION_TOO_MANY_ATTEMPTS`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me":{"get":{"operationId":"getMe","summary":"Get the signed-in customer","description":"The verified customer behind the customer token, as this shop knows them.\n\nRate limit: 120 requests a minute per visitor IP address; 600 requests a minute per API key.","security":[{"apiKey":[],"customerToken":[]}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`, `CUSTOMER_TOKEN_REQUIRED`, `CUSTOMER_TOKEN_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/bookings":{"get":{"operationId":"listMyBookings","summary":"List the customer's bookings","description":"The signed-in customer's bookings with this shop only, upcoming (soonest first) or past (newest first), 20 to a page by default. Pass `nextCursor` back as `cursor` for the next page.\n\nRate limit: 120 requests a minute per visitor IP address; 600 requests a minute per API key.","security":[{"apiKey":[],"customerToken":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"`upcoming` (default) lists bookings that have not started and are not cancelled or completed, soonest first; `past` lists the rest (started, cancelled or completed), newest first. A cursor only works with the list that issued it.","schema":{"type":"string","enum":["upcoming","past"]}},{"name":"limit","in":"query","required":false,"description":"Page size, 1 to 50. Defaults to 20.","schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"cursor","in":"query","required":false,"description":"The `nextCursor` of the previous page.","schema":{"type":"string","maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingPage"}}}},"400":{"description":"`VALIDATION_ERROR`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`, `CUSTOMER_TOKEN_REQUIRED`, `CUSTOMER_TOKEN_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bookings":{"post":{"operationId":"createBooking","summary":"Create a booking","description":"Books a pay-in-person appointment. Send a new `Idempotency-Key` for every booking attempt and reuse it only to retry the same attempt; a retry answers with the same booking. At a location whose `bookingVerification` is not NONE, also send the customer token from a confirmed verification. When the location needs approval, `status` is PENDING_APPROVAL until the shop answers. The customer gets the same confirmations and reminders as a booking made on BarberFlow. Keep `manageToken` to read, cancel or reschedule the booking later.\n\nRate limit: 10 bookings a minute per visitor IP address; 120 bookings a minute per API key.","security":[{"apiKey":[]},{"apiKey":[],"customerToken":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"A new random value (a UUID works) for every booking attempt; 1 to 128 letters, digits, - or _.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$","maxLength":128,"examples":["0f8b6c2e-5d7a-4a43-9a43-2f3c1d9e8b10"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBookingRequest"}}}},"responses":{"201":{"description":"Created","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingCreated"}}}},"400":{"description":"`VALIDATION_ERROR`, `INVALID_JSON`, `INVALID_BODY`, `IDEMPOTENCY_KEY_REQUIRED`, `IDEMPOTENCY_KEY_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`, `CUSTOMER_TOKEN_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`, `VERIFICATION_REQUIRED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LOCATION_NOT_BOOKABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`IDEMPOTENCY_KEY_REUSED`, `PAY_IN_PERSON_NOT_ALLOWED`, `SLOT_TAKEN`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`SERVICE_NOT_AVAILABLE`, `BARBER_NOT_AT_STORE`, `OUTSIDE_WORKING_HOURS`, `INVALID_SLOT_INTERVAL`, `OUTSIDE_BOOKING_HORIZON`, `DATE_IN_PAST`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bookings/{bookingId}":{"get":{"operationId":"getBooking","summary":"Get a booking","description":"One booking, with whether it can still be cancelled or rescheduled and the late cancellation fee cancelling now would charge. Send the manage token from POST /bookings as `X-Booking-Token`, or the customer token of the customer who owns it.\n\nRate limit: 120 requests a minute per visitor IP address, shared with the BarberFlow manage page; 600 requests a minute per API key.","security":[{"apiKey":[],"bookingToken":[]},{"apiKey":[],"customerToken":[]}],"parameters":[{"name":"bookingId","in":"path","required":true,"description":"An opaque id.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"`VALIDATION_ERROR`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`, `CREDENTIAL_REQUIRED`, `CUSTOMER_TOKEN_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`BOOKING_NOT_FOUND`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bookings/{bookingId}/cancel":{"post":{"operationId":"cancelBooking","summary":"Cancel a booking","description":"Cancels the booking for the customer. A late cancellation fee applies only when the shop holds a card for the booking and the time is inside its charge window; API bookings carry no card, so they cancel free.\n\nRate limit: 120 requests a minute per visitor IP address, shared with the BarberFlow manage page; 600 requests a minute per API key.","security":[{"apiKey":[],"bookingToken":[]},{"apiKey":[],"customerToken":[]}],"parameters":[{"name":"bookingId","in":"path","required":true,"description":"An opaque id.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelBookingRequest"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"`VALIDATION_ERROR`, `INVALID_JSON`, `INVALID_BODY`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`, `CREDENTIAL_REQUIRED`, `CUSTOMER_TOKEN_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`BOOKING_NOT_FOUND`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`BOOKING_ALREADY_CANCELLED`, `BOOKING_NOT_CANCELLABLE`, `BOOKING_PAID`, `CONFLICT`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bookings/{bookingId}/reschedule":{"post":{"operationId":"rescheduleBooking","summary":"Reschedule a booking","description":"Moves the booking to a new start time with the same barber and services. A customer can reschedule a booking once; after that they must contact the shop.\n\nRate limit: 120 requests a minute per visitor IP address, shared with the BarberFlow manage page; 600 requests a minute per API key.","security":[{"apiKey":[],"bookingToken":[]},{"apiKey":[],"customerToken":[]}],"parameters":[{"name":"bookingId","in":"path","required":true,"description":"An opaque id.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleBookingRequest"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"`VALIDATION_ERROR`, `INVALID_JSON`, `INVALID_BODY`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`API_KEY_REQUIRED`, `API_KEY_INVALID`, `CREDENTIAL_REQUIRED`, `CUSTOMER_TOKEN_INVALID`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ORIGIN_NOT_ALLOWED`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`BOOKING_NOT_FOUND`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`BOOKING_ALREADY_CANCELLED`, `BOOKING_NOT_RESCHEDULABLE`, `BOOKING_RESCHEDULE_LIMIT`, `CONFLICT`, `SLOT_TAKEN`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`PAYLOAD_TOO_LARGE`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`OUTSIDE_WORKING_HOURS`, `INVALID_SLOT_INTERVAL`, `OUTSIDE_BOOKING_HORIZON`, `DATE_IN_PAST`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"Get the OpenAPI document","description":"This API described as OpenAPI 3.1. No key needed.\n\nRate limit: 120 requests a minute per IP address.","security":[],"responses":{"200":{"description":"OK","headers":{},"content":{"application/json":{"schema":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/llms-full.txt":{"get":{"operationId":"getLlmsFullText","summary":"Get the full reference as Markdown","description":"The whole reference as one Markdown file for AI coding tools. No key needed.\n\nRate limit: 120 requests a minute per IP address.","security":[],"responses":{"200":{"description":"OK","headers":{},"content":{"text/markdown":{"schema":{"type":"string"}}}},"429":{"description":"`RATE_LIMITED`","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"description":"A stable machine-readable code.","examples":["SLOT_TAKEN"],"type":"string"},"message":{"description":"A sentence you can show to the customer.","type":"string"},"details":{}},"required":["code","message"]}},"required":["error"]},"Business":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"name":{"type":"string"},"slug":{"type":"string"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"websiteUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"phone":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"cancellationPolicyText":{"anyOf":[{"type":"string"},{"type":"null"}]},"payment":{"type":"object","properties":{"payInPerson":{"description":"The shop takes payment at the appointment.","type":"boolean"},"online":{"description":"The shop takes card payment online on its hosted booking page.","type":"boolean"},"payInPersonRequiresCard":{"description":"Pay in person needs a card on file. API bookings are refused while this is true.","type":"boolean"}},"required":["payInPerson","online","payInPersonRequiresCard"]},"bookingUrl":{"description":"The shop's hosted booking page.","type":"string"}},"required":["id","name","slug","logoUrl","description","websiteUrl","phone","email","cancellationPolicyText","payment","bookingUrl"]},"Address":{"type":"object","properties":{"line1":{"type":"string"},"city":{"anyOf":[{"type":"string"},{"type":"null"}]},"province":{"anyOf":[{"type":"string"},{"type":"null"}]},"postalCode":{"anyOf":[{"type":"string"},{"type":"null"}]},"latitude":{"anyOf":[{"type":"number"},{"type":"null"}]},"longitude":{"anyOf":[{"type":"number"},{"type":"null"}]},"hidden":{"description":"The shop hides its street address; the other fields are empty.","type":"boolean"}},"required":["line1","city","province","postalCode","latitude","longitude","hidden"]},"OpeningHours":{"type":"object","properties":{"day":{"type":"string","enum":["sunday","monday","tuesday","wednesday","thursday","friday","saturday"]},"open":{"description":"A time of day as HH:mm.","examples":["14:30"],"type":"string"},"close":{"description":"A time of day as HH:mm.","examples":["14:30"],"type":"string"}},"required":["day","open","close"]},"Location":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"name":{"type":"string"},"slug":{"type":"string"},"address":{"$ref":"#/components/schemas/Address"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}]},"imageUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"timezone":{"description":"An IANA time zone.","examples":["America/Toronto"],"type":"string"},"hours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"}},"bookingVerification":{"description":"NONE, or PHONE / EMAIL when the customer must confirm a one-time code before booking.","type":"string","enum":["NONE","PHONE","EMAIL"]},"bookingApprovalRequired":{"description":"New bookings wait for the shop to approve them.","type":"boolean"},"bookingUrl":{"description":"This location on the shop's hosted booking page.","type":"string"}},"required":["id","name","slug","address","phone","imageUrl","timezone","hours","bookingVerification","bookingApprovalRequired","bookingUrl"]},"LocationList":{"type":"array","items":{"$ref":"#/components/schemas/Location"}},"AddOn":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"serviceId":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"name":{"type":"string"},"title":{"type":"string"},"priceCents":{"description":"An amount in cents.","type":"integer","minimum":0},"currency":{"description":"Always CAD.","type":"string","const":"CAD"},"durationMinutes":{"description":"A length of time in minutes.","type":"integer","minimum":0},"allowPayInPerson":{"type":"boolean"},"barberIds":{"type":"array","items":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}},"required":["id","serviceId","name","title","priceCents","currency","durationMinutes","allowPayInPerson","barberIds"]},"Service":{"type":"object","properties":{"id":{"description":"The id to send in `serviceIds`.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"serviceId":{"description":"The service this option belongs to. Several options can share one.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"name":{"description":"The service name, such as Haircut.","type":"string"},"title":{"description":"This option's title, such as Senior barber.","type":"string"},"slug":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"priceCents":{"description":"The listed price. The booking shows the final price.","type":"integer","minimum":0},"currency":{"description":"Always CAD.","type":"string","const":"CAD"},"durationMinutes":{"description":"A length of time in minutes.","type":"integer","minimum":0},"addOnOnly":{"description":"Bookable only as an add-on to another service.","type":"boolean"},"allowPayInPerson":{"description":"Only services that allow pay in person can be booked here.","type":"boolean"},"barberIds":{"type":"array","items":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},"addOns":{"description":"Add-ons that can be booked with this service. Book an add-on with a barber listed in both its `barberIds` and the service's.","type":"array","items":{"$ref":"#/components/schemas/AddOn"}}},"required":["id","serviceId","name","title","slug","description","priceCents","currency","durationMinutes","addOnOnly","allowPayInPerson","barberIds","addOns"]},"ServiceList":{"type":"array","items":{"$ref":"#/components/schemas/Service"}},"TeamMember":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"name":{"type":"string"},"slug":{"type":"string"},"imageUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"bio":{"anyOf":[{"type":"string"},{"type":"null"}]},"serviceIds":{"description":"The service ids this barber performs at the location.","type":"array","items":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},"nextAvailable":{"description":"The barber's earliest opening for their shortest service, or null. Check availability for the exact services.","anyOf":[{"type":"object","properties":{"startsAt":{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},"date":{"description":"A calendar date in the location's time zone, as YYYY-MM-DD.","examples":["2026-10-20"],"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"time":{"description":"A time of day as HH:mm.","examples":["14:30"],"type":"string"},"timezone":{"description":"An IANA time zone.","examples":["America/Toronto"],"type":"string"}},"required":["startsAt","date","time","timezone"]},{"type":"null"}]}},"required":["id","name","slug","imageUrl","bio","serviceIds","nextAvailable"]},"TeamList":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"}},"Slot":{"type":"object","properties":{"startsAt":{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},"time":{"description":"A time of day as HH:mm.","examples":["14:30"],"type":"string"},"barberIds":{"description":"Every barber free at this time for the services.","type":"array","items":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}},"required":["startsAt","time","barberIds"]},"AvailabilityDay":{"type":"object","properties":{"date":{"description":"A calendar date in the location's time zone, as YYYY-MM-DD.","examples":["2026-10-20"],"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"slots":{"type":"array","items":{"$ref":"#/components/schemas/Slot"}}},"required":["date","slots"]},"Availability":{"type":"object","properties":{"timezone":{"description":"An IANA time zone.","examples":["America/Toronto"],"type":"string"},"slotIntervalMinutes":{"description":"A length of time in minutes.","type":"integer","minimum":0},"horizonDays":{"description":"How many days ahead the shop takes bookings.","type":"integer"},"days":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityDay"}}},"required":["timezone","slotIntervalMinutes","horizonDays","days"]},"NextSlot":{"type":"object","properties":{"startsAt":{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},"date":{"description":"A calendar date in the location's time zone, as YYYY-MM-DD.","examples":["2026-10-20"],"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"time":{"description":"A time of day as HH:mm.","examples":["14:30"],"type":"string"},"timezone":{"description":"An IANA time zone.","examples":["America/Toronto"],"type":"string"},"barberIds":{"type":"array","items":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}}},"required":["startsAt","date","time","timezone","barberIds"]},"NextSlotOrNull":{"anyOf":[{"$ref":"#/components/schemas/NextSlot"},{"type":"null"}]},"Verification":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"method":{"type":"string","enum":["PHONE","EMAIL"]},"destination":{"description":"Where the code went: the last two digits of the phone number, or the masked email.","examples":["23","s…@example.com"],"type":"string"},"expiresAt":{"description":"When the code stops working, in UTC.","examples":["2026-10-20T18:35:00Z"],"type":"string"}},"required":["id","method","destination","expiresAt"]},"CustomerSession":{"type":"object","properties":{"customerToken":{"description":"Send as X-Customer-Token. Valid for 30 days with this API key.","type":"string"},"expiresAt":{"description":"When the customer token stops working, in UTC.","examples":["2026-10-20T18:35:00Z"],"type":"string"},"customer":{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"type":"string"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","email","phone"]}},"required":["customerToken","expiresAt","customer"]},"Customer":{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"type":"string"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}]},"customerId":{"description":"Null until the customer has booked with this shop.","anyOf":[{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},{"type":"null"}]}},"required":["name","email","phone","customerId"]},"BookingService":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"serviceId":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"name":{"type":"string"},"title":{"type":"string"},"priceCents":{"description":"An amount in cents.","type":"integer","minimum":0},"currency":{"description":"Always CAD.","type":"string","const":"CAD"},"durationMinutes":{"description":"A length of time in minutes.","type":"integer","minimum":0}},"required":["id","serviceId","name","title","priceCents","currency","durationMinutes"]},"Booking":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"status":{"type":"string","enum":["SCHEDULED","RESCHEDULED","CANCELLED","NO_SHOW","COMPLETED","PENDING_APPROVAL"]},"startsAt":{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},"endsAt":{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},"durationMinutes":{"description":"A length of time in minutes.","type":"integer","minimum":0},"timezone":{"description":"An IANA time zone.","examples":["America/Toronto"],"type":"string"},"location":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"name":{"type":"string"},"slug":{"type":"string"},"timezone":{"description":"An IANA time zone.","examples":["America/Toronto"],"type":"string"}},"required":["id","name","slug","timezone"]},"barber":{"type":"object","properties":{"id":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"name":{"type":"string"},"slug":{"type":"string"},"imageUrl":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","slug","imageUrl"]},"services":{"description":"The services booked. A cancelled booking still lists them.","type":"array","items":{"$ref":"#/components/schemas/BookingService"}},"totalCents":{"description":"The bill total: the booking's main sale plus any live sales added to it at the shop, taxes included. A cancelled booking totals 0.","type":"integer","minimum":0},"currency":{"description":"Always CAD.","type":"string","const":"CAD"},"payment":{"description":"How the booking is paid: PAY_IN_PERSON at the shop (every booking made through this API), CARD charged to a card on file, or GIFT_CARD covered by a gift card when booked on BarberFlow.","type":"string","enum":["PAY_IN_PERSON","CARD","GIFT_CARD"]},"customer":{"description":"The customer's contact. On POST /bookings it repeats the contact sent in the request. Reads with a manage token mask it: the email as its first letter and domain, the phone as its last two digits. Reads with the customer token of the customer who owns the booking return the contact the shop holds in full.","type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"type":"string"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","email","phone"]},"approval":{"anyOf":[{"type":"object","properties":{"requestedAt":{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},"expiresAt":{"anyOf":[{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},{"type":"null"}]},"outcome":{"anyOf":[{"type":"string","enum":["APPROVED","DECLINED","EXPIRED","WITHDRAWN"]},{"type":"null"}]}},"required":["requestedAt","expiresAt","outcome"]},{"type":"null"}]},"rescheduledToStartsAt":{"anyOf":[{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},{"type":"null"}]},"cancelledAt":{"anyOf":[{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"},{"type":"null"}]},"cancelledReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"canCancel":{"type":"boolean"},"canReschedule":{"type":"boolean"},"lateCancelFee":{"description":"The fee cancelling now would charge, or null when cancelling is free.","anyOf":[{"type":"object","properties":{"feeCents":{"description":"An amount in cents.","type":"integer","minimum":0},"currency":{"description":"Always CAD.","type":"string","const":"CAD"},"insideChargeWindow":{"type":"boolean"},"chargeWindowMinutes":{"description":"A length of time in minutes.","type":"integer","minimum":0}},"required":["feeCents","currency","insideChargeWindow","chargeWindowMinutes"]},{"type":"null"}]},"manageUrl":{"description":"The hosted page where the customer can manage this booking.","anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"description":"An ISO 8601 instant with the location's UTC offset.","examples":["2026-10-20T14:30:00-04:00"],"type":"string"}},"required":["id","status","startsAt","endsAt","durationMinutes","timezone","location","barber","services","totalCents","currency","payment","customer","approval","rescheduledToStartsAt","cancelledAt","cancelledReason","canCancel","canReschedule","lateCancelFee","manageUrl","createdAt"]},"BookingPage":{"type":"object","properties":{"bookings":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["bookings","nextCursor"]},"BookingCreated":{"type":"object","properties":{"booking":{"$ref":"#/components/schemas/Booking"},"status":{"description":"The booking's current status, the same as `booking.status`. A fresh booking is SCHEDULED, or PENDING_APPROVAL when the location approves bookings; a retry with the same Idempotency-Key reports where the booking stands now.","type":"string","enum":["SCHEDULED","RESCHEDULED","CANCELLED","NO_SHOW","COMPLETED","PENDING_APPROVAL"]},"manageToken":{"description":"Send as X-Booking-Token to read, cancel or reschedule.","anyOf":[{"type":"string"},{"type":"null"}]},"manageUrl":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["booking","status","manageToken","manageUrl"]},"StartVerificationRequest":{"type":"object","properties":{"locationId":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"method":{"description":"PHONE or EMAIL. At a location whose `bookingVerification` is PHONE or EMAIL, it must be that method; at a NONE location either works.","type":"string","enum":["PHONE","EMAIL"]},"contact":{"type":"object","properties":{"email":{"description":"The customer's email. Required for both methods.","type":"string","maxLength":254,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phone":{"description":"Required when `method` is PHONE.","type":"string","minLength":7,"maxLength":32},"name":{"type":"string","minLength":1,"maxLength":120}},"required":["email"]}},"required":["locationId","method","contact"]},"ConfirmVerificationRequest":{"type":"object","properties":{"code":{"description":"The code the customer received.","type":"string","minLength":3,"maxLength":10}},"required":["code"]},"CreateBookingRequest":{"type":"object","properties":{"locationId":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"barberId":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"serviceIds":{"description":"Service ids, add-ons included. 1 to 10, no repeats.","minItems":1,"maxItems":10,"type":"array","items":{"description":"An opaque id.","type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"}},"startsAt":{"description":"Either the location's local time as YYYY-MM-DDTHH:mm (no offset), or an ISO 8601 instant with an offset. The `startsAt` of an availability slot can be sent back as is.","examples":["2026-10-20T14:30","2026-10-20T14:30:00-04:00"],"type":"string"},"customer":{"description":"The customer's contact. It fills in the shop's own customer record (only fields that are still empty); the customer's BarberFlow account is never changed.","type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"type":"string","minLength":1,"maxLength":120},"phone":{"type":"string","minLength":7,"maxLength":32},"smsMarketingConsent":{"description":"True only when the customer ticked a box agreeing to marketing texts.","type":"boolean"}},"required":["email"]},"attribution":{"description":"Optional first-touch marketing data from the visit. All fields are optional. Send `referrerHost` and `referrerUrl` only for referrers outside the shop's own site.","type":"object","properties":{"anonymousId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$"},"utmSource":{"type":"string","minLength":1,"maxLength":200},"utmMedium":{"type":"string","minLength":1,"maxLength":200},"utmCampaign":{"type":"string","minLength":1,"maxLength":200},"utmTerm":{"type":"string","minLength":1,"maxLength":200},"utmContent":{"type":"string","minLength":1,"maxLength":200},"gclid":{"type":"string","minLength":1,"maxLength":512},"fbclid":{"type":"string","minLength":1,"maxLength":512},"ttclid":{"type":"string","minLength":1,"maxLength":512},"msclkid":{"type":"string","minLength":1,"maxLength":512},"referrerHost":{"type":"string","minLength":1,"maxLength":255},"referrerUrl":{"type":"string","minLength":1,"maxLength":2048},"landingPagePath":{"type":"string","minLength":1,"maxLength":2048},"bookingEntry":{"type":"string","minLength":1,"maxLength":100},"sourceSessionId":{"type":"string","minLength":1,"maxLength":200},"sourceTouchedAt":{"type":"integer","exclusiveMinimum":0}}}},"required":["locationId","barberId","serviceIds","startsAt","customer"]},"CancelBookingRequest":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":500}}},"RescheduleBookingRequest":{"type":"object","properties":{"startsAt":{"description":"Either the location's local time as YYYY-MM-DDTHH:mm (no offset), or an ISO 8601 instant with an offset. The `startsAt` of an availability slot can be sent back as is.","examples":["2026-10-20T14:30","2026-10-20T14:30:00-04:00"],"type":"string"},"reason":{"type":"string","minLength":1,"maxLength":500}},"required":["startsAt"]}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"bf_pk_…","description":"The shop's publishable API key, from Settings → Developers in BarberFlow."},"customerToken":{"type":"apiKey","in":"header","name":"X-Customer-Token","description":"A customer token from POST /verifications/{id}/confirm."},"bookingToken":{"type":"apiKey","in":"header","name":"X-Booking-Token","description":"The manageToken returned by POST /bookings."}},"headers":{"RateLimitLimit":{"description":"Requests allowed in the window.","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the window.","schema":{"type":"integer"}},"RateLimitReset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}}}}