{"system_guidance":"You are booking appointments through the OtterFlow Bookings API. Rules: (1) Never invent or guess an appointment time \u2014 call check_availability and use a slot start it returned verbatim. (2) Resolve relative dates yourself using the business time from get_business; the API rejects \"tomorrow\". (3) A time with no timezone is rejected \u2014 send an ISO 8601 instant with an offset, or {date, time} which is read in the business timezone. (4) If create_booking returns 409, read `alternatives` and offer those times directly. (5) If it returns 503, retry the identical request. (6) Prices are not stored \u2014 never quote one.","tools":[{"function":{"description":"The business, this key's scopes, the current limits, and the datetime rules. Call this first. It reports the business timezone and the current server time, which is what you use to resolve anything relative like \"tomorrow\".","name":"get_business","parameters":{"additionalProperties":false,"properties":{},"required":[],"type":"object"}},"type":"function"},{"function":{"description":"List bookable services. Each service carries its duration, notice period, booking horizon, and a `requires` block telling you which customer fields are mandatory. Prices are not stored by this system.","name":"list_services","parameters":{"additionalProperties":false,"properties":{"include_inactive":{"description":"Include services that aren't currently bookable.","type":"boolean"}},"required":[],"type":"object"}},"type":"function"},{"function":{"description":"Open slots for a service across a date range. The ONLY way to find a bookable time. Every returned slot start can be passed straight to create_booking. Days with nothing free carry a `reason` (closed, time_off, fully_booked) \u2014 'closed' means that weekday isn't worked, which is different from being fully booked. Maximum range 31 days.","name":"check_availability","parameters":{"additionalProperties":false,"properties":{"from":{"description":"First date, YYYY-MM-DD. Defaults to today.","type":"string"},"service_id":{"description":"Numeric id or slug of the service.","type":"string"},"to":{"description":"Last date, YYYY-MM-DD. Defaults to `from` + 6 days.","type":"string"}},"required":["service_id"],"type":"object"}},"type":"function"},{"function":{"description":"Which days have any availability, without the slot lists. Much smaller than check_availability. Use it to narrow a long period down to candidate days, then call check_availability for one of them. Maximum range 92 days.","name":"check_available_days","parameters":{"additionalProperties":false,"properties":{"from":{"description":"","type":"string"},"service_id":{"description":"","type":"string"},"to":{"description":"","type":"string"}},"required":["service_id"],"type":"object"}},"type":"function"},{"function":{"description":"Create a booking. Send an Idempotency-Key header so a retry can't create a second appointment. If the slot has gone, the 409 response carries `alternatives` \u2014 offer those to the customer instead of calling check_availability again. A 503 means the system was momentarily busy: retry the identical request, the slot is probably still free.","name":"create_booking","parameters":{"additionalProperties":false,"properties":{"client":{"properties":{"address":{"description":"Required only when the service's `requires.client_address` is true.","maxLength":2000,"type":"string"},"email":{"description":"Optional. Omit it if you don't have one \u2014 do NOT invent an address.","maxLength":255,"type":"string"},"name":{"description":"The customer's full name. Required.","maxLength":120,"type":"string"},"phone":{"description":"Philippine mobile number (09XXXXXXXXX, +639XXXXXXXXX or 639XXXXXXXXX). Required when the service sends SMS \u2014 check `requires.client_phone` on the service.","maxLength":40,"type":"string"}},"required":["name"],"type":"object"},"notes":{"description":"Anything the business should know.","maxLength":2000,"type":"string"},"service_id":{"description":"Numeric id or slug of the service.","type":"string"},"start":{"description":"Send either an ISO 8601 instant with an explicit UTC offset or Z (\"2026-08-20T14:30:00+08:00\"), or an object {\"date\": \"2026-08-20\", \"time\": \"14:30\"} which is read in the business timezone. A time with no timezone is REJECTED rather than guessed. Never guess a time \u2014 call check_availability first and use a slot it returned.","oneOf":[{"description":"ISO 8601 with offset or Z.","examples":["2026-08-20T14:30:00+08:00","2026-08-20T06:30:00Z"],"type":"string"},{"description":"Read in the business timezone unless `tz` is given.","properties":{"date":{"description":"YYYY-MM-DD","type":"string"},"time":{"description":"\"14:30\" or \"2:30 PM\"","type":"string"},"tz":{"description":"IANA name, e.g. Asia/Manila","type":"string"}},"required":["date","time"],"type":"object"}]}},"required":["client","service_id","start"],"type":"object"}},"type":"function"},{"function":{"description":"List bookings, newest first. Searching by `phone` needs the customers:read scope. Paginate with the returned `next_cursor`.","name":"find_bookings","parameters":{"additionalProperties":false,"properties":{"cursor":{"description":"`next_cursor` from the previous page.","type":"string"},"from":{"description":"Only bookings starting on/after this date (YYYY-MM-DD).","type":"string"},"limit":{"description":"1-100, default 25.","type":"integer"},"phone":{"description":"Find a customer's bookings. Needs customers:read.","type":"string"},"service_id":{"description":"","type":"string"},"status":{"description":"Comma-separated: confirmed, pending, cancelled, rejected.","type":"string"},"to":{"description":"Only bookings starting on/before this date (YYYY-MM-DD).","type":"string"}},"required":[],"type":"object"}},"type":"function"},{"function":{"description":"One booking by id or code.","name":"get_booking","parameters":{"additionalProperties":false,"properties":{"booking_id":{"description":"Numeric id or the booking's short code.","type":"string"}},"required":["booking_id"],"type":"object"}},"type":"function"},{"function":{"description":"Edit the customer's details or the notes. Times are NOT changed here \u2014 use reschedule_booking. Omitted fields are left untouched.","name":"update_booking","parameters":{"additionalProperties":false,"properties":{"booking_id":{"description":"","type":"string"},"client":{"properties":{"address":{"description":"Required only when the service's `requires.client_address` is true.","maxLength":2000,"type":"string"},"email":{"description":"Optional. Omit it if you don't have one \u2014 do NOT invent an address.","maxLength":255,"type":"string"},"name":{"description":"The customer's full name. Required.","maxLength":120,"type":"string"},"phone":{"description":"Philippine mobile number (09XXXXXXXXX, +639XXXXXXXXX or 639XXXXXXXXX). Required when the service sends SMS \u2014 check `requires.client_phone` on the service.","maxLength":40,"type":"string"}},"required":["name"],"type":"object"},"notes":{"maxLength":2000,"type":"string"}},"required":["booking_id"],"type":"object"}},"type":"function"},{"function":{"description":"Move a booking to a different time. A booking awaiting approval stays awaiting approval. Cancelled bookings cannot be rescheduled \u2014 create a new one. Texts the customer their new time, because nobody is watching a screen when an agent moves a booking; pass notify_client=false to move it silently.","name":"reschedule_booking","parameters":{"additionalProperties":false,"properties":{"booking_id":{"description":"","type":"string"},"notify_client":{"type":"boolean"},"start":{"description":"Send either an ISO 8601 instant with an explicit UTC offset or Z (\"2026-08-20T14:30:00+08:00\"), or an object {\"date\": \"2026-08-20\", \"time\": \"14:30\"} which is read in the business timezone. A time with no timezone is REJECTED rather than guessed. Never guess a time \u2014 call check_availability first and use a slot it returned.","oneOf":[{"description":"ISO 8601 with offset or Z.","examples":["2026-08-20T14:30:00+08:00","2026-08-20T06:30:00Z"],"type":"string"},{"description":"Read in the business timezone unless `tz` is given.","properties":{"date":{"description":"YYYY-MM-DD","type":"string"},"time":{"description":"\"14:30\" or \"2:30 PM\"","type":"string"},"tz":{"description":"IANA name, e.g. Asia/Manila","type":"string"}},"required":["date","time"],"type":"object"}]}},"required":["booking_id","start"],"type":"object"}},"type":"function"},{"function":{"description":"Cancel a booking. Safe to retry: cancelling an already-cancelled booking returns 200 with changed: false. Pass notify_client: false when you're already speaking to the customer, so they don't also get a cancellation text.","name":"cancel_booking","parameters":{"additionalProperties":false,"properties":{"booking_id":{"description":"","type":"string"},"notify_client":{"description":"Whether to text the customer. Omit to use the business default.","type":"boolean"}},"required":["booking_id"],"type":"object"}},"type":"function"}]}
