{"openapi":"3.1.0","info":{"title":"Torim API","description":"The business-side API. A key belongs to one business and names it, so no path carries a business identifier.\n\nVersion 1 is additive only: fields and endpoints may be added, and nothing already here is renamed, removed or retyped.","license":{"name":"MIT","identifier":"MIT"},"version":"1"},"paths":{"/api/v1/appointments":{"get":{"tags":["appointments"],"summary":"The diary, a window at a time","description":"`to` is inclusive and defaults to six days after `from`. The window may not exceed 62 days.","operationId":"appointments","parameters":[{"name":"from","in":"query","description":"First day of the window, `YYYY-MM-DD`. Defaults to today.","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"Last day of the window, inclusive. Defaults to six days after `from`.","required":false,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentView"}}}}},"400":{"description":"invalid_range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"403":{"description":"insufficient_scope — needs appointments:read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]},"post":{"tags":["appointments"],"summary":"Book somebody in","description":"The slot is re-derived server-side and the price is read from the service, so neither is taken from this request. The booking is the business's own, so it is `confirmed` at once: it never waits for approval, and neither an unsigned consent form nor a rule about a customer's no-shows or cancellations refuses it — those govern customers booking for themselves.","operationId":"create_appointment","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAppointment"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentView"}}}},"400":{"description":"invalid_name, invalid_email, invalid_service","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"403":{"description":"insufficient_scope — needs appointments:write","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"404":{"description":"no such service, or that person does not work here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"409":{"description":"slot_unavailable, slot_taken, session_full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/appointments/{id}":{"get":{"tags":["appointments"],"summary":"One appointment","operationId":"appointment","parameters":[{"name":"id","in":"path","description":"The appointment's id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentView"}}}},"404":{"description":"no such appointment here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/appointments/{id}/cancel":{"post":{"tags":["appointments"],"summary":"Call one off","description":"Frees the slot and tells the waiting list. Where the business keeps a freed time for whoever has waited longest, `/availability` does not offer it while it is kept. A `pending` request may be cancelled here too; this is a cancellation, not the diary's decline, and the customer is not sent the decline message.","operationId":"cancel_appointment","parameters":[{"name":"id","in":"path","description":"The appointment's id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentView"}}}},"400":{"description":"illegal_status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"404":{"description":"no such appointment here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/appointments/{id}/move":{"post":{"tags":["appointments"],"summary":"Reschedule","description":"One update under the same lock a booking takes, so a lost race leaves the appointment where it was rather than nowhere.","operationId":"move_appointment","parameters":[{"name":"id","in":"path","description":"The appointment's id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveAppointment"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentView"}}}},"404":{"description":"no such appointment here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"409":{"description":"already_cancelled, slot_unavailable, slot_taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/availability":{"get":{"tags":["availability"],"summary":"When one service can be booked","description":"Answered by the same code the public booking page uses, so a time offered here is a time `POST /appointments` will accept.","operationId":"availability","parameters":[{"name":"service","in":"query","description":"Service **slug**, as `/api/v1/services` returns it.","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","description":"First day to look at, `YYYY-MM-DD`. Defaults to today where the\nbusiness is.","required":false,"schema":{"type":"string","format":"date"}},{"name":"days","in":"query","description":"How many days to look at. Capped by the business's own booking horizon.","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityView"}}}},"400":{"description":"invalid_range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"404":{"description":"no such service","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/business":{"get":{"tags":["business"],"summary":"The business this key belongs to","description":"No identifier is passed: the key names the business.","operationId":"business","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessView"}}}},"401":{"description":"invalid_token, token_revoked, token_expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"403":{"description":"insufficient_scope — needs catalogue:read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/closures":{"get":{"tags":["catalogue"],"summary":"Days the business is shut, from today on","operationId":"closures","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClosureView"}}}}},"403":{"description":"insufficient_scope — needs catalogue:read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]},"post":{"tags":["catalogue"],"summary":"Shut the business for a day or a run of days","operationId":"create_closure","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateClosure"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClosureView"}}}},"400":{"description":"invalid_range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"403":{"description":"insufficient_scope — needs catalogue:write","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/closures/{id}":{"delete":{"tags":["catalogue"],"summary":"Reopen a day that was closed","operationId":"remove_closure","parameters":[{"name":"id","in":"path","description":"The closure's id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Reopened"},"403":{"description":"insufficient_scope — needs catalogue:write","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"404":{"description":"no such closure here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/hours":{"get":{"tags":["catalogue"],"summary":"The week the business is open","operationId":"hours","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OpeningRuleView"}}}}},"403":{"description":"insufficient_scope — needs catalogue:read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]},"put":{"tags":["catalogue"],"summary":"Replace that week","description":"The whole week, not a patch: \"Tuesday now closes at six\" and \"Tuesday is gone\" are the same edit to a caller holding the week. Sending an empty list closes the business, which is a real thing to want and not an error.","operationId":"replace_hours","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceHours"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OpeningRuleView"}}}}},"400":{"description":"invalid_window, overlapping_hours, invalid_weekday","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"403":{"description":"insufficient_scope — needs catalogue:write","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/services":{"get":{"tags":["catalogue"],"summary":"Every service, including retired ones","operationId":"services","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServiceView"}}}}},"403":{"description":"insufficient_scope — needs catalogue:read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]},"post":{"tags":["catalogue"],"summary":"Add a service","description":"The slug is chosen by the server and returned — it has to be unique within the business, and a caller-proposed one is either a collision it cannot act on or a uniqueness check that races.","operationId":"create_service","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceBody"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceView"}}}},"400":{"description":"invalid_duration, invalid_occupancy, invalid_price and the other field checks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"403":{"description":"insufficient_scope — needs catalogue:write","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/services/{slug}":{"patch":{"tags":["catalogue"],"summary":"Change a service","description":"Every field is replaced, so send the whole service. Changing a price leaves appointments already booked alone — each one keeps what it was booked with.","operationId":"update_service","parameters":[{"name":"slug","in":"path","description":"The service's slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceBody"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceView"}}}},"400":{"description":"a field failed its check","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}},"404":{"description":"no such service here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/services/{slug}/active":{"post":{"tags":["catalogue"],"summary":"Retire a service, or bring it back","description":"There is no delete. A slug appears in booking links already handed out and in appointments already made; retiring stops new bookings and leaves both intact.","operationId":"set_service_active","parameters":[{"name":"slug","in":"path","description":"The service's slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetActive"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceView"}}}},"404":{"description":"no such service here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}},"/api/v1/team":{"get":{"tags":["catalogue"],"summary":"Who works here","operationId":"team","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TeamMemberView"}}}}},"403":{"description":"insufficient_scope — needs catalogue:read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorView"}}}}},"security":[{"token":[]}]}}},"components":{"schemas":{"AppointmentView":{"type":"object","description":"One appointment in the diary.\n\nThe same shape every surface answers with, so a webhook payload and a\n`GET` will not disagree about what an appointment looks like.","required":["id","reference","status","starts_at","ends_at","service","service_name","price_agorot","created_at"],"properties":{"arrival_window_min":{"type":["integer","null"],"format":"int32","description":"For a home visit booked as an arrival window, its length: the\ncustomer was promised arrival between `starts_at` and this many\nminutes later."},"attendee_name":{"type":["string","null"],"description":"Who on the customer's account it is for — a child, a parent — when\nnot the customer themselves. The customer above stays who is reached."},"created_at":{"type":"string","format":"date-time"},"customer_email":{"type":["string","null"]},"customer_name":{"type":["string","null"]},"customer_phone":{"type":["string","null"]},"ends_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"note":{"type":["string","null"]},"price_agorot":{"type":"integer","format":"int32","description":"What it was booked at, in agorot. An appointment keeps the price it was\nmade with even after the service's own price changes."},"reference":{"type":"string","description":"A short code a person can read down a phone line."},"service":{"type":"string"},"service_name":{"$ref":"#/components/schemas/TextView"},"staff_id":{"type":["string","null"],"format":"uuid","description":"Absent for a class attendee: the instructor belongs to the session\nrather than to the booking."},"staff_name":{"type":["string","null"]},"starts_at":{"type":"string","format":"date-time"},"status":{"type":"string","description":"`pending`, `confirmed`, `completed`, `no_show` or `cancelled`.\n\n`pending` is a customer's booking request waiting for the business to\napprove it, where the business asks for that. It holds its time exactly\nas a confirmed booking does. Approved, it becomes `confirmed`; declined,\nor still unanswered about an hour before it starts, `cancelled`.\nA booking made through this API is never `pending`. `pending` was\nadded within v1, so do not treat this list as closed."},"visit_address":{"type":["string","null"],"description":"Where a home visit goes, as the customer gave it when booking — kept\non the appointment, so it still says where after they move house."},"visit_id":{"type":["string","null"],"format":"uuid","description":"The visit this appointment is one part of, when the customer booked\nseveral services back to back: every part carries the same id. Each\npart is still an appointment of its own here — cancelled and moved on\nits own — and absent for a booking of one service."}}},"AvailabilityDayView":{"type":"object","description":"A day of them.","required":["date","slots"],"properties":{"date":{"type":"string","description":"Local calendar date, `YYYY-MM-DD`."},"slots":{"type":"array","items":{"$ref":"#/components/schemas/SlotView"},"description":"Present even when empty, so a caller drawing a strip of days does not\nhave to invent the gaps."}}},"AvailabilityView":{"type":"object","description":"What one service has free.","required":["service","timezone","granularity_min","days"],"properties":{"days":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityDayView"}},"granularity_min":{"type":"integer","format":"int32","description":"The grid the business works to, in minutes."},"service":{"type":"string"},"timezone":{"type":"string"}}},"BusinessView":{"type":"object","description":"The business a token belongs to.","required":["slug","name","timezone","policy"],"properties":{"address":{"type":["string","null"]},"category":{"type":["string","null"]},"city":{"type":["string","null"]},"name":{"$ref":"#/components/schemas/TextView"},"policy":{"$ref":"#/components/schemas/PolicyView"},"slug":{"type":"string"},"timezone":{"type":"string","description":"IANA, e.g. `Asia/Jerusalem`. Every instant this API returns is UTC; this\nis what a caller needs to render one as the business's own clock."}}},"ClosureView":{"type":"object","description":"A day, or a run of days, the business is shut.","required":["id","from","to"],"properties":{"from":{"type":"string","description":"Inclusive local dates, as a person would say them."},"id":{"type":"string","format":"uuid"},"reason":{"type":["string","null"]},"to":{"type":"string"}}},"CreateAppointment":{"type":"object","description":"`POST /api/v1/appointments`","required":["service","staff_id","start","customer_name"],"properties":{"customer_email":{"type":["string","null"],"description":"The only way this booking can ever be written to — a walk-in with no\naddress can be told nothing when it moves."},"customer_name":{"type":"string"},"customer_phone":{"type":["string","null"]},"note":{"type":["string","null"],"description":"Free text for the diary. A service at the customer's home takes no\naddress here: only a customer's own account sets one, so a walk-in's\naddress is written in the note, as the desk does."},"off_grid":{"type":"boolean","description":"Take `start` as given rather than checking it against the slot grid.\n\nDefaults to false, which is the safe answer for a program: ask\n`/availability` and book one of the times it gave you. Set it when you\nare recording something that happened away from the grid — a walk-in at\n10:07 — and accept that nothing then checks the time is sensible except\nthe double-booking constraint."},"service":{"type":"string","description":"Service **slug**."},"staff_id":{"type":"string","format":"uuid","description":"Which member of the team performs it. From `/api/v1/team`."},"start":{"type":"string","format":"date-time","description":"Absolute start instant, RFC 3339."}},"additionalProperties":false},"CreateClosure":{"type":"object","description":"`POST /api/v1/closures`","required":["from"],"properties":{"from":{"type":"string","format":"date","description":"First day closed, `YYYY-MM-DD`."},"reason":{"type":["string","null"]},"to":{"type":["string","null"],"format":"date","description":"Last day closed, inclusive. Defaults to `from`, since a one-day\nclosure is the common case."}},"additionalProperties":false},"ErrorView":{"type":"object","description":"The body every failure carries.\n\nSwitch on `code`; it is stable. The `error` is English for a developer\nreading logs and may be reworded at any time.","required":["code","error"],"properties":{"code":{"type":"string","example":"insufficient_scope"},"error":{"type":"string","example":"this token does not hold the permission for that"}}},"MoveAppointment":{"type":"object","description":"`POST /api/v1/appointments/{id}/move`","required":["start"],"properties":{"off_grid":{"type":"boolean","description":"As on `CreateAppointment`."},"start":{"type":"string","format":"date-time","description":"The new start instant, RFC 3339."}},"additionalProperties":false},"OpeningRuleBody":{"type":"object","description":"One span of a weekday the business is open.","required":["weekday","start","end"],"properties":{"end":{"type":"string"},"start":{"type":"string","description":"`HH:MM`, on the business's own clock."},"weekday":{"type":"integer","format":"int32","description":"0 = Sunday, matching the week Israel starts on."}},"additionalProperties":false},"OpeningRuleView":{"type":"object","description":"One span of a weekday a business is open.","required":["weekday","start","end"],"properties":{"end":{"type":"string"},"start":{"type":"string","description":"`HH:MM`, on the business's own clock — not an instant, because a weekly\nrule is a fact about the clock rather than about any particular day."},"weekday":{"type":"integer","format":"int32","description":"0 = Sunday."}}},"PolicyView":{"type":"object","description":"The four numbers that decide when this business may be booked and unbooked.\n\nPublished because a caller building its own picker cannot compute a sensible\none without them — and because they explain a refusal: a booking rejected\nfor `min_notice_min` looks arbitrary until you can read the number.","required":["slot_granularity_min","min_notice_min","horizon_days","cancel_notice_min","online_booking","online_cancel"],"properties":{"cancel_notice_min":{"type":"integer","format":"int32","description":"`0` means an appointment may be called off any time before it starts."},"horizon_days":{"type":"integer","format":"int32"},"min_notice_min":{"type":"integer","format":"int32"},"online_booking":{"type":"boolean","description":"Whether *customers* may book on Torim, and whether they may cancel or\nmove for themselves. Neither binds this API — a key acts for the\nbusiness, and a business is never refused its own diary — but a caller\nrendering a public picker needs to know not to."},"online_cancel":{"type":"boolean"},"slot_granularity_min":{"type":"integer","format":"int32"}}},"ReplaceHours":{"type":"object","description":"`PUT /api/v1/hours` — the whole week, replaced.\n\nA replacement rather than a patch, because \"Tuesday now closes at six\" and\n\"Tuesday is gone\" are the same edit to a caller holding the week, and a\npartial update makes deleting a day the awkward case.","required":["rules"],"properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/OpeningRuleBody"}}},"additionalProperties":false},"ServiceBody":{"type":"object","description":"The fields a service is made of.\n\nCarries `ToSchema` because `/api/v1` takes the identical body — one shape\nand one set of checks for the two surfaces, rather than a v1 copy that\ndrifts the first time a field is added here.","required":["name_he","duration_min","price_agorot"],"properties":{"arrival_window_min":{"type":["integer","null"],"format":"int32","description":"For a home visit, the arrival window it is booked as, in minutes —\n\"between eight and eleven\" is 180 — or `null` for an exact time. A\nmultiple of half an hour, up to eight hours. Absent leaves it as it\nis, for the reason the instructions give; anything but a home visit\nhas none."},"description_en":{"type":["string","null"]},"description_he":{"type":["string","null"]},"duration_min":{"type":"integer","format":"int32"},"instructions_en":{"type":["string","null"]},"instructions_he":{"type":["string","null"],"description":"What a customer should read before the visit, in paragraphs — shown on\nthe page, the confirmation and the visit card. Empty or `null` clears\nthem; **absent leaves them as they are**, unlike every field above.\n\nThe exception is on purpose: `/api/v1` takes this same body, and an\nintegration written before these fields existed sends a whole service\nwithout them. Read as \"none\", its next update would silently wipe what\nthe business wrote for its customers."},"location":{"type":"string","description":"`on_site` (the default), `online` or `at_home`. An online service\ncarries a room in `meeting_url`; the other two refuse one."},"meeting_url":{"type":["string","null"]},"name_en":{"type":["string","null"]},"name_he":{"type":"string"},"occupy_min":{"type":["integer","null"],"format":"int32","description":"How long a booking holds the person performing it, when that differs\nfrom how long the customer is booked for. Absent means it does not."},"price_agorot":{"type":"integer","format":"int32"},"rebook_after_days":{"type":["integer","null"],"format":"int32","description":"How many days after a visit to nudge the customer to book the next\none — between [`REBOOK_AFTER_DAYS`]'s ends, or absent for never. Only\npeople who agreed to hear from the business are nudged."},"show_duration":{"type":["boolean","null"],"description":"Whether customers are told how long it takes. The length still sets\nthe times offered either way. Absent leaves it as it is — on, for a\nnew service — for the reason the instructions give below."},"show_price":{"type":"boolean"},"sort_order":{"type":"integer","format":"int32"},"visits_per_window":{"type":["integer","null"],"format":"int32","description":"For a home visit booked as a window, how many visits one person takes\nin it — `null` for as many as the visit's length fits, never more.\nAbsent leaves it as it is."}}},"ServiceView":{"type":"object","description":"One service, as its business sells it.","required":["slug","name","duration_min","show_duration","on_hours","active","location"],"properties":{"active":{"type":"boolean","description":"A retired service still appears here, and still refuses new bookings."},"arrival_window_min":{"type":["integer","null"],"format":"int32","description":"For a home visit booked as a window, how long the window is."},"description":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TextView"}]},"duration_min":{"type":"integer","format":"int32"},"instructions":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TextView","description":"What whoever books is asked to do before coming — fast, bring a\nreferral — as the business wrote it. Read back here because the write\ntakes it: without it a client could set the instructions and never\nsee them again."}]},"location":{"type":"string","description":"`on_site`, `online` or `at_home`, and for the second the room the\nbusiness pasted. Published: this is the business's own key reading its\nown catalogue."},"meeting_url":{"type":["string","null"]},"name":{"$ref":"#/components/schemas/TextView"},"occupy_min":{"type":["integer","null"],"format":"int32","description":"How long a booking holds the person performing it. Absent means the\nappointment's own length."},"on_hours":{"type":"boolean","description":"Bookable in any of the business's opening hours — beside, not instead\nof, the sessions on its own books, each of which carries its own\nsize. Off, the service is booked at its sessions alone. A service has\nno kind of its own: what used to be `kind` and `capacity` here are\nfacts about its assignments — this switch, and the sessions."},"price_agorot":{"type":["integer","null"],"format":"int32","description":"Absent when the business chose not to publish its price — and absent\nfrom the payload rather than hidden, so it is not sitting in the\nresponse for anybody who looks."},"rebook_after_days":{"type":["integer","null"],"format":"int32","description":"Days after a visit the customer is nudged to book the next one, when\nthe business set it. Read back here because the write takes the whole\nservice: a client that saves without it clears it."},"show_duration":{"type":"boolean","description":"Whether the business tells its customers `duration_min`. Present\neither way, unlike a hidden price: v1 is additive-only and has always\ncarried the length, and a caller building its own picker needs it.\nA page that shows it to customers should read this first."},"slug":{"type":"string","description":"Stable for the life of the service, and the id every other endpoint\ntakes. A service is retired rather than deleted and its slug never\nchanges, because booking links already handed out contain it."},"visits_per_window":{"type":["integer","null"],"format":"int32","description":"How many visits one person takes in that window, when the business\nset fewer than fit."}}},"SetActive":{"type":"object","description":"Whether a service is offered.","required":["active"],"properties":{"active":{"type":"boolean"}},"additionalProperties":false},"SlotView":{"type":"object","description":"One bookable moment.","required":["start","label","seats_left"],"properties":{"label":{"type":"string","description":"The same moment on the business's own clock, `HH:MM`. Provided because\nformatting it client-side is how a caller in another timezone shows a\ncustomer the wrong hour."},"seats_left":{"type":"integer","format":"int32","description":"How many more bookings this moment can take."},"start":{"type":"string","format":"date-time","description":"Absolute instant, RFC 3339 — the only unambiguous form, and what\n`POST /appointments` expects back."}}},"TeamMemberView":{"type":"object","description":"Somebody on the team.","required":["id","role","bookable","active"],"properties":{"active":{"type":"boolean","description":"False while an invitation is outstanding, or once somebody has left."},"bookable":{"type":"boolean","description":"Whether appointments may be booked onto this person at all. A manager\nwho runs the place without cutting hair is a member and not bookable."},"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"role":{"type":"string","description":"`manager` or `employee`."}}},"TextView":{"type":"object","description":"A name in the languages a business gave it.","required":["he"],"properties":{"en":{"type":["string","null"]},"he":{"type":"string"}}}},"securitySchemes":{"token":{"type":"http","scheme":"bearer","description":"An API key, minted by a manager under Integrations. Send it as `Authorization: Bearer torim_…`. The session cookie is not accepted here."}}},"tags":[{"name":"business","description":"The business itself"},{"name":"catalogue","description":"Services, team and closures"},{"name":"availability","description":"When things can be booked"},{"name":"appointments","description":"The diary, and changing it"}]}