# Sawatdi Booking API > Sawatdi is a massage and wellness studio in Tallinn, Estonia. > This API lets you book appointments programmatically. Base URL: https://sawatdi.com All endpoints accept and return JSON when using the `.json` suffix or `Accept: application/json` header. Timezone: Europe/Tallinn. All timestamps are ISO 8601. ## Booking Flow 1. GET /booking.json — fetch service catalog, employees, available dates 2. POST /appointments.json — create appointment with a service option 3. (Optional) POST /appointments/:token/services.json — add more services 4. (Optional) PATCH /appointments/:token/employees/:id.json — pick employee 5. GET /appointments/:token/available_times.json?date=YYYY-MM-DD — get time slots 6. PATCH /appointments/:token/mark_as_pending_at.json — reserve a time slot 7. POST /appointments/:token/contacts.json — add name, email, phone (repeat for each) 8. (Optional) POST /appointments/:token/comment.json — add a note 9. PATCH /appointments/:token/confirm.json — confirm the appointment ## Endpoints ### GET /booking.json Returns service catalog with option IDs, prices, durations; employee list; available dates. Response: service_groups[].name — group name service_groups[].services[].name — service name service_groups[].services[].description — service description service_groups[].services[].options[].id — service option ID (use this to create appointments) service_groups[].services[].options[].duration — minutes service_groups[].services[].options[].price — formatted price service_groups[].services[].options[].price_cents — price in cents (EUR) employees[].id — employee ID employees[].name — employee name available_dates[] — dates with availability (YYYY-MM-DD strings) ### POST /appointments.json Params: service_option_id (required) Creates appointment in "created" state. Returns 201. ### GET /appointments/:token.json Returns current appointment state and details. ### POST /appointments/:token/services.json Params: service_option_id (required) Adds a service to the appointment. Returns 422 if duration would exceed 240 minutes. ### DELETE /appointments/:token/services/:id.json Removes a booked service. :id is the booked_service ID from the appointment response. ### PATCH /appointments/:token/employees/:id.json Assigns an employee. :id is the employee ID from the catalog. ### DELETE /appointments/:token/employees/:id.json Removes employee assignment. ### GET /appointments/:token/available_times.json?date=YYYY-MM-DD Params: date (required, from available_dates) Response: date — the requested date times[] — available start times as Unix timestamps levels — object mapping date strings to availability level ("high", "medium", "low") ### PATCH /appointments/:token/mark_as_pending_at.json Params: pending_time (required, Unix timestamp from times array) Reserves the time slot. Returns 409 if slot is no longer available. Reservation expires after 3 minutes — confirm before then. ### POST /appointments/:token/contacts.json Params: contact[kind], contact[data] Kinds: "preferred_name", "email_address", "phone_number" Call once per contact kind. At least email or phone required before confirming. ### POST /appointments/:token/comment.json Params: content Adds or updates a client comment/note. ### PATCH /appointments/:token/confirm.json Confirms the appointment. Requires: pending state, at least one contact. Returns 422 if confirmation fails. ### PATCH /appointments/:token/change.json Moves confirmed appointment back to pending for rescheduling. Returns 422 if not allowed. ### PATCH /appointments/:token/cancel.json Cancels a pending or confirmed appointment. Returns 422 if not allowed. ### POST /appointments/:token/vouchers.json Params: provider ("sawatdi" or "stebby"), token (voucher code) Applies a gift card or voucher. Returns 422 with error message if invalid. ### DELETE /appointments/:token/vouchers/:token.json Removes a voucher from the appointment. ## Constraints - Maximum appointment duration: 240 minutes - Time slot reservation: 3 minutes (must confirm before expiry) - Appointment creation rate limit: 10 per 15 minutes per IP - All prices in EUR