{"openapi":"3.1.0","info":{"title":"BarberBook API","version":"1.0.0","description":"Booking API for a barbershop. Authenticate with `Authorization: Bearer <shop API key>` (find it in the shop dashboard → Settings → API). All times are in the shop's timezone unless an offset is included. `/tools/*` endpoints are forgiving and voice-agent friendly; the REST endpoints are strict."},"servers":[{"url":"https://www.vocsera.com"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}},"ToolResult":{"type":"object","properties":{"ok":{"type":"boolean"},"message":{"type":"string"}},"additionalProperties":true},"Slot":{"type":"object","properties":{"start":{"type":"string","format":"date-time"},"time":{"type":"string","example":"10:30"},"staff":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}}}}},"Appointment":{"type":"object","properties":{"id":{"type":"string"},"ref":{"type":"string","example":"K7Q4MX"},"status":{"type":"string","enum":["BOOKED","CANCELLED","COMPLETED","NO_SHOW"]},"source":{"type":"string","enum":["WEB","PHONE_AGENT","ADMIN","API"]},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"date":{"type":"string"},"time":{"type":"string"},"when":{"type":"string"},"staff":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"service":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"durationMin":{"type":"integer"}}},"name":{"type":"string","description":"Name given for this booking (falls back to the customer's name)"},"customer":{"type":"object","description":"The shop's customer record for this phone number","properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}}},"price":{"type":"number"},"priceFormatted":{"type":"string"},"notes":{"type":"string"},"manageUrl":{"type":"string"}}}}},"paths":{"/api/v1/shop":{"get":{"operationId":"getShop","tags":["Shop"],"summary":"Shop info, hours, services, barbers, current local time","responses":{"200":{"description":"OK"},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/availability":{"get":{"operationId":"getAvailability","tags":["Availability"],"summary":"Free slots for a day (`date`) or the next available slots (omit `date`)","parameters":[{"name":"service","in":"query","required":true,"description":"Service id or name","schema":{"type":"string"}},{"name":"staff","in":"query","required":false,"description":"Staff id, name, or 'any'","schema":{"type":"string"}},{"name":"date","in":"query","required":false,"description":"YYYY-MM-DD","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"Start date for next-available search","schema":{"type":"string"}},{"name":"days","in":"query","required":false,"description":"Days to scan (default 14)","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Max slots (default 20)","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/appointments":{"get":{"operationId":"listAppointments","tags":["Appointments"],"summary":"Upcoming appointments for a phone number, or the day sheet for a date","parameters":[{"name":"phone","in":"query","required":false,"description":"Customer phone","schema":{"type":"string"}},{"name":"date","in":"query","required":false,"description":"YYYY-MM-DD (default today)","schema":{"type":"string"}},{"name":"days","in":"query","required":false,"description":"Number of days (max 31)","schema":{"type":"string"}},{"name":"past","in":"query","required":false,"description":"1 to include past (with phone)","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createAppointment","tags":["Appointments"],"summary":"Book an appointment","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["customer"],"properties":{"service":{"type":"string","description":"Service id or name"},"staff":{"type":"string","description":"Staff id/name or 'any'"},"start":{"type":"string","description":"ISO datetime; or use date + time"},"date":{"type":"string","example":"2026-10-09"},"time":{"type":"string","example":"14:30"},"customer":{"type":"object","required":["name","phone"],"properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"}}},"notes":{"type":"string"},"source":{"type":"string","enum":["PHONE_AGENT","API"]}}}}}},"responses":{"200":{"description":"This customer (same phone) already holds this slot: nothing new was created; a different name updates that booking's name","content":{"application/json":{"schema":{"type":"object","properties":{"appointment":{"$ref":"#/components/schemas/Appointment"},"outcome":{"type":"string","enum":["ALREADY_YOURS"]}}}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"appointment":{"$ref":"#/components/schemas/Appointment"},"outcome":{"type":"string","enum":["CREATED"]}}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/appointments/{ref}":{"parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string"},"description":"Booking reference or id"}],"get":{"operationId":"getAppointment","tags":["Appointments"],"summary":"Get one appointment","responses":{"200":{"description":"OK"},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"rescheduleAppointment","tags":["Appointments"],"summary":"Reschedule (new start and/or staff)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"string"},"date":{"type":"string"},"time":{"type":"string"},"staff":{"type":"string"}}}}}},"responses":{"200":{"description":"OK"},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteAppointment","tags":["Appointments"],"summary":"Cancel","parameters":[{"name":"reason","in":"query","required":false,"description":"Optional reason","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/appointments/{ref}/cancel":{"parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string"}}],"post":{"operationId":"cancelAppointment","tags":["Appointments"],"summary":"Cancel an appointment","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"OK"},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tools/get_shop_info":{"post":{"operationId":"get_shop_info","tags":["Agent tools"],"summary":"Get the shop's current local date/time, opening hours, services with prices and durations, and barbers. Call this at the start of every call.","parameters":[{"name":"x-caller-phone","in":"header","required":false,"schema":{"type":"string"},"description":"Caller ID, used when customer_phone is omitted"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolResult"}}}}}}},"/api/v1/tools/check_availability":{"post":{"operationId":"check_availability","tags":["Agent tools"],"summary":"Check free appointment times. Give a date to list that day's free times; add a time to check one exact slot (returns closest alternatives if taken). Omit the date to get the next available times.","parameters":[{"name":"x-caller-phone","in":"header","required":false,"schema":{"type":"string"},"description":"Caller ID, used when customer_phone is omitted"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"service":{"type":"string","description":"Service name, e.g. 'Haircut'. Optional if the shop has only one service."},"barber":{"type":"string","description":"Barber name, or 'any' for no preference."},"date":{"type":"string","description":"Date as YYYY-MM-DD (preferred), or 'today'/'tomorrow'/weekday name."},"time":{"type":"string","description":"Optional time to check, 24h HH:mm, e.g. '14:30'."}}}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolResult"}}}}}}},"/api/v1/tools/book_appointment":{"post":{"operationId":"book_appointment","tags":["Agent tools"],"summary":"Book an appointment. Only call after confirming service, barber, date, time and the customer's name with the caller. The caller's phone number is used automatically if customer_phone is omitted.","parameters":[{"name":"x-caller-phone","in":"header","required":false,"schema":{"type":"string"},"description":"Caller ID, used when customer_phone is omitted"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"service":{"type":"string","description":"Service name."},"barber":{"type":"string","description":"Barber name, or 'any'."},"date":{"type":"string","description":"YYYY-MM-DD"},"time":{"type":"string","description":"24h HH:mm"},"customer_name":{"type":"string","description":"Customer's full name."},"customer_phone":{"type":"string","description":"Customer's phone number. Omit to use the caller ID."},"notes":{"type":"string","description":"Optional notes for the barber."}},"required":["date","time","customer_name"]}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolResult"}}}}}}},"/api/v1/tools/find_appointments":{"post":{"operationId":"find_appointments","tags":["Agent tools"],"summary":"List a customer's upcoming appointments by phone number (defaults to the caller ID).","parameters":[{"name":"x-caller-phone","in":"header","required":false,"schema":{"type":"string"},"description":"Caller ID, used when customer_phone is omitted"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"customer_phone":{"type":"string","description":"Phone number. Omit to use the caller ID."}}}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolResult"}}}}}}},"/api/v1/tools/cancel_appointment":{"post":{"operationId":"cancel_appointment","tags":["Agent tools"],"summary":"Cancel an appointment by booking reference, or by the customer's phone (+ date if they have several). Confirm with the caller before calling. If it is refused as too late, say so using the message returned and the shop's cancellation policy from get_shop_info; do not promise exceptions, the shop decides.","parameters":[{"name":"x-caller-phone","in":"header","required":false,"schema":{"type":"string"},"description":"Caller ID, used when customer_phone is omitted"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reference":{"type":"string","description":"6-character booking reference, if the caller has it."},"customer_phone":{"type":"string","description":"Phone number. Omit to use the caller ID."},"date":{"type":"string","description":"Date of the appointment to cancel (YYYY-MM-DD) if they have more than one."},"reason":{"type":"string","description":"Optional reason."}}}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolResult"}}}}}}},"/api/v1/tools/reschedule_appointment":{"post":{"operationId":"reschedule_appointment","tags":["Agent tools"],"summary":"Move an existing appointment to a new date/time (and optionally another barber). Check availability first. If it is refused as too late, say so using the message returned; do not promise exceptions, the shop decides.","parameters":[{"name":"x-caller-phone","in":"header","required":false,"schema":{"type":"string"},"description":"Caller ID, used when customer_phone is omitted"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reference":{"type":"string","description":"Booking reference, if known."},"customer_phone":{"type":"string","description":"Phone number. Omit to use the caller ID."},"current_date":{"type":"string","description":"Current appointment date (YYYY-MM-DD) if they have several."},"new_date":{"type":"string","description":"New date YYYY-MM-DD."},"new_time":{"type":"string","description":"New time HH:mm."},"barber":{"type":"string","description":"Optional new barber name."}},"required":["new_time"]}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolResult"}}}}}}}}}