{"openapi":"3.1.0","info":{"title":"Adella Spa Booking API","version":"1.0.0","summary":"Look up treatments and prices, check real availability and submit booking requests at Adella Spa (sports massage & muscle recovery, Da Nang, Vietnam).","description":"Workflow for agents:\n1. `listServices` — pick a service `id` and one of its duration `id`s (and optionally a therapist `id`).\n2. `checkAvailability` — get bookable start times for a date (local time, Asia/Ho_Chi_Minh). Offer the guest real times only.\n3. `requestBooking` — submit the guest's full name and phone number. Set `consent: true` ONLY after the guest agreed to the privacy policy and terms.\nThe result is a PENDING request; the spa confirms with the guest by phone, WhatsApp or Zalo. Never tell the guest the booking is confirmed.\nOnly name and phone are accepted — do not send email, health details or other personal data.\nErrors always include `error` (stable code), `message` and `hint` explaining how to recover.\nAn MCP server with the same tools is available at /api/mcp (Streamable HTTP).","contact":{"name":"Adella Spa","url":"https://www.adellaspa.com"}},"servers":[{"url":"https://www.adellaspa.com"}],"externalDocs":{"description":"Summary for AI systems","url":"https://www.adellaspa.com/llms.txt"},"paths":{"/api/services":{"get":{"operationId":"listServices","summary":"List services, durations, prices, therapists and booking rules","description":"Returns everything needed to build a valid booking request. Prices are in Vietnamese dong (VND). Use the `id` values exactly as returned.","responses":{"200":{"description":"Catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Catalog"}}}}}}},"/api/availability":{"get":{"operationId":"checkAvailability","summary":"Get available start times for a service on a date","description":"Availability is computed from real therapist schedules, rooms and existing bookings. Times are local (Asia/Ho_Chi_Minh). An empty `slots` list with `reason` explains why (closed, past, too-far, no-staff).","parameters":[{"name":"serviceId","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"services[].id from listServices"},{"name":"durationId","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"services[].durations[].id from listServices"},{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date"},"example":"2026-12-01","description":"Local date YYYY-MM-DD"},{"name":"therapistId","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Only times when this therapist is free"}],"responses":{"200":{"description":"Available times","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Availability"}}}},"400":{"description":"invalid_request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"service_unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"booking_unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/bookings":{"post":{"operationId":"requestBooking","summary":"Submit a booking request (pending until the spa confirms)","description":"Re-checks availability on the server, then stores a PENDING request with the guest's name and phone. Agents must omit `startedAt` (it is only used by the website form). Rate-limited to 5 requests per hour per network.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRequest"},"example":{"serviceId":"3f0c…","durationId":"9a1b…","date":"2026-12-01","time":"10:00","fullName":"Jane Runner","phone":"+44 7700 900123","locale":"en","consent":true}}}},"responses":{"201":{"description":"Booking request stored (status pending)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingCreated"}}}},"409":{"description":"slot_taken (time no longer free — call checkAvailability again) or service_unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"validation_failed — see `fields`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"booking_unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error","message","hint"],"properties":{"error":{"type":"string","enum":["invalid_request","invalid_json","payload_too_large","validation_failed","forbidden","spam_suspected","captcha_failed","rate_limited","booking_unavailable","service_unavailable","slot_taken","server_error"],"description":"Stable machine-readable code"},"message":{"type":"string"},"hint":{"type":"string","description":"What to do next"},"fields":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}}}}}},"Catalog":{"type":"object","properties":{"business":{"type":"object","description":"Name, city, timezone, opening hours and (when configured) address, phone, WhatsApp"},"booking":{"type":"object","description":"Booking rules: notice, advance window, currency, consent and privacy notes"},"services":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"object","properties":{"en":{"type":"string"},"vi":{"type":"string"}}},"summary":{"type":"object","properties":{"en":{"type":"string"},"vi":{"type":"string"}}},"bookingEnabled":{"type":"boolean"},"durations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"minutes":{"type":"integer"},"priceVnd":{"type":"integer"}}}}}}},"therapists":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"languages":{"type":"array","items":{"type":"string"}}}}}}},"Availability":{"type":"object","properties":{"date":{"type":"string","format":"date"},"timezone":{"type":"string","const":"Asia/Ho_Chi_Minh"},"reason":{"type":["string","null"],"enum":["closed","past","too-far","no-staff",null]},"slots":{"type":"array","items":{"type":"object","properties":{"time":{"type":"string","example":"10:00"},"startsAt":{"type":"string","format":"date-time"},"therapistIds":{"type":"array","items":{"type":"string"}}}}}}},"BookingRequest":{"type":"object","additionalProperties":false,"required":["serviceId","durationId","date","time","fullName","phone","consent"],"properties":{"serviceId":{"type":"string","format":"uuid"},"durationId":{"type":"string","format":"uuid"},"date":{"type":"string","format":"date","description":"Local date YYYY-MM-DD"},"time":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"One of the times returned by checkAvailability"},"therapistId":{"type":["string","null"],"format":"uuid","description":"Optional preferred therapist"},"fullName":{"type":"string","minLength":2,"maxLength":100},"phone":{"type":"string","description":"Vietnamese local (0905 123 456) or international with country code (+44 7700 900123). Used by the spa to confirm by phone/WhatsApp/Zalo."},"locale":{"type":"string","enum":["en","vi"],"default":"en","description":"Language for the spa's confirmation message"},"consent":{"type":"boolean","const":true,"description":"Guest agreed to the privacy policy and terms"}}},"BookingCreated":{"type":"object","properties":{"ok":{"type":"boolean"},"reference":{"type":"string","example":"ADL-7K3M9Q"},"status":{"type":"string","const":"pending"},"startsAt":{"type":"string","format":"date-time"},"serviceName":{"type":"string"},"durationMinutes":{"type":"integer"},"priceVnd":{"type":"integer"},"next":{"type":"string"}}}}}}