{"openapi":"3.1.0","info":{"title":"AI Services Booking","version":"1.0.0","description":"AI Services Booking lets you find and book appointments at independent local businesses: barbers and hair salons first, then beauty, nails, spas and more.\n\nTypical flow:\n1. search_businesses to find a business (or use a business_id you already know).\n2. get_business for services, prices, staff, opening hours and policies.\n3. check_availability for real, bookable slots.\n4. hold_slot to reserve the chosen slot for 10 minutes.\n5. Read the hold summary back to the client, including price and cancellation policy. Only after they agree, call confirm_booking with their name and a phone number or email.\n\nNever confirm a booking the client hasn't explicitly agreed to. Times are ISO-8601 in UTC; labels are already in the business's local time, so use the labels when talking to people.\n\nThe same operations are available as an MCP server at https://ai-services-booking.vercel.app/api/mcp. No key needed. Send an `X-Client-Name` header (e.g. \"ChatGPT\") so businesses see who booked."},"servers":[{"url":"https://ai-services-booking.vercel.app/api/v1"}],"paths":{"/businesses":{"get":{"operationId":"search_businesses","summary":"Search businesses","description":"Find local businesses by what the client wants (e.g. 'skin fade', 'balayage', 'gel nails') and/or area (e.g. 'Hackney Wick', 'E9'). Filter by type. Returns business ids for the other operations.","parameters":[{"name":"query","in":"query","required":false,"description":"Free text: service, style or business name.","schema":{"type":"string"}},{"name":"area","in":"query","required":false,"description":"Neighbourhood or postcode district.","schema":{"type":"string"}},{"name":"type","in":"query","required":false,"description":"Only this kind of business.","schema":{"type":"string","enum":["barber","hair salon","beauty","nails","spa","massage","other"]}}],"responses":{"200":{"description":"Success. `summary` is a plain-language version of the result, ready to read back.","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":true}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid","forbidden","not_found","unavailable","too_late","expired","method_not_allowed","server_error"]},"message":{"type":"string","description":"Plain language; safe to read to the client."}},"required":["code","message"]}},"required":["error"]}}}}}}},"/businesses/{business_id}":{"get":{"operationId":"get_business","summary":"Get business details","description":"Full profile of one business: services with prices and durations, who does what, opening hours, address and booking policies. Call this before checking availability so you use valid service ids.","parameters":[{"name":"business_id","in":"path","required":true,"description":"From search_businesses, e.g. 'atelier-nord'.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success. `summary` is a plain-language version of the result, ready to read back.","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":true}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid","forbidden","not_found","unavailable","too_late","expired","method_not_allowed","server_error"]},"message":{"type":"string","description":"Plain language; safe to read to the client."}},"required":["code","message"]}},"required":["error"]}}}}}}},"/businesses/{business_id}/availability":{"get":{"operationId":"check_availability","summary":"Check availability","description":"Live bookable slots for a service. Defaults to the next 7 days. Filter by staff member or time of day. Each slot lists which staff are free.","parameters":[{"name":"business_id","in":"path","required":true,"description":"From search_businesses, e.g. 'atelier-nord'.","schema":{"type":"string"}},{"name":"service_id","in":"query","required":true,"description":"From get_business, e.g. 'skin-fade'.","schema":{"type":"string"}},{"name":"date_from","in":"query","required":false,"description":"Date in YYYY-MM-DD, the business's local time.","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"date_to","in":"query","required":false,"description":"Date in YYYY-MM-DD, the business's local time.","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"staff_id","in":"query","required":false,"description":"Only this staff member, e.g. 'maya'.","schema":{"type":"string"}},{"name":"time_of_day","in":"query","required":false,"schema":{"type":"string","enum":["morning","afternoon","evening","any"]}},{"name":"limit","in":"query","required":false,"description":"Max slots to return (default 12).","schema":{"type":"integer","minimum":1,"maximum":40}}],"responses":{"200":{"description":"Success. `summary` is a plain-language version of the result, ready to read back.","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":true}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid","forbidden","not_found","unavailable","too_late","expired","method_not_allowed","server_error"]},"message":{"type":"string","description":"Plain language; safe to read to the client."}},"required":["code","message"]}},"required":["error"]}}}}}}},"/holds":{"post":{"operationId":"hold_slot","summary":"Hold a slot","description":"Reserve a slot for 10 minutes while you confirm with the client. Returns a hold_id and a summary (price, deposit, policy) to read back. Nothing is booked until confirm_booking.","x-openai-isConsequential":true,"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"business_id":{"type":"string","description":"From search_businesses, e.g. 'atelier-nord'."},"service_id":{"type":"string"},"start":{"type":"string","description":"Exact `start` value from check_availability (ISO-8601)."},"staff_id":{"description":"Preferred staff member; omit for anyone free.","type":"string"}},"required":["business_id","service_id","start"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success. `summary` is a plain-language version of the result, ready to read back.","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":true}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid","forbidden","not_found","unavailable","too_late","expired","method_not_allowed","server_error"]},"message":{"type":"string","description":"Plain language; safe to read to the client."}},"required":["code","message"]}},"required":["error"]}}}}}}},"/bookings":{"post":{"operationId":"confirm_booking","summary":"Confirm booking","description":"Turn a hold into a confirmed booking. Only call after the client has agreed to the time, price and policy. Needs the client's name and a phone number or email.","x-openai-isConsequential":true,"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hold_id":{"type":"string"},"customer_name":{"type":"string","minLength":2},"customer_phone":{"type":"string"},"customer_email":{"type":"string","format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"notes":{"description":"Anything the business should know, e.g. hair type or goals.","type":"string","maxLength":500}},"required":["hold_id","customer_name"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success. `summary` is a plain-language version of the result, ready to read back.","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":true}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid","forbidden","not_found","unavailable","too_late","expired","method_not_allowed","server_error"]},"message":{"type":"string","description":"Plain language; safe to read to the client."}},"required":["code","message"]}},"required":["error"]}}}}}}},"/bookings/{reference}/lookup":{"post":{"operationId":"get_booking","summary":"Look up a booking","description":"Find an existing booking by its reference plus the phone number or email used to book.","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact":{"type":"string","description":"Phone number or email given at booking."}},"required":["contact"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success. `summary` is a plain-language version of the result, ready to read back.","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":true}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid","forbidden","not_found","unavailable","too_late","expired","method_not_allowed","server_error"]},"message":{"type":"string","description":"Plain language; safe to read to the client."}},"required":["code","message"]}},"required":["error"]}}}}}}},"/bookings/{reference}/reschedule":{"post":{"operationId":"reschedule_booking","summary":"Reschedule booking","description":"Move a confirmed booking to a new time (check availability first). Keeps the same staff member when they're free, otherwise picks another qualified one unless staff_id is given.","x-openai-isConsequential":true,"parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact":{"type":"string","description":"Phone number or email given at booking."},"new_start":{"type":"string","description":"ISO-8601 start from check_availability."},"staff_id":{"type":"string"}},"required":["contact","new_start"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success. `summary` is a plain-language version of the result, ready to read back.","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":true}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid","forbidden","not_found","unavailable","too_late","expired","method_not_allowed","server_error"]},"message":{"type":"string","description":"Plain language; safe to read to the client."}},"required":["code","message"]}},"required":["error"]}}}}}}},"/bookings/{reference}/cancel":{"post":{"operationId":"cancel_booking","summary":"Cancel booking","description":"Cancel a booking. Confirm with the client first. Tells you whether it falls inside the free-cancellation window.","x-openai-isConsequential":true,"parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact":{"type":"string","description":"Phone number or email given at booking."}},"required":["contact"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success. `summary` is a plain-language version of the result, ready to read back.","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"],"additionalProperties":true}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid","forbidden","not_found","unavailable","too_late","expired","method_not_allowed","server_error"]},"message":{"type":"string","description":"Plain language; safe to read to the client."}},"required":["code","message"]}},"required":["error"]}}}}}}}}}