{"openapi":"3.1.0","info":{"title":"RemindR API","version":"1.0.0","description":"Connect other systems to a RemindR instance: keep services, work hours and bookings in step. Authenticate with an instance API key (Authorization: Bearer rmr_…), made by an instance admin under Settings → PC-Kassa → API keys. The key decides the instance; its scopes decide what it may do."},"servers":[{"url":"https://remindr.se/api/v1"}],"security":[{"apiKey":[]}],"tags":[{"name":"Account","description":"The key you are calling with and the instance it belongs to."},{"name":"Services","description":"The catalogue: categories, services and their add-ons, and which resources can perform each service."},{"name":"Resources","description":"Staff and other resources that bookings are placed on. Created and named in RemindR Admin."},{"name":"Work hours","description":"When each resource works, week by week, and exceptions such as closed days or other hours."},{"name":"Bookings","description":"Bookings as staff see them. Placing, moving and cancelling check capacity and work hours, and send the same confirmations as RemindR Admin."},{"name":"Availability","description":"Free days and start times, as staff see them: the customer booking window (notice, how far ahead) does not apply."}],"paths":{"/me":{"get":{"operationId":"getMe","tags":["Account"],"summary":"Who am I","description":"The instance the key belongs to, its time zone and booking mode, and the key's scopes. Works with any valid key: a good first call.","x-scope":"any key","parameters":[],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/categories":{"get":{"operationId":"listCategories","tags":["Services"],"summary":"List categories","x-scope":"services:read","parameters":[],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createCategory","tags":["Services"],"summary":"Create a category","x-scope":"services:write","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryCreate"}}}},"responses":{"201":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/categories/{id}":{"patch":{"operationId":"updateCategory","tags":["Services"],"summary":"Update a category","description":"Only the fields given change.","x-scope":"services:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryUpdate"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteCategory","tags":["Services"],"summary":"Delete a category","description":"Only an empty category can be deleted; move or deactivate its services first.","x-scope":"services:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"responses":{"204":{"description":"No content","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/services":{"get":{"operationId":"listServices","tags":["Services"],"summary":"List services","description":"Every service, active or not, with its add-ons and resources, in the order they are shown.","x-scope":"services:read","parameters":[],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createService","tags":["Services"],"summary":"Create a service","description":"Give the category by id, or by name to find or create it. A new service is not shown on the booking site until isPublic is true, and cannot be booked until at least one resource can perform it.","x-scope":"services:write","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceCreate"}}}},"responses":{"201":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/services/{id}":{"get":{"operationId":"getService","tags":["Services"],"summary":"Get a service","x-scope":"services:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateService","tags":["Services"],"summary":"Update a service","description":"Only the fields given change; `resources`, when given, replaces the whole list. Services are not deleted through the API, because bookings refer to them: set isActive to false instead. Existing bookings keep the name, duration and price they were booked with.","x-scope":"services:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUpdate"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/services/{id}/addons":{"post":{"operationId":"createAddon","tags":["Services"],"summary":"Add an add-on","x-scope":"services:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddonCreate"}}}},"responses":{"201":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Addon"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/services/{id}/addons/{addonId}":{"patch":{"operationId":"updateAddon","tags":["Services"],"summary":"Update an add-on","description":"Only the fields given change.","x-scope":"services:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"addonId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddonUpdate"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Addon"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteAddon","tags":["Services"],"summary":"Delete an add-on","description":"Bookings keep their own copy of the add-on.","x-scope":"services:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"addonId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"responses":{"204":{"description":"No content","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/resources":{"get":{"operationId":"listResources","tags":["Resources"],"summary":"List resources","description":"Active resources first.","x-scope":"any read scope","parameters":[],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Resource"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/resources/{id}/work-hours":{"get":{"operationId":"getWorkHours","tags":["Work hours"],"summary":"Get a resource's work hours","x-scope":"schedules:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkHours"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"addWorkHours","tags":["Work hours"],"summary":"Add work hours","description":"Adds the same hours to each weekday given. Returns all of the resource's work hours. Hours may not overlap others on the same weekday and dates.","x-scope":"schedules:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkHoursCreate"}}}},"responses":{"201":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkHours"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"replaceWorkHours","tags":["Work hours"],"summary":"Replace all work hours","description":"Sets the resource's complete work hours in one go: all or nothing. Handy for keeping RemindR in step with another system. Existing bookings are not moved.","x-scope":"schedules:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkHoursReplace"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkHours"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/resources/{id}/work-hours/{entryId}":{"delete":{"operationId":"deleteWorkHours","tags":["Work hours"],"summary":"Delete work hours","x-scope":"schedules:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"entryId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"responses":{"204":{"description":"No content","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/schedule-exceptions":{"get":{"operationId":"listExceptions","tags":["Work hours"],"summary":"List exceptions","description":"Closed days and other hours that have not ended yet, for the whole instance and each resource.","x-scope":"schedules:read","parameters":[{"name":"from","in":"query","required":false,"description":"Exceptions ending on or after this day. Default: today.","schema":{"type":"string","format":"date","example":"2026-10-05"}},{"name":"resourceId","in":"query","required":false,"description":"Only this resource's exceptions (instance-wide ones are always included).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleException"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createException","tags":["Work hours"],"summary":"Add an exception","description":"Closes a period, or sets other hours for it, for one resource or the whole instance. Takes precedence over the weekly work hours. Existing bookings are not moved.","x-scope":"schedules:write","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleExceptionCreate"}}}},"responses":{"201":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleException"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/schedule-exceptions/{id}":{"delete":{"operationId":"deleteException","tags":["Work hours"],"summary":"Delete an exception","x-scope":"schedules:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"responses":{"204":{"description":"No content","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bookings":{"get":{"operationId":"listBookings","tags":["Bookings"],"summary":"List bookings","description":"Bookings on the days from `from` to `to`, at most 93 days at a time.","x-scope":"bookings:read","parameters":[{"name":"from","in":"query","required":true,"description":"A date, YYYY-MM-DD, in the instance's time zone.","schema":{"type":"string","format":"date","example":"2026-10-05"}},{"name":"to","in":"query","required":true,"description":"A date, YYYY-MM-DD, in the instance's time zone.","schema":{"type":"string","format":"date","example":"2026-10-05"}},{"name":"status","in":"query","required":false,"schema":{"default":"confirmed","type":"string","enum":["confirmed","cancelled","all"]}},{"name":"resourceId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createBooking","tags":["Bookings"],"summary":"Create a booking","description":"For a service booked by time slot give `startsAt`; for one booked by the day give `day`. Without `resourceId` the first free resource that can perform the service is chosen. Give an existing customer's id, or the customer's details: a customer with the same mobile number is reused. The instance's confirmation e-mail and SMS are sent as when staff book.","x-scope":"bookings:write","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingCreate"}}}},"responses":{"201":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bookings/{id}":{"get":{"operationId":"getBooking","tags":["Bookings"],"summary":"Get a booking","x-scope":"bookings:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateBooking","tags":["Bookings"],"summary":"Move or update a booking","description":"Giving `startsAt`, `day` or `resourceId` moves the booking, checking capacity as for a new one. Notes and opt-ins change as given. No message is sent to the customer.","x-scope":"bookings:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingUpdate"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/bookings/{id}/cancel":{"post":{"operationId":"cancelBooking","tags":["Bookings"],"summary":"Cancel a booking","description":"Stops reminders not yet sent and sends the instance's cancellation message. Cancelling a cancelled booking changes nothing.","x-scope":"bookings:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string, e.g. a UUID. A retry with the same key and the same request gets the first response back, marked Idempotent-Replayed: true, instead of doing the change twice. Kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingCancel"}}}},"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/availability":{"get":{"operationId":"getAvailableDays","tags":["Availability"],"summary":"Free days","description":"Days from `from` to `to` (at most 63) with room for the service and add-ons.","x-scope":"bookings:read","parameters":[{"name":"serviceId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"addonId","in":"query","required":false,"description":"Add-on ids; repeat the parameter for more than one.","schema":{"anyOf":[{"type":"string","format":"uuid"},{"maxItems":20,"type":"array","items":{"type":"string","format":"uuid"}}]}},{"name":"from","in":"query","required":true,"description":"A date, YYYY-MM-DD, in the instance's time zone.","schema":{"type":"string","format":"date","example":"2026-10-05"}},{"name":"to","in":"query","required":true,"description":"A date, YYYY-MM-DD, in the instance's time zone.","schema":{"type":"string","format":"date","example":"2026-10-05"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AvailableDay"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/availability/slots":{"get":{"operationId":"getAvailableSlots","tags":["Availability"],"summary":"Free start times","description":"Start times on a day for a service booked by time slot, each with the resource it would be booked on. Empty for services booked by the day.","x-scope":"bookings:read","parameters":[{"name":"serviceId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"addonId","in":"query","required":false,"description":"Add-on ids; repeat the parameter for more than one.","schema":{"anyOf":[{"type":"string","format":"uuid"},{"maxItems":20,"type":"array","items":{"type":"string","format":"uuid"}}]}},{"name":"day","in":"query","required":true,"description":"A date, YYYY-MM-DD, in the instance's time zone.","schema":{"type":"string","format":"date","example":"2026-10-05"}}],"responses":{"200":{"description":"OK","headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AvailableSlot"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the count starts over.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"4XX":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An instance API key: rmr_<prefix>_<secret>."}},"schemas":{"Me":{"type":"object","properties":{"instance":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"timeZone":{"type":"string","example":"Europe/Stockholm"},"bookingMode":{"type":"string","enum":["day_capacity","time_slots"],"description":"`time_slots`: the customer picks a start time. `day_capacity`: the customer picks a day and the work is planned into it."}},"required":["id","slug","name","timeZone","bookingMode"]},"key":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"scopes":{"type":"array","items":{"type":"string"}}},"required":["id","scopes"]}},"required":["instance","key"]},"Category":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"sortOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"isActive":{"type":"boolean"},"serviceCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","sortOrder","isActive","serviceCount"]},"CategoryCreate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"sortOrder":{"default":0,"type":"integer","minimum":-100000,"maximum":100000,"description":"Lower comes first."},"isActive":{"default":true,"type":"boolean"}},"required":["name"]},"CategoryUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"sortOrder":{"type":"integer","minimum":-100000,"maximum":100000,"description":"Lower comes first."},"isActive":{"type":"boolean"}}},"Addon":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"durationMinutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"price":{"type":"number"},"sortOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"isActive":{"type":"boolean"},"isPublic":{"type":"boolean"}},"required":["id","name","description","durationMinutes","price","sortOrder","isActive","isPublic"]},"AddonCreate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"default":null,"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}]},"durationMinutes":{"default":0,"type":"integer","minimum":0,"maximum":1440,"description":"Minutes the add-on adds to the booking."},"price":{"default":0,"type":"number","minimum":0,"maximum":9999999,"description":"SEK including VAT, at most two decimals.","example":1495},"sortOrder":{"default":0,"type":"integer","minimum":-100000,"maximum":100000,"description":"Lower comes first."},"isActive":{"default":true,"type":"boolean"},"isPublic":{"default":false,"type":"boolean"}},"required":["name"]},"AddonUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}]},"durationMinutes":{"type":"integer","minimum":0,"maximum":1440,"description":"Minutes the add-on adds to the booking."},"price":{"type":"number","minimum":0,"maximum":9999999,"description":"SEK including VAT, at most two decimals.","example":1495},"sortOrder":{"type":"integer","minimum":-100000,"maximum":100000,"description":"Lower comes first."},"isActive":{"type":"boolean"},"isPublic":{"type":"boolean","description":"Offered on the public booking site."}}},"Service":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"categoryId":{"type":"string","format":"uuid"},"categoryName":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"durationMinutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"price":{"type":"number"},"sortOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"isActive":{"type":"boolean"},"isPublic":{"type":"boolean"},"bookingMode":{"anyOf":[{"type":"string","enum":["day_capacity","time_slots"],"description":"`time_slots`: the customer picks a start time. `day_capacity`: the customer picks a day and the work is planned into it."},{"type":"null"}],"description":"Null follows the instance's setting."},"slotIntervalMinutes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Null follows the instance's setting."},"minHoursAhead":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Notice needed, in hours. Null follows the instance's setting."},"maxDaysAhead":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How far ahead it can be booked, in days. Null follows the instance's setting."},"addons":{"type":"array","items":{"$ref":"#/components/schemas/Addon"}},"resources":{"type":"array","items":{"type":"object","properties":{"resourceId":{"type":"string","format":"uuid"},"priority":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["resourceId","priority"]},"description":"The resources (staff) that can perform the service."}},"required":["id","categoryId","categoryName","name","description","durationMinutes","price","sortOrder","isActive","isPublic","bookingMode","slotIntervalMinutes","minHoursAhead","maxDaysAhead","addons","resources"]},"ServiceCreate":{"type":"object","properties":{"categoryId":{"type":"string","format":"uuid","description":"An existing category. Give this or `categoryName`."},"categoryName":{"type":"string","minLength":1,"maxLength":120,"description":"A category by name; created if there is none. Give this or `categoryId`."},"name":{"type":"string","minLength":1,"maxLength":200},"description":{"default":null,"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}]},"durationMinutes":{"type":"integer","minimum":5,"maximum":1440},"price":{"type":"number","minimum":0,"maximum":9999999,"description":"SEK including VAT, at most two decimals.","example":1495},"sortOrder":{"default":0,"type":"integer","minimum":-100000,"maximum":100000,"description":"Lower comes first."},"isActive":{"default":true,"type":"boolean"},"isPublic":{"default":false,"type":"boolean"},"bookingMode":{"default":null,"anyOf":[{"type":"string","enum":["day_capacity","time_slots"],"description":"`time_slots`: the customer picks a start time. `day_capacity`: the customer picks a day and the work is planned into it."},{"type":"null"}]},"slotIntervalMinutes":{"default":null,"anyOf":[{"anyOf":[{"type":"number","const":5},{"type":"number","const":10},{"type":"number","const":15},{"type":"number","const":20},{"type":"number","const":30},{"type":"number","const":60}]},{"type":"null"}]},"minHoursAhead":{"default":null,"anyOf":[{"type":"integer","minimum":0,"maximum":720},{"type":"null"}]},"maxDaysAhead":{"default":null,"anyOf":[{"type":"integer","minimum":0,"maximum":730},{"type":"null"}]},"resources":{"default":[],"maxItems":200,"type":"array","items":{"type":"object","properties":{"resourceId":{"type":"string","format":"uuid"},"priority":{"default":5,"description":"0–9. When several resources are free, the lowest number is booked first.","type":"integer","minimum":0,"maximum":9}},"required":["resourceId"]},"description":"Replaces the resources that can perform the service. Resources of other instances are ignored."}},"required":["name","durationMinutes","price"]},"ServiceUpdate":{"type":"object","properties":{"categoryId":{"type":"string","format":"uuid","description":"An existing category. Give this or `categoryName`."},"categoryName":{"type":"string","minLength":1,"maxLength":120,"description":"A category by name; created if there is none. Give this or `categoryId`."},"name":{"type":"string","minLength":1,"maxLength":200},"description":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}]},"durationMinutes":{"type":"integer","minimum":5,"maximum":1440},"price":{"type":"number","minimum":0,"maximum":9999999,"description":"SEK including VAT, at most two decimals.","example":1495},"sortOrder":{"type":"integer","minimum":-100000,"maximum":100000,"description":"Lower comes first."},"isActive":{"type":"boolean"},"isPublic":{"type":"boolean","description":"Offered on the public booking site."},"bookingMode":{"anyOf":[{"type":"string","enum":["day_capacity","time_slots"],"description":"`time_slots`: the customer picks a start time. `day_capacity`: the customer picks a day and the work is planned into it."},{"type":"null"}]},"slotIntervalMinutes":{"anyOf":[{"anyOf":[{"type":"number","const":5},{"type":"number","const":10},{"type":"number","const":15},{"type":"number","const":20},{"type":"number","const":30},{"type":"number","const":60}]},{"type":"null"}]},"minHoursAhead":{"anyOf":[{"type":"integer","minimum":0,"maximum":720},{"type":"null"}]},"maxDaysAhead":{"anyOf":[{"type":"integer","minimum":0,"maximum":730},{"type":"null"}]},"resources":{"maxItems":200,"type":"array","items":{"type":"object","properties":{"resourceId":{"type":"string","format":"uuid"},"priority":{"default":5,"description":"0–9. When several resources are free, the lowest number is booked first.","type":"integer","minimum":0,"maximum":9}},"required":["resourceId"]},"description":"Replaces the resources that can perform the service. Resources of other instances are ignored."}}},"Resource":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"code":{"description":"Up to five characters; starts the booking reference.","type":["string","null"]},"sortOrder":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"isActive":{"type":"boolean"}},"required":["id","name","code","sortOrder","isActive"]},"WorkHours":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"weekday":{"type":"integer","minimum":1,"maximum":7,"description":"ISO weekday: 1 Monday … 7 Sunday."},"startTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},"endTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},"validFrom":{"anyOf":[{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},{"type":"null"}],"description":"First day the hours apply. Null: from the beginning."},"validTo":{"anyOf":[{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},{"type":"null"}],"description":"Last day the hours apply. Null: until further notice."}},"required":["id","weekday","startTime","endTime","validFrom","validTo"]},"WorkHoursCreate":{"type":"object","properties":{"weekdays":{"minItems":1,"maxItems":7,"type":"array","items":{"type":"integer","minimum":1,"maximum":7},"description":"The same hours on each of these weekdays.","example":[1,2,3,4,5]},"startTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},"endTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},"validFrom":{"default":null,"anyOf":[{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},{"type":"null"}]},"validTo":{"default":null,"anyOf":[{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},{"type":"null"}]}},"required":["weekdays","startTime","endTime"]},"WorkHoursReplace":{"type":"object","properties":{"entries":{"maxItems":200,"type":"array","items":{"type":"object","properties":{"weekday":{"type":"integer","minimum":1,"maximum":7},"startTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},"endTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},"validFrom":{"default":null,"anyOf":[{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},{"type":"null"}]},"validTo":{"default":null,"anyOf":[{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},{"type":"null"}]}},"required":["weekday","startTime","endTime"]},"description":"The resource's complete work hours. Everything not listed is removed. An empty list removes all."}},"required":["entries"]},"ScheduleException":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"resourceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Null: applies to the whole instance, every resource."},"startDate":{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},"endDate":{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},"kind":{"type":"string","enum":["closed","custom_hours"]},"startTime":{"anyOf":[{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},{"type":"null"}]},"endTime":{"anyOf":[{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},{"type":"null"}]},"note":{"type":["string","null"]}},"required":["id","resourceId","startDate","endDate","kind","startTime","endTime","note"]},"ScheduleExceptionCreate":{"type":"object","properties":{"resourceId":{"default":null,"description":"Null or left out: the whole instance.","anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"startDate":{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},"endDate":{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},"kind":{"type":"string","enum":["closed","custom_hours"],"description":"`closed`: no work at all. `custom_hours`: only between startTime and endTime."},"startTime":{"default":null,"description":"Required for custom_hours.","anyOf":[{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},{"type":"null"}]},"endTime":{"default":null,"description":"Required for custom_hours.","anyOf":[{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Time of day, HH:MM (24 h).","example":"08:00"},{"type":"null"}]},"note":{"default":null,"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}},"required":["startDate","endDate","kind"]},"Customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"firstName":{"type":"string"},"lastName":{"type":["string","null"]},"phone":{"type":["string","null"]},"email":{"type":["string","null"]}},"required":["id","firstName","lastName","phone","email"]},"Booking":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string","description":"Shown to the customer, e.g. A1042."},"status":{"type":"string","enum":["confirmed","cancelled"]},"day":{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},"startsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 with offset.","example":"2026-10-05T08:00:00+02:00"},{"type":"null"}],"description":"Null for bookings by the day (day_capacity)."},"endsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 with offset.","example":"2026-10-05T08:00:00+02:00"},{"type":"null"}]},"service":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"name":{"type":"string"}},"required":["id","name"]},"addons":{"type":"array","items":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"name":{"type":"string"}},"required":["id","name"]}},"durationMinutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"price":{"type":"number"},"resource":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"required":["id","name"]},"customer":{"anyOf":[{"$ref":"#/components/schemas/Customer"},{"type":"null"}]},"customerNote":{"type":["string","null"]},"internalNote":{"type":["string","null"]},"smsOptIn":{"type":"boolean"},"emailOptIn":{"type":"boolean"},"source":{"type":"string","enum":["admin","api","public","sync"]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 with offset.","example":"2026-10-05T08:00:00+02:00"},"cancelledAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 with offset.","example":"2026-10-05T08:00:00+02:00"},{"type":"null"}]},"cancelReason":{"type":["string","null"]}},"required":["id","reference","status","day","startsAt","endsAt","service","addons","durationMinutes","price","resource","customer","customerNote","internalNote","smsOptIn","emailOptIn","source","createdAt","cancelledAt","cancelReason"]},"CustomerCreate":{"type":"object","properties":{"firstName":{"type":"string","minLength":1,"maxLength":100},"lastName":{"default":null,"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"phone":{"default":null,"anyOf":[{"type":"string","maxLength":40,"description":"Mobile number. Swedish numbers may be given without +46.","example":"0701234567"},{"type":"null"}]},"email":{"default":null,"anyOf":[{"type":"string","maxLength":320,"format":"email"},{"type":"null"}]}},"required":["firstName"],"description":"A customer with the same mobile number is reused as it is; otherwise a new customer is created."},"BookingCreate":{"type":"object","properties":{"serviceId":{"type":"string","format":"uuid"},"addonIds":{"default":[],"maxItems":20,"type":"array","items":{"type":"string","format":"uuid"}},"day":{"description":"Required for services booked by the day. Ignored for time slots, where the day follows startsAt.","type":"string","format":"date","example":"2026-10-05"},"startsAt":{"description":"Required for services booked by time slot.","type":"string","format":"date-time","example":"2026-10-05T08:00:00+02:00"},"resourceId":{"description":"A specific resource. Left out: any free resource that can do the service.","type":"string","format":"uuid"},"customerId":{"description":"An existing customer. Give this or `customer`.","type":"string","format":"uuid"},"customer":{"$ref":"#/components/schemas/CustomerCreate"},"customerNote":{"default":null,"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"internalNote":{"default":null,"description":"Only visible to staff.","anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"smsOptIn":{"default":true,"description":"Send SMS confirmation and reminder, if the instance sends them.","type":"boolean"},"emailOptIn":{"default":true,"description":"Send e-mail confirmation and reminder, if the instance sends them.","type":"boolean"}},"required":["serviceId"]},"BookingUpdate":{"type":"object","properties":{"day":{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},"startsAt":{"type":"string","format":"date-time","description":"ISO 8601 with offset.","example":"2026-10-05T08:00:00+02:00"},"resourceId":{"description":"Moves the booking to this resource. Null: any free resource. Left out: keeps the current one if it is free.","anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"customerNote":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"internalNote":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"smsOptIn":{"type":"boolean"},"emailOptIn":{"type":"boolean"}},"description":"Giving day, startsAt or resourceId moves the booking; the other fields are updated as given."},"BookingCancel":{"type":"object","properties":{"reason":{"default":null,"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]}}},"AvailableDay":{"type":"object","properties":{"day":{"type":"string","format":"date","description":"A date, YYYY-MM-DD, in the instance's time zone.","example":"2026-10-05"},"free":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Free start times (time_slots), or free minutes on the best resource (day_capacity)."}},"required":["day","free"]},"AvailableSlot":{"type":"object","properties":{"startsAt":{"type":"string","format":"date-time","description":"ISO 8601 with offset.","example":"2026-10-05T08:00:00+02:00"},"resourceId":{"type":"string","format":"uuid"}},"required":["startsAt","resourceId"]},"Error":{"type":"object","properties":{"error":{"type":"string","description":"Machine-readable code, e.g. `not_found`, `missing_scope`, `no_capacity`."},"detail":{"description":"Human-readable detail, when there is one.","type":"string"}},"required":["error"]}}}}