{"openapi":"3.1.0","info":{"title":"ARUNEE CLEAN — Booking Calendar API","version":"1.0.0","description":"ARUNEE CLEAN is a home-cleaning service in Pattaya, Thailand. This API exposes live availability, the price list, and a way to place a confirmed booking. Reads are public. Placing a booking (POST /api/calendar/bookings) needs the calendar API key, sent as `Authorization: Bearer <key>` or `X-Api-Key: <key>`. Call GET /api/calendar first: it returns the services, prices, areas, start times and the questions to ask a customer.","contact":{"name":"ARUNEE CLEAN","email":"book@aruneeclean.com","url":"https://aruneeclean.com"}},"servers":[{"url":"https://www.aruneeclean.com"}],"paths":{"/api/calendar":{"get":{"operationId":"getCalendar","summary":"Everything an assistant needs: services, prices, slots and current availability","description":"The one call to start with. Returns the catalog (services, unit types, areas, the five start slots, the live price list and the intake questions) and, by default, availability for the next 14 days.","parameters":[{"name":"days","in":"query","schema":{"type":"integer","minimum":1,"maximum":31}},{"name":"from","in":"query","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"}},{"name":"to","in":"query","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"}},{"name":"availability","in":"query","description":"Set to `0` to omit availability and return the catalog only.","schema":{"type":"string","enum":["0","1"]}}],"responses":{"200":{"description":"Catalog, with availability unless disabled.","content":{"application/json":{"schema":{"type":"object","properties":{"catalog":{"type":"object"},"availability":{"type":"object","properties":{"from":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"},"to":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"},"timeZone":{"type":"string","example":"Asia/Bangkok"},"maxPerDay":{"type":"integer"},"generatedAt":{"type":"string","format":"date-time"},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"},"weekday":{"type":"string"},"isSunday":{"type":"boolean"},"isPast":{"type":"boolean"},"maxPerDay":{"type":"integer"},"booked":{"type":"integer"},"remaining":{"type":"integer"},"freeSlots":{"type":"array","items":{"type":"string","enum":["08:00","10:00","12:00","14:00","16:00"]}},"takenSlots":{"type":"array","items":{"type":"string"}},"bookable":{"type":"boolean"}}}}}}}}}}}}}},"/api/calendar/availability":{"get":{"operationId":"getAvailability","summary":"Free start slots and remaining capacity per day","parameters":[{"name":"from","in":"query","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"},"description":"Defaults to today."},{"name":"to","in":"query","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"}},{"name":"days","in":"query","schema":{"type":"integer","minimum":1,"maximum":31}}],"responses":{"200":{"description":"Availability window.","content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"},"to":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"},"timeZone":{"type":"string","example":"Asia/Bangkok"},"maxPerDay":{"type":"integer"},"generatedAt":{"type":"string","format":"date-time"},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"},"weekday":{"type":"string"},"isSunday":{"type":"boolean"},"isPast":{"type":"boolean"},"maxPerDay":{"type":"integer"},"booked":{"type":"integer"},"remaining":{"type":"integer"},"freeSlots":{"type":"array","items":{"type":"string","enum":["08:00","10:00","12:00","14:00","16:00"]}},"takenSlots":{"type":"array","items":{"type":"string"}},"bookable":{"type":"boolean"}}}}}}}}},"400":{"description":"Bad query."}}}},"/api/calendar/pricing":{"get":{"operationId":"getPricing","summary":"The live price list, in Thai Baht","responses":{"200":{"description":"Price list and the enums that describe it.","content":{"application/json":{"schema":{"type":"object","properties":{"currency":{"type":"string","example":"THB"},"priceList":{"type":"array","items":{"type":"object","properties":{"serviceType":{"type":"string"},"unitType":{"type":"string"},"sizeBand":{"type":"string"},"listPriceThb":{"type":["integer","null"]},"durationEstimateMin":{"type":["integer","null"]},"note":{"type":"string"}}}}}}}}}}}},"/api/calendar/bookings":{"post":{"operationId":"createBooking","summary":"Place a confirmed booking","description":"Holds the slot immediately and emails the operator a summary. This call completes the booking — there is no separate confirmation step, so read the `message` in the response back to the customer. The slot must be free and the day must not be sold out — check /api/calendar/availability first. Send a stable `requestId` so a retry does not book twice.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["name","email","phone","scheduledDate","scheduledTime","serviceType","unitType","area","building","floor","unit"],"properties":{"name":{"type":"string","minLength":1,"maxLength":120},"email":{"type":"string","format":"email"},"phone":{"type":"string","description":"Reachable phone number."},"scheduledDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25","description":"Booking day, from today onward."},"scheduledTime":{"type":"string","enum":["08:00","10:00","12:00","14:00","16:00"]},"serviceType":{"type":"string","enum":["standard","end_of_lease","first_trial","turnover","post_renovation","post_construction"]},"unitType":{"type":"string","enum":["studio","1br","2br","3br","end_of_lease","townhouse","house","villa"]},"area":{"type":"string","enum":["jomtien","na_jomtien","central_pattaya","naklua","pratamnak","north_pattaya","buakhao","east_pattaya","other"]},"areaOther":{"type":"string","description":"Required when area is `other`."},"building":{"type":"string"},"floor":{"type":"string"},"unit":{"type":"string"},"bedrooms":{"type":["integer","null"],"minimum":1,"maximum":30},"bathrooms":{"type":["integer","null"],"minimum":1,"maximum":30},"notes":{"type":"string","maxLength":2000},"requestId":{"type":"string","minLength":8,"maxLength":128,"description":"Stable id for this booking so a retry is idempotent."}}}}}},"responses":{"200":{"description":"Booked.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"bookingId":{"type":"string"},"status":{"type":"string","example":"booked"},"scheduledDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-25"},"scheduledTime":{"type":"string"},"duplicate":{"type":"boolean"},"notified":{"type":"boolean","description":"Whether the operator email was sent."},"message":{"type":"string","description":"The confirmation to read back to the customer. This response completes the booking; there is no separate step."}}}}}},"400":{"description":"Invalid request."},"401":{"description":"Missing or wrong calendar API key."},"409":{"description":"Slot taken, or the day is full. Body carries `freeSlots`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"freeSlots":{"type":"array","items":{"type":"string"}}}}}}},"422":{"description":"A past date."},"503":{"description":"Bookings are switched off (no key configured)."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key"}}}}