{"openapi":"3.1.0","info":{"title":"Scaling Firm Booking API","version":"1.0.0-preview","description":"Versioned booking, deposit and catalogue API. Tenant identity always comes from the credential. Money is in integer minor units. Every mutating response reports outcome: accepted, pending, completed or requires_staff_action. MCP tools at POST /mcp call the same operations."},"servers":[{"url":"http://127.0.0.1:4310","description":"Private local preview"}],"paths":{"/api/v1/public/{slug}/services":{"get":{"summary":"List services for a business","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/providers":{"get":{"summary":"List staff and locations","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/availability":{"get":{"summary":"Search provisional slots","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"service_ids","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated services for a multi-service visit"},{"name":"location_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"service_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"staff_id","in":"query","required":false,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/capabilities":{"get":{"summary":"Booking features this business supports (multi-service visits, calendar files)","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/bookings":{"post":{"summary":"Create a held booking awaiting deposit (idempotent)","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldRequest"}}}}}},"/api/v1/public/{slug}/bookings/{reference}":{"get":{"summary":"Read a booking with its manage token","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"},"description":"Manage token. May also be sent as X-Manage-Token or in the body."}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/bookings/{reference}/deposit":{"post":{"summary":"Create or return the hosted deposit checkout","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"},"description":"Manage token. May also be sent as X-Manage-Token or in the body."}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenBody"}}}}}},"/api/v1/public/{slug}/bookings/{reference}/payment":{"get":{"summary":"Authoritative payment status","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"},"description":"Manage token. May also be sent as X-Manage-Token or in the body."}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/bookings/{reference}/reschedule":{"post":{"summary":"Reschedule within policy","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"},"description":"Manage token. May also be sent as X-Manage-Token or in the body."}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleRequest"}}}}}},"/api/v1/public/{slug}/bookings/{reference}/cancel":{"post":{"summary":"Cancel within policy","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"},"description":"Manage token. May also be sent as X-Manage-Token or in the body."}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRequest"}}}}}},"/api/v1/public/{slug}/bookings/{reference}/cancel-preview":{"get":{"summary":"Read cancellation policy and stored money without mutation","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"},"description":"Manage token. May also be sent as X-Manage-Token or in the body."}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/messages":{"post":{"summary":"Leave a message for the team","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageRequest"}}}}}},"/api/v1/public/{slug}/handover":{"post":{"summary":"Request a human handover","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverRequest"}}}}}},"/api/v1/me":{"get":{"summary":"Current credential, scopes and MCP tools","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/services":{"get":{"summary":"List services","x-required-scope":"catalogue:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a service","x-required-scope":"catalogue:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}}}},"/api/v1/services/{id}":{"patch":{"summary":"Update a service","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}}}},"/api/v1/providers":{"get":{"summary":"List staff, hours and locations","x-required-scope":"catalogue:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/providers/{id}/working-hours":{"put":{"summary":"Replace weekly hours","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkingHours"}}}}}},"/api/v1/providers/{id}/time-off":{"post":{"summary":"Add leave or a block","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeOff"}}}}}},"/api/v1/availability":{"get":{"summary":"Search slots","x-required-scope":"bookings:read","parameters":[{"name":"service_ids","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated services for a multi-service visit"},{"name":"location_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"service_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"staff_id","in":"query","required":false,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/capabilities":{"get":{"summary":"Booking features this business supports","x-required-scope":"bookings:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/bookings":{"get":{"summary":"List bookings (from, to as YYYY-MM-DD)","x-required-scope":"bookings:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a held booking","x-required-scope":"bookings:write","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldRequest"}}}}}},"/api/v1/bookings/{reference}":{"get":{"summary":"Read a booking with history","x-required-scope":"bookings:read","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/bookings/{reference}/deposit":{"post":{"summary":"Create deposit request","x-required-scope":"bookings:write","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/bookings/{reference}/payment":{"get":{"summary":"Payment status","x-required-scope":"payments:read","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/bookings/{reference}/reschedule":{"post":{"summary":"Reschedule","x-required-scope":"bookings:write","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleRequest"}}}}}},"/api/v1/bookings/{reference}/cancel":{"post":{"summary":"Cancel","x-required-scope":"bookings:cancel","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRequest"}}}}}},"/api/v1/bookings/{reference}/cancel-preview":{"get":{"summary":"Read cancellation preview and saved version without mutation","x-required-scope":"bookings:cancel","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/bookings/{reference}/outcome":{"post":{"summary":"Mark completed or no-show","x-required-scope":"bookings:write","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutcomeRequest"}}}}}},"/api/v1/bookings/{reference}/refund":{"post":{"summary":"Refund a settled deposit (owner approval)","x-required-scope":"payments:refund","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundRequest"}}}}}},"/api/v1/bookings/{reference}/adjust":{"post":{"summary":"Move or resize a booking with a version check (drag and drop)","x-required-scope":"bookings:write","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"new_start":{"type":"string"},"new_end":{"type":"string"},"staff_id":{"type":"string"},"resource_id":{"type":"string"},"expected_version":{"type":"integer"}},"required":["new_start","expected_version"]}}}}}},"/api/v1/bookings/{reference}/approval":{"post":{"summary":"Approve or decline a booking that needs staff approval","x-required-scope":"bookings:write","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["approve","decline"]},"note":{"type":"string"}},"required":["decision"]}}}}}},"/api/v1/bookings/{reference}/manage-link":{"post":{"summary":"Rotate the customer manage link; the old link stops working","x-required-scope":"bookings:write","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/bookings/{reference}/manage-link":{"post":{"summary":"Customer: rotate your manage link (needs the current link token)","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"manage_token":{"type":"string"}},"required":[]}}}}}},"/api/v1/calendar":{"get":{"summary":"Calendar for a date range: bookings, working hours, leave, closures and external busy time (from, to, location_id, staff_ids, resource_ids)","x-required-scope":"bookings:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/approvals":{"get":{"summary":"Bookings waiting for staff approval","x-required-scope":"bookings:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/recurring-series":{"post":{"summary":"Staff: create a weekly, fortnightly or monthly series; each occurrence is held independently","x-required-scope":"bookings:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"service_id":{"type":"string"},"staff_id":{"type":"string"},"location_id":{"type":"string"},"start":{"type":"string"},"frequency":{"type":"string","enum":["weekly","fortnightly","monthly"]},"count":{"type":"integer"},"until":{"type":"string"},"client":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"}},"required":["name","email"]},"add_on_ids":{"type":"array","items":{"type":"string"}},"consent":{"type":"boolean"},"intake_answer":{"type":"string"},"idempotency_key":{"type":"string"}},"required":["service_id","start","frequency","client","idempotency_key"]}}}}}},"/api/v1/recurring-series/{id}/cancel":{"post":{"summary":"Cancel the rest of a series (from_reference onwards, or all future occurrences)","x-required-scope":"bookings:cancel","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"from_reference":{"type":"string"},"reason":{"type":"string"}},"required":[]}}}}}},"/api/v1/locations":{"get":{"summary":"Locations with opening hours","x-required-scope":"catalogue:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a location","x-required-scope":"catalogue:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"address":{"type":"string"},"phone":{"type":"string"},"active":{"type":"boolean"},"sort":{"type":"integer"}},"required":["name"]}}}}}},"/api/v1/resources":{"get":{"summary":"Rooms and equipment","x-required-scope":"catalogue:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add a room or piece of equipment","x-required-scope":"catalogue:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"resource_type":{"type":"string"},"location_id":{"type":"string"},"active":{"type":"boolean"},"notes":{"type":"string"},"sort":{"type":"integer"}},"required":["name","resource_type","location_id"]}}}}}},"/api/v1/closures":{"post":{"summary":"Add a closure for all locations or one; returns clashing bookings","x-required-scope":"catalogue:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"location_id":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"reason":{"type":"string"}},"required":["starts_at","ends_at"]}}}}},"get":{"summary":"Company closures (from, to)","x-required-scope":"catalogue:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/messages":{"post":{"summary":"Capture a message","x-required-scope":"inbox:write","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageRequest"}}}}}},"/api/v1/handover":{"post":{"summary":"Request a human handover","x-required-scope":"inbox:write","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverRequest"}}}}}},"/api/v1/inbox":{"get":{"summary":"Messages, handovers and staff actions","x-required-scope":"inbox:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/inbox/{id}/resolve":{"post":{"summary":"Resolve an inbox item","x-required-scope":"inbox:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/reports/summary":{"get":{"summary":"Owner report","x-required-scope":"reports:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/notifications":{"get":{"summary":"Notification outbox (simulated delivery)","x-required-scope":"bookings:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/audit":{"get":{"summary":"Audit trail","x-required-scope":"audit:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/api-keys":{"get":{"summary":"List API keys","x-required-scope":"keys:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a scoped API key","x-required-scope":"keys:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRequest"}}}}}},"/api/v1/api-keys/{id}/revoke":{"post":{"summary":"Revoke a key","x-required-scope":"keys:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhook-endpoints":{"get":{"summary":"List webhook endpoints and deliveries","x-required-scope":"settings:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add a signed webhook endpoint (loopback in sandbox)","x-required-scope":"settings:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookRequest"}}}}}},"/api/v1/users":{"post":{"summary":"Add a staff or admin user","x-required-scope":"keys:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tenants":{"post":{"summary":"Create a sandbox business and owner account","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantRequest"}}}}}},"/api/v1/auth/login":{"post":{"summary":"Staff sign in (sets an HttpOnly session cookie)","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/payments/webhooks/{provider}":{"post":{"summary":"Signed payment provider events (simpay, stripe_test)","x-required-scope":"signature","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/health":{"get":{"summary":"Health check","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden, tenant mismatch or policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot unavailable, invalid state or idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Consent, intake or prerequisite missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/mfa":{"post":{"summary":"Complete sign-in with a TOTP or recovery code (after MFA_REQUIRED)","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/choose":{"post":{"summary":"Choose a business after signing in with an identity that has several memberships","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/password/forgot":{"post":{"summary":"Request a password reset email (always answers the same way)","x-required-scope":"none","parameters":[],"security":[],"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/password/reset":{"post":{"summary":"Set a new password with a reset token; revokes every session","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/email/verify":{"post":{"summary":"Verify an email address with a verification token","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/invitations/preview":{"post":{"summary":"Show who an invitation is for and which business","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/invitations/accept":{"post":{"summary":"Accept an invitation: set a password, or link the signed-in account","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/providers":{"get":{"summary":"Sign-in methods available","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/csrf":{"get":{"summary":"CSRF token for cookie-authenticated writes","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/memberships":{"get":{"summary":"Businesses the signed-in person belongs to","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/switch":{"post":{"summary":"Switch business (new session for the chosen membership)","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/security":{"get":{"summary":"Security overview of the signed-in person","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/sessions":{"get":{"summary":"List my active sessions in this business","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/sessions/{id}/revoke":{"post":{"summary":"Revoke one of my sessions","x-required-scope":"any","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/sessions/revoke-others":{"post":{"summary":"Revoke all my other sessions, in every business","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/email/send-verification":{"post":{"summary":"Send a verification email","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/mfa/enrol":{"post":{"summary":"Start TOTP enrolment (secret shown once)","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/mfa/confirm":{"post":{"summary":"Confirm TOTP enrolment with a code; returns 10 recovery codes once","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/mfa/disable":{"post":{"summary":"Turn off TOTP (password and a current code required)","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/mfa/recovery-codes":{"post":{"summary":"Replace recovery codes (password and a current code required)","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/oidc/google/link":{"post":{"summary":"Start linking a Google account to the signed-in person","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me/oidc/google/unlink":{"post":{"summary":"Unlink Google (needs a password on the account)","x-required-scope":"any","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/roles":{"get":{"summary":"Roles and their capabilities","x-required-scope":"team:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/members":{"get":{"summary":"List team members and roles","x-required-scope":"team:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/members/{id}/role":{"post":{"summary":"Change a member role (not above your own; owners only for owner)","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/members/{id}/deactivate":{"post":{"summary":"Deactivate a member: ends sessions and revokes keys they created","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/members/{id}/reactivate":{"post":{"summary":"Reactivate a member","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/invitations":{"get":{"summary":"List invitations","x-required-scope":"team:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Invite a person by email (single-use link, 7 days)","x-required-scope":"team:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/invitations/{id}/revoke":{"post":{"summary":"Revoke a pending invitation","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations":{"get":{"summary":"Integration catalogue with connection status, last success and failure, and setup gates","x-required-scope":"integrations:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations/jobs":{"get":{"summary":"Background jobs (filter by status=dead to see failures needing attention)","x-required-scope":"integrations:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations/jobs/{id}/retry":{"post":{"summary":"Retry a dead job","x-required-scope":"integrations:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations/{provider}":{"get":{"summary":"One integration with its connection status","x-required-scope":"integrations:read","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations/{provider}/connect":{"post":{"summary":"Connect or reconfigure an integration (status stays pending until a test completes)","x-required-scope":"integrations:write","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Provider settings. SMTP: host, port, security (tls | starttls | none for a loopback test sink), username, password, from_name, from_address, reply_to, hourly_quota. Secrets are encrypted and never returned.","additionalProperties":true}}}}}},"/api/v1/integrations/{provider}/test":{"post":{"summary":"Run a real operation and record it in the health log","x-required-scope":"integrations:write","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations/{provider}/disconnect":{"post":{"summary":"Disconnect (forget_credentials: true also deletes the saved secret)","x-required-scope":"integrations:write","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations/{provider}/reconnect":{"post":{"summary":"Reconnect using the saved settings, then test","x-required-scope":"integrations:write","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations/{provider}/events":{"get":{"summary":"Health log of operations for this integration","x-required-scope":"integrations:read","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/settings":{"get":{"summary":"SMTP connection status, quota and outbox counts (no secrets)","x-required-scope":"comms:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Save SMTP settings (same as POST /api/v1/integrations/smtp/connect)","x-required-scope":"integrations:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/settings/test":{"post":{"summary":"SMTP connection check (not delivery proof)","x-required-scope":"integrations:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/templates":{"get":{"summary":"Available email templates","x-required-scope":"comms:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/preview":{"post":{"summary":"Render a template with a synthetic sample booking and tenant branding","x-required-scope":"comms:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/test":{"post":{"summary":"Queue a test email (sandbox: local capture server only)","x-required-scope":"comms:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/messages":{"post":{"summary":"Queue a template email (marketing needs comms:bulk and consent)","x-required-scope":"comms:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"Outbox with status, attempts and SMTP responses","x-required-scope":"comms:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/event-failures":{"get":{"summary":"Booking and payment events whose email handling failed: retrying with backoff, or dead-lettered after the attempt limit (state=retrying|dead)","x-required-scope":"comms:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/event-failures/{id}/retry":{"post":{"summary":"Request a new dead-letter attempt after correcting its cause. Current stored facts and existing message dedupe still apply.","x-required-scope":"comms:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["idempotency_key","reason"],"properties":{"idempotency_key":{"type":"string","minLength":8,"maxLength":100},"reason":{"type":"string","minLength":1,"maxLength":500}}}}}}}},"/api/v1/email/messages/{id}":{"get":{"summary":"One message with its rendered content","x-required-scope":"comms:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/messages/{id}/retry":{"post":{"summary":"Retry a failed or held message","x-required-scope":"comms:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/messages/{id}/cancel":{"post":{"summary":"Cancel a queued message","x-required-scope":"comms:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/messages/{id}/delivery":{"post":{"summary":"Record a delivery report or bounce (outcome delivered | bounced)","x-required-scope":"comms:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/suppressions":{"get":{"summary":"Unsubscribed and bounced addresses","x-required-scope":"comms:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Suppress an address (scope marketing or all)","x-required-scope":"comms:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/suppressions/{id}":{"delete":{"summary":"Remove a suppression","x-required-scope":"comms:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/email/unsubscribe/{token}":{"get":{"summary":"Unsubscribe confirmation page","x-required-scope":"none","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"One-click unsubscribe from marketing email","x-required-scope":"none","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/payments/stripe/connection":{"get":{"summary":"Stripe Connect merchant connection status (mode, account, charges enabled, requirements)","x-required-scope":"payments:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/payments/stripe/onboarding":{"post":{"summary":"Create the connected account if needed and return an onboarding link","x-required-scope":"integrations:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"return_url":{"type":"string"},"refresh_url":{"type":"string"}}}}}}}},"/api/v1/payments/stripe/refresh":{"post":{"summary":"Re-read the connected account from Stripe and update its status","x-required-scope":"integrations:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/payments/settings":{"put":{"summary":"Choose the card payment provider: simpay (local simulator) or stripe (Connect)","x-required-scope":"settings:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["provider"],"properties":{"provider":{"type":"string","enum":["simpay","stripe"]}}}}}}}},"/api/v1/payments/reconcile":{"post":{"summary":"Re-read open checkouts and refunds from the provider, fix local state and queue refund decisions for cancelled or expired bookings that still hold money","x-required-scope":"payments:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"payment_request_id":{"type":"string"}}}}}}}},"/api/v1/payments/reconciliation":{"get":{"summary":"Reconciliation and refund decision queue (late money, disputes, mismatches, failed refunds)","x-required-scope":"payments:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/payments/reconciliation/{id}/decision":{"post":{"summary":"Decide a reconciliation item: refund, keep, rebooked or dismissed (note required)","x-required-scope":"payments:refund","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["decision","note"],"properties":{"decision":{"type":"string","enum":["refund","keep","rebooked","dismissed"]},"note":{"type":"string","minLength":5},"amount_minor":{"type":"integer"}}}}}}}},"/api/v1/payments/webhooks/stripe":{"post":{"summary":"Signed Stripe Connect events (verified with the SDK against the raw body; bound to the mapped connected account)","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/payments":{"get":{"summary":"List payment requests with display state","x-required-scope":"payments:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/payments/{id}":{"get":{"summary":"One payment request with refunds, provider events and ledger rows","x-required-scope":"payments:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/payments/{id}/refunds":{"post":{"summary":"Refund part or all of a settled payment (owner approval note; pending until the provider confirms)","x-required-scope":"payments:refund","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["approval_note"],"properties":{"amount_minor":{"type":"integer","minimum":1,"description":"Defaults to the full refundable amount"},"approval_note":{"type":"string","minLength":5},"idempotency_key":{"type":"string"}}}}}}}},"/api/v1/bookings/{reference}/payment-requests":{"post":{"summary":"Create or return a deposit, full payment or remaining balance checkout (balance needs payments:write)","x-required-scope":"bookings:write","parameters":[{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"purpose":{"type":"string","enum":["deposit","full","balance"]},"idempotency_key":{"type":"string"}}}}}}}},"/api/v1/public/{slug}/bookings/{reference}/payment-requests":{"post":{"summary":"Client: pay the deposit or the full price for a held booking (manage token required)","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["manage_token"],"properties":{"purpose":{"type":"string","enum":["deposit","full"]},"manage_token":{"type":"string"},"idempotency_key":{"type":"string"}}}}}}}},"/api/v1/billing":{"get":{"summary":"Current SHINIMINI plan, subscription state, entitlements and synthetic test plans","x-required-scope":"billing:manage","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/billing/checkout":{"post":{"summary":"Start subscription Checkout on the platform account (synthetic test prices; live refused)","x-required-scope":"billing:manage","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["plan_id"],"properties":{"plan_id":{"type":"string"}}}}}}}},"/api/v1/billing/portal":{"post":{"summary":"Open the billing customer portal","x-required-scope":"billing:manage","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/billing/webhooks/stripe":{"post":{"summary":"Signed Stripe billing events for SHINIMINI subscriptions (separate endpoint and secret)","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/recurring-series/{id}":{"get":{"summary":"Read a series and its occurrences","x-required-scope":"bookings:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/locations/{id}":{"patch":{"summary":"Update or deactivate a location","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"address":{"type":"string"},"phone":{"type":"string"},"active":{"type":"boolean"},"sort":{"type":"integer"},"expected_version":{"type":"integer"}},"required":[]}}}}},"delete":{"summary":"Delete a location, or deactivate it when referenced","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expected_version":{"type":"integer"}},"required":[]}}}}}},"/api/v1/locations/{id}/hours":{"put":{"summary":"Replace location opening hours (empty list removes the restriction)","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hours":{"type":"array","items":{"type":"object","properties":{"weekday":{"type":"integer"},"start_minute":{"type":"integer"},"end_minute":{"type":"integer"}},"required":["weekday","start_minute","end_minute"]}},"expected_version":{"type":"integer"}},"required":["hours"]}}}}}},"/api/v1/resources/{id}":{"patch":{"summary":"Update or deactivate a resource","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"resource_type":{"type":"string"},"location_id":{"type":"string"},"active":{"type":"boolean"},"notes":{"type":"string"},"sort":{"type":"integer"},"expected_version":{"type":"integer"}},"required":[]}}}}},"delete":{"summary":"Delete a resource, or deactivate it when it has bookings","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expected_version":{"type":"integer"}},"required":[]}}}}}},"/api/v1/closures/{id}":{"delete":{"summary":"Remove a closure","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/categories":{"get":{"summary":"Service categories","x-required-scope":"catalogue:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a category","x-required-scope":"catalogue:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"sort":{"type":"integer"},"visible":{"type":"boolean"}},"required":["name"]}}}}}},"/api/v1/public/{slug}/categories":{"get":{"summary":"Visible service categories","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/categories/{id}":{"patch":{"summary":"Update a category (order, visibility)","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"sort":{"type":"integer"},"visible":{"type":"boolean"},"expected_version":{"type":"integer"}},"required":[]}}}}},"delete":{"summary":"Delete a category, or hide it when services use it","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expected_version":{"type":"integer"}},"required":[]}}}}}},"/api/v1/add-ons":{"get":{"summary":"Add-ons with the services they attach to","x-required-scope":"catalogue:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create an add-on (extra minutes and price)","x-required-scope":"catalogue:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"extra_minutes":{"type":"integer"},"price_minor":{"type":"integer"},"active":{"type":"boolean"},"sort":{"type":"integer"},"service_ids":{"type":"array","items":{"type":"string"}}},"required":["name"]}}}}}},"/api/v1/add-ons/{id}":{"patch":{"summary":"Update an add-on","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"extra_minutes":{"type":"integer"},"price_minor":{"type":"integer"},"active":{"type":"boolean"},"sort":{"type":"integer"},"service_ids":{"type":"array","items":{"type":"string"}},"expected_version":{"type":"integer"}},"required":[]}}}}}},"/api/v1/services/{id}/settings":{"patch":{"summary":"Service settings: category, online visibility, approval, notice, booking window, start windows, add-ons, image","x-required-scope":"catalogue:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"category_id":{"type":"string"},"online":{"type":"boolean"},"requires_approval":{"type":"boolean"},"approval_hours":{"type":"integer"},"min_notice_minutes":{"type":"integer"},"max_advance_days":{"type":"integer"},"deposit_percent":{"type":"integer"},"resource_type":{"type":"string"},"summary":{"type":"string"},"image_url":{"type":"string"},"image_alt":{"type":"string"},"image_licence":{"type":"string"},"allow_external_image":{"type":"boolean"},"windows":{"type":"array","items":{"type":"object","properties":{"weekday":{"type":"integer"},"start_minute":{"type":"integer"},"end_minute":{"type":"integer"}},"required":["weekday","start_minute","end_minute"]}},"add_on_ids":{"type":"array","items":{"type":"string"}},"expected_version":{"type":"integer"}},"required":[]}}}}}},"/api/v1/business":{"get":{"summary":"Saved business identity, branding and booking policies","x-required-scope":"catalogue:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update business setup with version and replay protection","x-required-scope":"settings:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"contact_email":{"type":"string"},"phone":{"type":"string"},"timezone":{"type":"string"},"brand_colour":{"type":"string"},"deposit_percent":{"type":"integer"},"hold_minutes":{"type":"integer"},"min_notice_minutes":{"type":"integer"},"max_advance_days":{"type":"integer"},"customer_cancel_cutoff_hours":{"type":"integer"},"slot_step_minutes":{"type":"integer"},"expected_version":{"type":"integer"},"idempotency_key":{"type":"string"}},"required":["expected_version","idempotency_key"],"additionalProperties":false}}}}}},"/api/v1/calendar/connections":{"get":{"summary":"Calendar sync status per staff member: connections, calendars, sync health, push channels and feed","x-required-scope":"integrations:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/connections/{provider}/start":{"post":{"summary":"Start connecting a staff calendar (authorisation code with PKCE). Returns the consent URL","x-required-scope":"integrations:write","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["staff_id"],"properties":{"staff_id":{"type":"string","format":"uuid"},"import_busy":{"type":"boolean"},"export_events":{"type":"boolean"},"login_hint":{"type":"string"}}}}}}}},"/api/v1/calendar/oauth/{provider}/callback":{"get":{"summary":"OAuth redirect target. Validates state, exchanges the code and saves the encrypted refresh token","x-required-scope":"integrations:write","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/connections/{id}/calendars":{"get":{"summary":"List the connected account's calendars (live provider call) with current choices","x-required-scope":"integrations:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/connections/{id}/settings":{"put":{"summary":"Choose calendars to read for busy time, the calendar that receives appointments, and the sync policy","x-required-scope":"integrations:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"read_calendar_ids":{"type":"array","items":{"type":"string"}},"write_calendar_id":{"type":["string","null"]},"import_busy":{"type":"boolean"},"export_events":{"type":"boolean"},"title_format":{"type":"string","enum":["service_and_first_name","service_only","busy_only"]}}}}}}}},"/api/v1/calendar/connections/{id}/resync":{"post":{"summary":"Queue a full resync of busy time for this connection","x-required-scope":"integrations:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/connections/{id}/disconnect":{"post":{"summary":"Disconnect: stop push channels, revoke the grant, delete the saved credential and imported busy time","x-required-scope":"integrations:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/busy":{"get":{"summary":"Imported external busy blocks (staff_id, from, to)","x-required-scope":"integrations:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/feeds/{staff_id}/rotate":{"post":{"summary":"Create or rotate a staff member's one-way ICS feed URL (shown once; the old URL stops working)","x-required-scope":"integrations:write","parameters":[{"name":"staff_id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/feeds/{staff_id}":{"delete":{"summary":"Revoke a staff member's ICS feed URL","x-required-scope":"integrations:write","parameters":[{"name":"staff_id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/feeds/{file}":{"get":{"summary":"One-way ICS subscription feed (secret token in the URL)","x-required-scope":"none","parameters":[{"name":"file","in":"path","required":true,"schema":{"type":"string"}}],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/public/{slug}/bookings/{reference}/ics":{"get":{"summary":"Calendar file for one booking. Requires manage_token. METHOD:CANCEL when cancelled","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/push/google":{"post":{"summary":"Google Calendar push notification. Validates channel id and token, then queues a fetch","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/calendar/push/microsoft":{"post":{"summary":"Microsoft Graph change notification (and subscription validation). Validates subscription id and clientState, then queues a fetch","x-required-scope":"none","parameters":[],"security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team":{"get":{"summary":"List the team with services, staff prices and durations, locations and photos","x-required-scope":"team:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add a team member","x-required-scope":"team:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"display_name":{"type":"string"},"title":{"type":"string"},"bio":{"type":"string"},"email":{"type":"string"},"colour":{"type":"string","example":"#6b4f8a"},"photo_url":{"type":"string","description":"https only; needs photo_licence"},"photo_licence":{"type":"string"},"location_id":{"type":"string","format":"uuid"},"active":{"type":"boolean"},"sort":{"type":"integer"},"expected_version":{"type":"integer"},"notification_prefs":{"type":"object","description":"Events booking_created, booking_rescheduled, booking_cancelled, daily_schedule, leave_changes, client_message; channels email, sms"},"force_reassign":{"type":"object","description":"{ to_staff_id } or { assignments: [{ booking_id, staff_id }] } when deactivating someone with future bookings"}},"required":["name"]}}}}}},"/api/v1/team/{id}":{"get":{"summary":"One team member with hours and leave","x-required-scope":"team:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update a profile. Practitioners may edit only their own profile and notification preferences. Deactivating someone with future bookings returns requires_staff_action unless force_reassign is given","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"display_name":{"type":"string"},"title":{"type":"string"},"bio":{"type":"string"},"email":{"type":"string"},"colour":{"type":"string","example":"#6b4f8a"},"photo_url":{"type":"string","description":"https only; needs photo_licence"},"photo_licence":{"type":"string"},"location_id":{"type":"string","format":"uuid"},"active":{"type":"boolean"},"sort":{"type":"integer"},"expected_version":{"type":"integer","minimum":1},"notification_prefs":{"type":"object","description":"Events booking_created, booking_rescheduled, booking_cancelled, daily_schedule, leave_changes, client_message; channels email, sms"},"force_reassign":{"type":"object","description":"{ to_staff_id } or { assignments: [{ booking_id, staff_id }] } when deactivating someone with future bookings"}}}}}}}},"/api/v1/team/{id}/hours":{"get":{"summary":"Weekly shifts with breaks and the bookable intervals availability uses","x-required-scope":"team:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Replace weekly shifts (split shifts, breaks, location per shift). Future bookings outside the new hours return requires_staff_action unless accept_conflicts is true","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["shifts"],"properties":{"shifts":{"type":"array","items":{"type":"object","required":["weekday","start","end"],"properties":{"weekday":{"type":"integer","minimum":0,"maximum":6},"location_id":{"type":"string","format":"uuid"},"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"17:00"},"breaks":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}}}}}}},"accept_conflicts":{"type":"boolean"},"expected_version":{"type":"integer","minimum":1}}}}}}}},"/api/v1/team/{id}/leave":{"post":{"summary":"Record leave or a one-off block. Clashing bookings return requires_staff_action unless accept_conflicts is true","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"type":"string"},"ends_at":{"type":"string"},"date":{"type":"string","description":"All-day leave from this date"},"end_date":{"type":"string"},"kind":{"type":"string","enum":["leave","sick","training","block","other"]},"reason":{"type":"string"},"accept_conflicts":{"type":"boolean"}}}}}}},"delete":{"summary":"Remove leave by ?leave_id=","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/{id}/leave/{leaveId}":{"delete":{"summary":"Remove one leave entry","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"leaveId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/{id}/services":{"put":{"summary":"Replace eligible services with optional staff price and duration","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["services"],"properties":{"services":{"type":"array","items":{"type":"object","required":["service_id"],"properties":{"service_id":{"type":"string","format":"uuid"},"price_minor":{"type":["integer","null"]},"duration_minutes":{"type":["integer","null"]}}}},"expected_version":{"type":"integer","minimum":1}}}}}}}},"/api/v1/team/{id}/photo":{"post":{"summary":"Upload a profile photo (JPEG, PNG or WebP, checked by content). Practitioners may upload their own","x-required-scope":"team:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content_type","data_base64"],"properties":{"content_type":{"type":"string"},"data_base64":{"type":"string"},"filename":{"type":"string"},"photo_licence":{"type":"string"}}}}}}},"get":{"summary":"Download the uploaded profile photo (authenticated, private)","x-required-scope":"team:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/clients":{"get":{"summary":"Search clients by name, email or phone (paginated). Practitioners see only clients booked with them","x-required-scope":"clients:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Add a client. Possible duplicates are flagged and returned","x-required-scope":"clients:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"}}}}}}}},"/api/v1/clients/duplicates":{"get":{"summary":"Duplicate review list: same email, same phone or flagged at booking","x-required-scope":"clients:merge","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/clients/duplicates/dismiss":{"post":{"summary":"Mark a pair as not duplicates","x-required-scope":"clients:merge","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id","other_id"],"properties":{"client_id":{"type":"string"},"other_id":{"type":"string"}}}}}}}},"/api/v1/clients/merge":{"post":{"summary":"Owner or manager: merge two clients in one transaction. Bookings, payment links, consents, notes, tags, uploads and intake move to the survivor; the other record stays as a tombstone","x-required-scope":"clients:merge","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["survivor_id","merged_id","confirm"],"properties":{"survivor_id":{"type":"string"},"merged_id":{"type":"string"},"confirm":{"type":"boolean"},"keep":{"type":"object","required":[],"properties":{"name":{"type":"string","enum":["survivor","merged"]},"email":{"type":"string","enum":["survivor","merged"]},"phone":{"type":"string","enum":["survivor","merged"]}}}}}}}}}},"/api/v1/clients/settings":{"get":{"summary":"Client privacy settings","x-required-scope":"clients:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Choose whether practitioners see client contact details","x-required-scope":"settings:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["practitioner_contact_visible"],"properties":{"practitioner_contact_visible":{"type":"boolean"}}}}}}}},"/api/v1/client-tags":{"get":{"summary":"List client tags","x-required-scope":"clients:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a client tag","x-required-scope":"clients:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"colour":{"type":"string"}}}}}}}},"/api/v1/intake-forms":{"get":{"summary":"List intake forms with their current version","x-required-scope":"clients:read","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create an intake form with typed questions","x-required-scope":"clients:write","parameters":[],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","questions"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"service_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"active":{"type":"boolean"},"questions":{"type":"array","items":{"type":"object","required":["id","type","label"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["text","textarea","number","date","yes_no","single_choice","multi_choice","consent"]},"label":{"type":"string"},"required":{"type":"boolean"},"options":{"type":"array","items":{"type":"string"}},"help":{"type":"string"},"min":{"type":"integer"},"max":{"type":"integer"}}}}}}}}}}},"/api/v1/intake-forms/{id}":{"patch":{"summary":"Update a form. Changed questions create a new version; earlier responses keep theirs","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"name":{"type":"string"},"description":{"type":"string"},"service_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"active":{"type":"boolean"},"questions":{"type":"array","items":{"type":"object","required":["id","type","label"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["text","textarea","number","date","yes_no","single_choice","multi_choice","consent"]},"label":{"type":"string"},"required":{"type":"boolean"},"options":{"type":"array","items":{"type":"string"}},"help":{"type":"string"},"min":{"type":"integer"},"max":{"type":"integer"}}}}}}}}}}},"/api/v1/public/{slug}/bookings/{reference}/intake":{"get":{"summary":"Client: intake forms for a booking (needs ?token= manage token)","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Client: submit an intake form with the booking manage token","x-required-scope":"public:book","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"reference","in":"path","required":true,"schema":{"type":"string"}}],"security":[{},{"apiKey":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token","form_id","answers"],"properties":{"token":{"type":"string"},"form_id":{"type":"string"},"answers":{"type":"object"}}}}}}}},"/api/v1/clients/{id}":{"get":{"summary":"Client profile with bookings, payments, messages, preferences, consents, notes, tags, uploads and intake","x-required-scope":"clients:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update name, contact details or identity status","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"identity_status":{"type":"string","enum":["verified","unverified"]}}}}}}}},"/api/v1/clients/{id}/consents":{"post":{"summary":"Record a contact preference with who, when, source and wording version","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["channel","purpose","granted"],"properties":{"channel":{"type":"string","enum":["email","sms","whatsapp"]},"purpose":{"type":"string","enum":["transactional","marketing"]},"granted":{"type":"boolean"},"source":{"type":"string"},"wording_version":{"type":"string"},"wording_text":{"type":"string"}}}}}}}},"/api/v1/clients/{id}/notes":{"post":{"summary":"Add a staff note (never shown to the client)","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"body":{"type":"string"}}}}}}}},"/api/v1/clients/{id}/notes/{noteId}":{"delete":{"summary":"Remove a staff note (kept in the audit trail)","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"noteId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/clients/{id}/tags":{"put":{"summary":"Replace a client's tags","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tag_ids"],"properties":{"tag_ids":{"type":"array","items":{"type":"string"}}}}}}}}},"/api/v1/clients/{id}/intake":{"post":{"summary":"Record intake answers for a client, optionally for one booking","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["form_id","answers"],"properties":{"form_id":{"type":"string"},"booking_id":{"type":"string"},"answers":{"type":"object"}}}}}}}},"/api/v1/clients/{id}/uploads":{"post":{"summary":"Upload a private file (JPEG, PNG, WebP or PDF, checked by content)","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content_type","data_base64"],"properties":{"filename":{"type":"string"},"content_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf"]},"data_base64":{"type":"string","description":"At most 160 KiB decoded. Content is checked against the declared type."},"note":{"type":"string"}}}}}}}},"/api/v1/clients/{id}/uploads/{fileId}":{"get":{"summary":"Download a private file as an attachment (authenticated and audited)","x-required-scope":"clients:read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"fileId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a private file (content erased, record kept)","x-required-scope":"clients:write","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"fileId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"apiKey":[]},{"session":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict or invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Scoped API key"},"session":{"type":"apiKey","in":"cookie","name":"sfb_session"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["VALIDATION_ERROR","UNAUTHENTICATED","FORBIDDEN","TENANT_MISMATCH","POLICY_BLOCKED","NOT_FOUND","SLOT_UNAVAILABLE","INVALID_STATE","HOLD_EXPIRED","IDEMPOTENCY_KEY_REUSED","CONFLICT","PREREQUISITE_REQUIRED","CONSENT_REQUIRED","INTAKE_REQUIRED","SIGNATURE_INVALID","RATE_LIMITED","NOT_IMPLEMENTED","EXPIRED","APPROVAL_REQUIRED","INTERNAL"]},"message":{"type":"string"},"details":{"type":"object"}},"required":["code","message"]}},"required":["error"]},"HoldRequest":{"type":"object","properties":{"service_id":{"type":"string","description":"One service. Mutually exclusive with items."},"items":{"type":"array","minItems":1,"maxItems":6,"description":"Multi-service visit, held all-or-none back to back. Replaces service_id.","items":{"type":"object","properties":{"service_id":{"type":"string"},"staff_id":{"type":"string"},"add_on_ids":{"type":"array","items":{"type":"string"}}},"required":["service_id"]}},"location_id":{"type":"string"},"add_on_ids":{"type":"array","items":{"type":"string"}},"staff_id":{"type":"string","description":"Omit or \"any\" for any eligible staff"},"start":{"type":"string","description":"ISO time with offset, or YYYY-MM-DDTHH:MM in the business zone"},"client":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"}},"required":["name","email"]},"consent":{"type":"boolean"},"intake_answer":{"type":"string"},"idempotency_key":{"type":"string"}},"required":["start","client","idempotency_key"],"oneOf":[{"required":["service_id"],"not":{"required":["items"]}},{"required":["items"],"not":{"required":["service_id"]}}]},"TokenBody":{"type":"object","properties":{"manage_token":{"type":"string"},"idempotency_key":{"type":"string"}},"required":[]},"AdjustRequest":{"type":"object","properties":{"new_start":{"type":"string"},"new_end":{"type":"string","description":"Resize: duration from half to three times the booked base, 5-minute steps"},"staff_id":{"type":"string"},"resource_id":{"type":"string"},"expected_version":{"type":"integer"}},"required":["new_start","expected_version"]},"ApprovalRequest":{"type":"object","properties":{"decision":{"type":"string","enum":["approve","decline"]},"note":{"type":"string"}},"required":["decision"]},"SeriesRequest":{"type":"object","properties":{"service_id":{"type":"string"},"staff_id":{"type":"string"},"location_id":{"type":"string"},"start":{"type":"string"},"frequency":{"type":"string","enum":["weekly","fortnightly","monthly"]},"count":{"type":"integer"},"until":{"type":"string","format":"date"},"client":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"}},"required":["name","email"]},"idempotency_key":{"type":"string"}},"required":["service_id","start","frequency","client","idempotency_key"]},"ClosureRequest":{"type":"object","properties":{"location_id":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"reason":{"type":"string"}},"required":["starts_at","ends_at"]},"RescheduleRequest":{"type":"object","properties":{"new_start":{"type":"string"},"staff_id":{"type":"string"},"manage_token":{"type":"string"},"idempotency_key":{"type":"string"}},"required":["new_start"]},"CancelRequest":{"type":"object","properties":{"reason":{"type":"string"},"manage_token":{"type":"string"},"expected_version":{"type":"integer","minimum":1}},"required":[]},"OutcomeRequest":{"type":"object","properties":{"outcome":{"type":"string","enum":["completed","no_show"]}},"required":["outcome"]},"RefundRequest":{"type":"object","properties":{"approval_note":{"type":"string"}},"required":["approval_note"]},"MessageRequest":{"type":"object","properties":{"name":{"type":"string"},"contact":{"type":"string"},"message":{"type":"string"},"reference":{"type":"string"},"idempotency_key":{"type":"string"}},"required":["name","contact","message"]},"HandoverRequest":{"type":"object","properties":{"name":{"type":"string"},"contact":{"type":"string"},"reason":{"type":"string"},"reference":{"type":"string"},"idempotency_key":{"type":"string"}},"required":["name","contact","reason"]},"Service":{"type":"object","properties":{"name":{"type":"string"},"category":{"type":"string"},"duration_minutes":{"type":"integer"},"price_minor":{"type":"integer"},"buffer_before_minutes":{"type":"integer"},"buffer_after_minutes":{"type":"integer"},"active":{"type":"boolean"},"staff_ids":{"type":"array","items":{"type":"string"}}},"required":[]},"WorkingHours":{"type":"object","properties":{"hours":{"type":"array","items":{"type":"object","properties":{"weekday":{"type":"integer"},"start_minute":{"type":"integer"},"end_minute":{"type":"integer"},"location_id":{"type":"string"}},"required":["weekday","start_minute","end_minute"]}}},"required":["hours"]},"TimeOff":{"type":"object","properties":{"starts_at":{"type":"string"},"ends_at":{"type":"string"},"reason":{"type":"string"}},"required":["starts_at","ends_at"]},"ApiKeyRequest":{"type":"object","properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}},"required":["name","scopes"]},"WebhookRequest":{"type":"object","properties":{"url":{"type":"string"},"event_types":{"type":"array","items":{"type":"string"}}},"required":["url"]},"TenantRequest":{"type":"object","properties":{"business_name":{"type":"string"},"slug":{"type":"string"},"owner_name":{"type":"string"},"owner_email":{"type":"string"},"password":{"type":"string"},"timezone":{"type":"string"}},"required":["business_name","slug","owner_name","owner_email","password"]}}},"x-webhooks":{"signature":"Header X-SFB-Signature: t=<unix>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the endpoint secret>. Reject if t is older than 300 seconds or the event id was already processed.","event_types":["booking.held","booking.approval_requested","booking.approved","booking.declined","booking.confirmed","booking.rescheduled","booking.cancelled","booking.expired","booking.completed","booking.no_show","payment.requested","payment.succeeded","payment.failed","payment.requires_staff_action","payment.refunded","message.captured","handover.requested"]}}