{"openapi":"3.1.0","info":{"title":"TimeTracking API","version":"1.1.0","description":"All data is yours: every resource is readable and writable over this API.\n\n- **Auth:** `Authorization: Bearer tt_live_…` (org API key; read or read+write scope) or a user access token.\n- **Envelope:** success `{ data }`, failure `{ error: { code, message } }`.\n- **Units:** durations are integer minutes, money integer cents, dates `YYYY-MM-DD` in the org timezone.\n- **Pagination:** lists accept `limit`, `cursor`, `updated_since` and return `{ items, next_cursor }`.\n- **Idempotency:** send `Idempotency-Key` on writes to retry safely.\n- **Rate limit:** 600 requests/min per key (429 + Retry-After).\n- **Versioning:** `/v1` is stable; breaking changes get a new version with 12 months overlap.","contact":{"name":"TimeTracking","url":"https://timetracking.tech/developers"}},"servers":[{"url":"https://app.timetracking.tech/api/v1"}],"security":[{"bearer":[]}],"tags":[{"name":"Hours","description":"Claims (raw inputs), days (resolved values), weeks, conflicts and approvals."},{"name":"Master data","description":"People, clients, placements — with external IDs for syncs."},{"name":"Exports","description":"Payroll export preview/commit, export runs and files, period locking."},{"name":"Approvals","description":"Client magic links and the public signing flow."},{"name":"Rosters","description":"Shifts and availability."},{"name":"Leave"},{"name":"Expenses"},{"name":"Employee","description":"Endpoints for the signed-in worker (mobile app, extension)."},{"name":"Inbox","description":"Inbound client timesheets (email/upload) and review."},{"name":"Integrations","description":"API keys and webhooks."},{"name":"Kiosk"},{"name":"Settings","description":"Hours deadline and late-reminder policy."},{"name":"Reports","description":"Aggregated hours for BI and payroll checks."},{"name":"Account","description":"Full account export (open data)."}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Org API key `tt_live_…` (Regels & koppelingen → API-sleutels) or a user access token."}},"parameters":{"limit":{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"description":"Page size (paginated lists)."},"cursor":{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor from `next_cursor` of the previous page."},"updated_since":{"name":"updated_since","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only rows changed at or after this instant (incremental sync)."},"from":{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"First date (inclusive)."},"to":{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"Last date (inclusive)."},"week":{"name":"week","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"Any date in the ISO week; defaults to the current week (org timezone)."},"id":{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"token":{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Magic-link token from the client approval URL."},"source":{"name":"source","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9_-]+$","examples":["bullhorn"]},"description":"Your system's name."},"ref":{"name":"ref","in":"path","required":true,"schema":{"type":"string","examples":["12345"]},"description":"The record's id in your system (URL-encoded)."},"idempotencyKey":{"name":"Idempotency-Key","in":"header","schema":{"type":"string","minLength":8,"maxLength":200},"description":"Optional on writes: the first successful response is replayed for 24h for the same key."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated","forbidden","not_found","invalid","conflict","locked","rate_limited","internal","invalid_transition","reason_required"]},"message":{"type":"string"},"issues":{"type":"array","description":"Validation details (zod issues) for code=invalid","items":{"type":"object"}}}}},"examples":[{"error":{"code":"conflict","message":"These hours are already submitted; ask the office to change them"}}]},"Page":{"type":"object","required":["items","next_cursor"],"properties":{"items":{"type":"array","items":{}},"next_cursor":{"type":["string","null"]}}},"Person":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"employee_number":{"type":["string","null"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"locale":{"type":["string","null"]},"active":{"type":"boolean"},"user_id":{"type":["string","null"],"description":"Set when the person has app access"},"external_source":{"type":["string","null"]},"external_ref":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Client":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"external_source":{"type":["string","null"]},"external_ref":{"type":["string","null"]},"approval_mode":{"type":"string","enum":["office","client","both"]},"mail_domains":{"type":"array","items":{"type":"string"}},"updated_at":{"type":"string","format":"date-time"}}},"Placement":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"person_id":{"type":"string","format":"uuid"},"client_id":{"type":"string","format":"uuid"},"site_id":{"type":["string","null"]},"ref":{"type":["string","null"]},"external_source":{"type":["string","null"]},"external_ref":{"type":["string","null"]},"role_title":{"type":["string","null"]},"starts_on":{"type":"string","format":"date","examples":["2026-10-19"]},"ends_on":{"type":["string","null"],"format":"date"},"active":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"}}},"Day":{"type":"object","description":"The resolved value for one placement on one date (one row per placement/date).","properties":{"id":{"type":"string","format":"uuid"},"placement_id":{"type":"string","format":"uuid"},"person_id":{"type":"string","format":"uuid"},"work_date":{"type":"string","format":"date","examples":["2026-10-19"]},"minutes":{"type":["integer","null"],"description":"null while in conflict"},"start_min":{"type":["integer","null"]},"end_min":{"type":["integer","null"]},"break_min":{"type":"integer","minimum":0,"description":"Duration in minutes"},"note":{"type":["string","null"]},"office_note":{"type":["string","null"]},"client_note":{"type":["string","null"]},"status":{"type":"string","enum":["draft","submitted","approved","exported","locked"]},"resolution":{"type":["string","null"]},"has_conflict":{"type":"boolean"},"approved_by_office_at":{"type":["string","null"]},"approved_by_client":{"type":["string","null"]},"approved_by_client_at":{"type":["string","null"]},"export_run_id":{"type":["string","null"]},"version":{"type":"integer"},"updated_at":{"type":"string","format":"date-time"}}},"Claim":{"type":"object","description":"One raw input (never edited). Several claims for the same placement/day are matched into a Day.","properties":{"id":{"type":"string","format":"uuid"},"placement_id":{"type":"string","format":"uuid"},"work_date":{"type":"string","format":"date","examples":["2026-10-19"]},"source":{"type":"string","enum":["app","kiosk","mail","import","predicted","manual_office","client_portal"]},"minutes":{"type":"integer","minimum":0,"description":"Duration in minutes"},"start_min":{"type":["integer","null"]},"end_min":{"type":["integer","null"]},"break_min":{"type":"integer","minimum":0,"description":"Duration in minutes"},"note":{"type":["string","null"]},"confidence":{"type":["number","null"]},"source_ref":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}}},"OrgWeek":{"type":"object","properties":{"weekStart":{"type":"string","format":"date","examples":["2026-10-19"]},"weekEnd":{"type":"string","format":"date","examples":["2026-10-19"]},"kpis":{"type":"object","properties":{"placements":{"type":"integer"},"submitted":{"type":"integer"},"approved":{"type":"integer"},"conflicts":{"type":"integer"},"missing":{"type":"integer"},"totalMinutes":{"type":"integer","minimum":0,"description":"Duration in minutes"},"late":{"type":"integer","description":"People whose hours are late"}}},"deadline":{"type":"object","description":"Hours deadline of the payroll period containing this week (org timezone)","properties":{"date":{"type":"string","format":"date","examples":["2026-10-19"]},"minute":{"type":"integer","description":"Minute of day, local time"},"passed":{"type":"boolean"}}},"rows":{"type":"array","items":{"type":"object","properties":{"placementId":{"type":"string","format":"uuid"},"personId":{"type":"string","format":"uuid"},"personName":{"type":"string"},"employeeNumber":{"type":["string","null"]},"clientName":{"type":"string"},"clientId":{"type":"string","format":"uuid"},"approvalMode":{"type":"string"},"totalMinutes":{"type":"integer","minimum":0,"description":"Duration in minutes"},"status":{"type":"string"},"late":{"type":"boolean","description":"Deadline passed (org timezone), active placement and no submitted day this week for this person"},"days":{"type":"array","minItems":7,"maxItems":7,"items":{"oneOf":[{"type":"null"},{"type":"object","properties":{"dayId":{"type":"string","format":"uuid"},"minutes":{"type":["integer","null"]},"status":{"type":"string","enum":["draft","submitted","approved","exported","locked"]},"conflict":{"type":"boolean"},"clientApproved":{"type":"boolean"},"officeApproved":{"type":"boolean"}}}]}}}}},"conflicts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"dayId":{"type":"string","format":"uuid"},"personName":{"type":"string"},"clientName":{"type":"string"},"workDate":{"type":"string","format":"date","examples":["2026-10-19"]},"candidates":{"type":"array","items":{"type":"object","properties":{"claimId":{"type":"string","format":"uuid"},"source":{"type":"string"},"minutes":{"type":"integer","minimum":0,"description":"Duration in minutes"}}}}}}}}},"ExportPreview":{"type":"object","properties":{"ok":{"type":"boolean"},"rows":{"type":"array","items":{"type":"array","items":{"type":"string"}}},"content":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"row":{"type":"integer"},"column":{"type":"string"},"message":{"type":"string"}}}},"dayCount":{"type":"integer"},"pendingCount":{"type":"integer"},"warnings":{"type":"array","items":{"type":"object"}},"packName":{"type":"string"}}},"ExportRun":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"mapping_id":{"type":["string","null"]},"starts_on":{"type":"string","format":"date","examples":["2026-10-19"]},"ends_on":{"type":"string","format":"date","examples":["2026-10-19"]},"mode":{"type":"string"},"row_count":{"type":"integer"},"content_hash":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}},"BatchResult":{"type":"object","properties":{"ok":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"ok":{"type":"boolean"},"data":{},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"ReminderSettings":{"type":"object","description":"Late = active placement, deadline passed in the org timezone, nothing submitted. Reminders: in-app at the deadline, WhatsApp after `whatsappDelayMinutes` if still late (quiet hours 21:00–07:00, max 3 per person per day, never email).","properties":{"timezone":{"type":"string"},"deadline":{"type":"object","properties":{"weekday":{"type":"integer","description":"ISO weekday after the period, 1 = Monday"},"minute":{"type":"integer","description":"Minute of day, local time"}}},"policy":{"type":"object","properties":{"enabled":{"type":"boolean"},"whatsappEnabled":{"type":"boolean"},"whatsappDelayMinutes":{"type":"integer"}}},"whatsappConfigured":{"type":"boolean"}}},"Notification":{"type":"object","description":"In-app notification for the signed-in employee (e.g. kind `hours.late`). Title and body are in the employee's language.","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","examples":["hours.late"]},"title":{"type":"string"},"body":{"type":"string"},"data":{"type":"object"},"readAt":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}}},"WebhookEvent":{"type":"object","description":"Delivered as POST with header tt-signature: t=<unix>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>. Events: timesheet.submitted, timesheet.approved, timesheet.client_approved, conflict.created, export.completed, period.locked, leave.requested, leave.decided, expense.submitted, shift.published, shift.claimed, shift.swap_requested, shift.request_decided, person.created, person.updated, placement.created, placement.updated, claim.created, expense.decided, leave.cancelled, webhook.test, client.created, client.updated, conflict.resolved.","properties":{"id":{"type":"string","examples":["evt_2f1c…"]},"type":{"type":"string","enum":["timesheet.submitted","timesheet.approved","timesheet.client_approved","conflict.created","export.completed","period.locked","leave.requested","leave.decided","expense.submitted","shift.published","shift.claimed","shift.swap_requested","shift.request_decided","person.created","person.updated","placement.created","placement.updated","claim.created","expense.decided","leave.cancelled","webhook.test","client.created","client.updated","conflict.resolved"]},"created":{"type":"string","format":"date-time"},"data":{"type":"object"}}}}},"paths":{"/org/reminders":{"get":{"operationId":"getReminderSettings","summary":"Hours deadline and late-reminder policy, and whether WhatsApp is configured","tags":["Settings"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ReminderSettings"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"updateReminderSettings","summary":"Set the hours deadline and reminder policy (admin)","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deadlineWeekday":{"type":"integer","minimum":1,"maximum":7},"deadlineTime":{"type":"string"},"enabled":{"type":"boolean"},"whatsappEnabled":{"type":"boolean"},"whatsappDelayMinutes":{"type":"integer","minimum":0,"maximum":1440}},"required":["deadlineWeekday","deadlineTime","enabled","whatsappEnabled","whatsappDelayMinutes"]},"example":{"deadlineWeekday":1,"deadlineTime":"09:00","enabled":true,"whatsappEnabled":true,"whatsappDelayMinutes":90}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ReminderSettings"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/org/week":{"get":{"operationId":"getOrgWeek","summary":"Hours of all placements for one week, with KPIs and open conflicts","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/week"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/OrgWeek"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/org/today":{"get":{"operationId":"getOrgToday","summary":"Today board: rostered people vs kiosk clock-ins and recorded hours (org timezone)","tags":["Hours"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/days":{"get":{"operationId":"listDays","summary":"Resolved hours over any date range","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"name":"status","in":"query","schema":{"type":"string"},"description":"Comma list of statuses"},{"name":"person_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter"},{"name":"placement_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter"},{"name":"client_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter"},{"$ref":"#/components/parameters/updated_since"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/cursor"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Page"},{"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Day"}}}}]}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/claims":{"get":{"operationId":"listClaims","summary":"Raw claims over a date range","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"name":"placement_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter"},{"name":"source","in":"query","schema":{"type":"string","enum":["app","kiosk","mail","import","predicted","manual_office","client_portal"]},"description":"Filter"},{"$ref":"#/components/parameters/updated_since"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/cursor"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Page"},{"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Claim"}}}}]}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createClaim","summary":"Record hours (start/end/break or minutes)","description":"Employees may use source app/kiosk for their own placements; office keys may also use mail, import, manual_office. The day is re-resolved immediately (conflict detection).","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"placementId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"workDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"startMin":{"type":"integer","minimum":0,"maximum":1439},"endMin":{"type":"integer","minimum":0,"maximum":1439},"breakMin":{"default":0,"type":"integer","minimum":0,"maximum":720},"minutes":{"type":"integer","minimum":0,"maximum":1440},"note":{"type":"string","maxLength":1000},"source":{"default":"app","type":"string","enum":["app","kiosk","mail","import","manual_office","client_portal"]},"confidence":{"type":"number","minimum":0,"maximum":1},"sourceRef":{"type":"string","maxLength":200},"deviceTime":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"}},"required":["placementId","workDate"]},"example":{"placementId":"00000000-0000-4000-8000-0000000000e1","workDate":"2026-10-20","startMin":420,"endMin":930,"breakMin":30,"source":"manual_office"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Day"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/claims/batch":{"post":{"operationId":"batchClaims","summary":"Record up to 500 claims; per-row results","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"placementId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"workDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"startMin":{"type":"integer","minimum":0,"maximum":1439},"endMin":{"type":"integer","minimum":0,"maximum":1439},"breakMin":{"default":0,"type":"integer","minimum":0,"maximum":720},"minutes":{"type":"integer","minimum":0,"maximum":1440},"note":{"type":"string","maxLength":1000},"source":{"default":"app","type":"string","enum":["app","kiosk","mail","import","manual_office","client_portal"]},"confidence":{"type":"number","minimum":0,"maximum":1},"sourceRef":{"type":"string","maxLength":200},"deviceTime":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"}},"required":["placementId","workDate"]}}},"required":["items"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BatchResult"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/weeks/submit":{"post":{"operationId":"submitWeek","summary":"Submit a person's draft days of a week","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"weekStart":{"type":"string"},"personId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["weekStart"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"submitted":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/days/approve":{"post":{"operationId":"approveDays","summary":"Office approval of submitted days","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"dayIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["dayIds"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"approved":{"type":"integer"},"waitingForClient":{"type":"integer"},"skipped":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conflicts":{"get":{"operationId":"listConflicts","summary":"Open conflicts for a week","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/week"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"type":"object"}}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conflicts/{id}/resolve":{"post":{"operationId":"resolveConflict","summary":"Pick the winning claim of a conflict","tags":["Hours"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"claimId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string"}},"required":["claimId"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"resolved":{"type":"boolean"}}}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/approval-links":{"post":{"operationId":"createApprovalLink","summary":"Create a client approval magic link (emailed to the client contact)","tags":["Approvals"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"clientId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"startsOn":{"type":"string"},"endsOn":{"type":"string"}},"required":["clientId","startsOn","endsOn"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"url":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"emailed":{"type":["string","null"]}}}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/client/approve/{token}":{"get":{"operationId":"getClientApproval","summary":"What the client sees behind a link (public; the token is the credential)","tags":["Approvals"],"parameters":[{"$ref":"#/components/parameters/token"}],"security":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"submitClientApproval","summary":"Submit client decisions with signature (needs x-approval-proof when a code is required)","tags":["Approvals"],"parameters":[{"$ref":"#/components/parameters/token"},{"name":"x-approval-proof","in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"signerName":{"type":"string","minLength":2,"maxLength":120},"signerRole":{"type":"string","maxLength":120},"signature":{"type":"string","minLength":2,"maxLength":120},"decisions":{"minItems":1,"type":"array","items":{"type":"object","properties":{"dayId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"decision":{"type":"string","enum":["approved","corrected","rejected"]},"correctedMinutes":{"type":"integer","minimum":0,"maximum":1440},"reason":{"type":"string","maxLength":500}},"required":["dayId","decision"]}}},"required":["signerName","signature","decisions"]}}}},"security":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/client/approve/{token}/code":{"post":{"operationId":"sendClientCode","summary":"Email a one-time code to the client contact","tags":["Approvals"],"parameters":[{"$ref":"#/components/parameters/token"}],"security":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/client/approve/{token}/verify":{"post":{"operationId":"verifyClientCode","summary":"Exchange the code for a 2-hour proof","tags":["Approvals"],"parameters":[{"$ref":"#/components/parameters/token"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"security":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"proof":{"type":"string"}}}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/exports":{"post":{"operationId":"runExport","summary":"Preview or commit a payroll export (mapping id e.g. preset-sdworx)","description":"commit is irreversible: included days become exported.","tags":["Exports"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"mappingId":{"type":"string"},"startsOn":{"type":"string"},"endsOn":{"type":"string"},"mode":{"type":"string","enum":["preview","commit"]}},"required":["mappingId","startsOn","endsOn","mode"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ExportPreview"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/exports/runs":{"get":{"operationId":"listExportRuns","summary":"Committed export runs","tags":["Exports"],"parameters":[{"$ref":"#/components/parameters/updated_since"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/cursor"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Page"},{"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ExportRun"}}}}]}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/exports/runs/{id}":{"get":{"operationId":"getExportRun","summary":"One export run with pay lines and warnings","tags":["Exports"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ExportRun"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/exports/runs/{id}/file":{"get":{"operationId":"getExportFile","summary":"The committed payroll file (CSV, TXT, fixed-width TXT or XML per mapping; ?as=json|xlsx converts)","tags":["Exports"],"parameters":[{"$ref":"#/components/parameters/id"},{"name":"as","in":"query","schema":{"type":"string","enum":["json","xlsx"]},"description":"json or xlsx"}],"responses":{"200":{"description":"File","content":{"text/csv":{"schema":{"type":"string"}},"text/plain":{"schema":{"type":"string"}},"application/xml":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/people/{id}/timesheet":{"get":{"operationId":"getTimesheetPdf","summary":"PDF timesheet (urenstaat) of one worker for a period (max 62 days)","tags":["Exports"],"parameters":[{"$ref":"#/components/parameters/id"},{"name":"from","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"From"},{"name":"to","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"To"}],"responses":{"200":{"description":"PDF","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reports/hours":{"get":{"operationId":"getHoursReport","summary":"Hours per person, client or placement over a date range (max 366 days)","description":"Counts resolved days with minutes > 0. minutesByStatus splits the total over draft/submitted/approved/exported/locked; conflictDays counts days still in conflict.","tags":["Reports"],"parameters":[{"name":"group_by","in":"query","schema":{"type":"string","enum":["person","client","placement"],"default":"person"},"description":"Grouping (default person)"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"name":"client_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter"},{"name":"person_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter"},{"name":"placement_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter"},{"name":"status","in":"query","schema":{"type":"string","enum":["draft","submitted","approved","exported","locked","conflict"]},"description":"Only days with this status, or conflict"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"from":{"type":"string","format":"date","examples":["2026-10-19"]},"to":{"type":"string","format":"date","examples":["2026-10-19"]},"group_by":{"type":"string","enum":["person","client","placement"]},"total_minutes":{"type":"integer","minimum":0,"description":"Duration in minutes"},"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"sub":{"type":["string","null"],"description":"person: employee number; placement: client name"},"personId":{"type":["string","null"]},"clientId":{"type":["string","null"]},"placementId":{"type":["string","null"]},"days":{"type":"integer"},"minutes":{"type":"integer","minimum":0,"description":"Duration in minutes"},"conflictDays":{"type":"integer"},"minutesByStatus":{"type":"object","properties":{"draft":{"type":"integer","minimum":0,"description":"Duration in minutes"},"submitted":{"type":"integer","minimum":0,"description":"Duration in minutes"},"approved":{"type":"integer","minimum":0,"description":"Duration in minutes"},"exported":{"type":"integer","minimum":0,"description":"Duration in minutes"},"locked":{"type":"integer","minimum":0,"description":"Duration in minutes"}}}}}}}}}},"example":{"data":{"from":"2026-10-19","to":"2026-10-25","group_by":"person","total_minutes":960,"rows":[{"id":"00000000-0000-4000-8000-0000000000d1","name":"Jan de Vries","sub":"1001","personId":"00000000-0000-4000-8000-0000000000d1","clientId":null,"placementId":null,"days":2,"minutes":960,"conflictDays":0,"minutesByStatus":{"draft":0,"submitted":480,"approved":480,"exported":0,"locked":0}}]}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/export/account":{"get":{"operationId":"exportAccount","summary":"Full account export: ZIP with every org table as CSV and JSON, schema.json and README.md (admin)","description":"Admin API key or owner/admin user token. Secrets (API key hashes, kiosk PIN and token hashes, webhook secrets, e-mail bodies) are excluded, also inside JSON columns. Every download is written to the audit log.","tags":["Account"],"responses":{"200":{"description":"ZIP archive","content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/periods/lock":{"post":{"operationId":"lockPeriod","summary":"Lock a period after export (admin)","tags":["Exports"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"startsOn":{"type":"string"},"endsOn":{"type":"string"},"kind":{"type":"string","enum":["week","four_weeks","semi_month","month"]}},"required":["startsOn","endsOn","kind"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/import/time-entries":{"post":{"operationId":"importTimeEntries","summary":"Import a Toggl Track, Clockify, TimeChimp or Harvest time-entry CSV (dry run by default)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"provider":{"type":"string","enum":["toggl","clockify","timechimp","harvest"]},"dryRun":{"default":true,"type":"boolean"},"personMap":{"type":"object","propertyNames":{"type":"string","maxLength":200},"additionalProperties":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"csv":{"type":"string"}},"required":["csv"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/import":{"post":{"operationId":"importMasterData","summary":"Import master data CSV (people, clients, placements)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"csv":{"type":"string"},"dryRun":{"type":"boolean"}},"required":["csv","dryRun"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/people":{"get":{"operationId":"listPersons","summary":"List people (full list, or paginated with limit/cursor/updated_since → { items, next_cursor })","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/updated_since"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/cursor"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/Person"}},{"allOf":[{"$ref":"#/components/schemas/Page"},{"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Person"}}}}]}]}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"upsertPerson","summary":"Create a person (or update when id is given)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"employeeNumber":{"type":"string","maxLength":40},"firstName":{"type":"string","minLength":1,"maxLength":80},"lastName":{"type":"string","minLength":1,"maxLength":80},"email":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":30},"locale":{"type":"string","maxLength":10},"active":{"default":true,"type":"boolean"}},"required":["firstName","lastName"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Person"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/people/{id}":{"get":{"operationId":"getPerson","summary":"Get one person","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Person"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updatePerson","summary":"Update a person (send the full object)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"employeeNumber":{"type":"string","maxLength":40},"firstName":{"type":"string","minLength":1,"maxLength":80},"lastName":{"type":"string","minLength":1,"maxLength":80},"email":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":30},"locale":{"type":"string","maxLength":10},"active":{"default":true,"type":"boolean"}},"required":["firstName","lastName"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Person"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deactivatePerson","summary":"Deactivate a person","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/people/by-external/{source}/{ref}":{"put":{"operationId":"upsertPersonByExternal","summary":"Idempotent upsert of a person by your system's id","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/source"},{"$ref":"#/components/parameters/ref"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"employeeNumber":{"type":"string","maxLength":40},"firstName":{"type":"string","minLength":1,"maxLength":80},"lastName":{"type":"string","minLength":1,"maxLength":80},"email":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":30},"locale":{"type":"string","maxLength":10},"active":{"default":true,"type":"boolean"}},"required":["firstName","lastName"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Person"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/people/batch":{"post":{"operationId":"batchPersons","summary":"Upsert up to 500 people; rows with externalSource+externalRef upsert by external id","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"items":{"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}}},"required":["items"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BatchResult"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clients":{"get":{"operationId":"listClients","summary":"List clients (full list, or paginated with limit/cursor/updated_since → { items, next_cursor })","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/updated_since"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/cursor"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/Client"}},{"allOf":[{"$ref":"#/components/schemas/Page"},{"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Client"}}}}]}]}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"upsertClient","summary":"Create a client (or update when id is given)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","minLength":1,"maxLength":160},"externalRef":{"type":"string","maxLength":80},"approvalMode":{"default":"office","type":"string","enum":["office","client","both"]},"mailDomains":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Client"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clients/{id}":{"get":{"operationId":"getClient","summary":"Get one client","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Client"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateClient","summary":"Update a client (send the full object)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","minLength":1,"maxLength":160},"externalRef":{"type":"string","maxLength":80},"approvalMode":{"default":"office","type":"string","enum":["office","client","both"]},"mailDomains":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Client"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deactivateClient","summary":"Delete a client without placements","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clients/by-external/{source}/{ref}":{"put":{"operationId":"upsertClientByExternal","summary":"Idempotent upsert of a client by your system's id","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/source"},{"$ref":"#/components/parameters/ref"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","minLength":1,"maxLength":160},"externalRef":{"type":"string","maxLength":80},"approvalMode":{"default":"office","type":"string","enum":["office","client","both"]},"mailDomains":{"type":"string"}},"required":["name"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Client"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clients/batch":{"post":{"operationId":"batchClients","summary":"Upsert up to 500 clients; rows with externalSource+externalRef upsert by external id","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"items":{"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}}},"required":["items"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BatchResult"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/placements":{"get":{"operationId":"listPlacements","summary":"List placements (full list, or paginated with limit/cursor/updated_since → { items, next_cursor })","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/updated_since"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/cursor"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/Placement"}},{"allOf":[{"$ref":"#/components/schemas/Page"},{"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Placement"}}}}]}]}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"upsertPlacement","summary":"Create a placement (or update when id is given)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"personId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"clientId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"ref":{"type":"string","maxLength":80},"roleTitle":{"type":"string","maxLength":120},"startsOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"endsOn":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"string","const":""}]},"active":{"default":true,"type":"boolean"}},"required":["personId","clientId","startsOn"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Placement"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/placements/{id}":{"get":{"operationId":"getPlacement","summary":"Get one placement","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Placement"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updatePlacement","summary":"Update a placement (send the full object)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"personId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"clientId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"ref":{"type":"string","maxLength":80},"roleTitle":{"type":"string","maxLength":120},"startsOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"endsOn":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"string","const":""}]},"active":{"default":true,"type":"boolean"}},"required":["personId","clientId","startsOn"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Placement"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deactivatePlacement","summary":"Deactivate a placement","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/placements/by-external/{source}/{ref}":{"put":{"operationId":"upsertPlacementByExternal","summary":"Idempotent upsert of a placement by your system's id","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/source"},{"$ref":"#/components/parameters/ref"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"personId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"clientId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"ref":{"type":"string","maxLength":80},"roleTitle":{"type":"string","maxLength":120},"startsOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"endsOn":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"string","const":""}]},"active":{"default":true,"type":"boolean"}},"required":["personId","clientId","startsOn"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Placement"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/placements/batch":{"post":{"operationId":"batchPlacements","summary":"Upsert up to 500 placements; rows with externalSource+externalRef upsert by external id","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"items":{"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}}},"required":["items"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BatchResult"}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/people/{id}/access":{"post":{"operationId":"grantAppAccess","summary":"Give app access (email-code login)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"revokeAppAccess","summary":"Revoke app access","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/people/{id}/anonymize":{"post":{"operationId":"anonymizePerson","summary":"GDPR erasure of a person (admin; owner protected)","tags":["Master data"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/people/{id}/pin":{"post":{"operationId":"setKioskPin","summary":"Set a person's kiosk PIN (6 digits)","tags":["Kiosk"],"parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"pin":{"type":"string"}},"required":["pin"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/kiosk-devices":{"get":{"operationId":"listKioskDevices","summary":"List kiosk tablets","tags":["Kiosk"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createKioskDevice","summary":"Create a kiosk tablet; returns its token once","tags":["Kiosk"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string"},"clientId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["name"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shifts":{"get":{"operationId":"getRoster","summary":"Roster week with planning issues","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/week"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"upsertShift","summary":"Create or update a shift (draft)","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"clientId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"placementId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"siteId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"workDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"startMin":{"type":"integer","minimum":0,"maximum":1439},"endMin":{"type":"integer","minimum":0,"maximum":1439},"breakMin":{"default":0,"type":"integer","minimum":0,"maximum":720},"roleTitle":{"type":"string","maxLength":120},"note":{"type":"string","maxLength":500},"requiresApproval":{"type":"boolean"}},"required":["clientId","workDate","startMin","endMin"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shifts/{id}":{"delete":{"operationId":"deleteShift","summary":"Delete a draft or cancel a published shift","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shifts/publish":{"post":{"operationId":"publishShifts","summary":"Publish all draft shifts of a week","tags":["Rosters"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"weekStart":{"type":"string"}},"required":["weekStart"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shifts/{id}/claim":{"post":{"operationId":"claimOpenShift","summary":"Signed-in employee: claim an open shift (first claim wins, or a request when the planner approves)","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"note":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shifts/{id}/swap":{"post":{"operationId":"requestShiftSwap","summary":"Signed-in employee: ask a colleague to take over (or exchange) your shift","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"colleaguePersonId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"colleagueShiftId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"note":{"type":"string","maxLength":500}},"required":["colleaguePersonId"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shifts/{id}/swap-candidates":{"get":{"operationId":"getSwapCandidates","summary":"Signed-in employee: colleagues who could take over this shift, with their shifts to exchange","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shift-requests":{"get":{"operationId":"listShiftRequests","summary":"Open-shift claims and swap requests (open; ?status=all adds the last 30 days) with planning issues","tags":["Rosters"],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["open","all"]},"description":"open or all"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shift-requests/{id}/respond":{"post":{"operationId":"respondShiftSwap","summary":"Colleague accepts or declines a swap request","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"accept":{"type":"boolean"}},"required":["accept"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shift-requests/{id}/decide":{"post":{"operationId":"decideShiftRequest","summary":"Planner approves or rejects a claim or an accepted swap","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"decision":{"type":"string","enum":["approved","rejected"]},"note":{"type":"string","maxLength":500}},"required":["decision"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shift-requests/{id}/cancel":{"post":{"operationId":"cancelShiftRequest","summary":"Requester (or office) withdraws an open request","tags":["Rosters"],"parameters":[{"$ref":"#/components/parameters/id"},{"$ref":"#/components/parameters/idempotencyKey"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shifts/copy-week":{"post":{"operationId":"copyShiftWeek","summary":"Copy previous week's shifts as drafts","tags":["Rosters"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"weekStart":{"type":"string"}},"required":["weekStart"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/availability":{"get":{"operationId":"getAvailability","summary":"Availability of the signed-in person (or personId for office)","tags":["Rosters"],"parameters":[{"name":"personId","in":"query","schema":{"type":"string","format":"uuid"},"description":"Person"},{"name":"from","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"From"},{"name":"to","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"To"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"setAvailability","summary":"Set availability for a day","tags":["Rosters"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"personId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"workDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"available":{"type":"boolean"},"note":{"type":"string","maxLength":300}},"required":["workDate","available"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leave":{"get":{"operationId":"listLeave","summary":"List leave requests","tags":["Leave"],"parameters":[{"name":"personId","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter"},{"name":"from","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"From"},{"name":"to","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"To"},{"name":"status","in":"query","schema":{"type":"string"},"description":"requested|approved|rejected|cancelled"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"requestLeave","summary":"Request or register leave","tags":["Leave"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"personId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"leaveTypeId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"startsOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"endsOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"minutesPerDay":{"default":480,"type":"integer","minimum":0,"maximum":1440},"note":{"type":"string","maxLength":500}},"required":["leaveTypeId","startsOn","endsOn"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leave/types":{"get":{"operationId":"listLeaveTypes","summary":"Leave types","tags":["Leave"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leave/balance":{"get":{"operationId":"getLeaveBalance","summary":"Leave balance per type (minutes)","tags":["Leave"],"parameters":[{"name":"personId","in":"query","schema":{"type":"string","format":"uuid"},"description":"Person"},{"name":"year","in":"query","schema":{"type":"integer"},"description":"Year"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leave/{id}/decide":{"post":{"operationId":"decideLeave","summary":"Approve or reject leave","tags":["Leave"],"parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"decision":{"type":"string","enum":["approved","rejected"]},"note":{"type":"string"}},"required":["decision"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/leave/{id}/cancel":{"post":{"operationId":"cancelLeave","summary":"Cancel leave","tags":["Leave"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/expenses":{"get":{"operationId":"listExpenses","summary":"List expenses","tags":["Expenses"],"parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"From"},{"name":"to","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"To"},{"name":"personId","in":"query","schema":{"type":"string","format":"uuid"},"description":"Person"},{"name":"status","in":"query","schema":{"type":"string"},"description":"Status"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createExpense","summary":"Record mileage, travel time or an expense","tags":["Expenses"],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"personId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"placementId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"workDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"kind":{"type":"string","enum":["km","travel_time","public_transport","parking","meal","other"]},"km":{"type":"number","minimum":0,"maximum":5000},"minutes":{"type":"integer","minimum":0,"maximum":1440},"amountCents":{"type":"integer","minimum":0,"maximum":10000000},"description":{"type":"string","maxLength":500},"receiptPath":{"type":"string","maxLength":300}},"required":["workDate","kind"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/expenses/{id}":{"delete":{"operationId":"deleteExpense","summary":"Delete or withdraw an expense","tags":["Expenses"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/expenses/decide":{"post":{"operationId":"decideExpenses","summary":"Approve or reject expenses","tags":["Expenses"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"decision":{"type":"string","enum":["approved","rejected"]}},"required":["ids","decision"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/expenses/receipt-upload":{"post":{"operationId":"createReceiptUpload","summary":"Signed URL to upload a receipt (employees)","tags":["Expenses"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"fileName":{"type":"string"}},"required":["fileName"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/week":{"get":{"operationId":"getMyWeek","summary":"Signed-in employee: placements and days of a week","tags":["Employee"],"parameters":[{"$ref":"#/components/parameters/week"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/open-shifts":{"get":{"operationId":"getMyOpenShifts","summary":"Signed-in employee: open shifts you may claim","tags":["Employee"],"parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"From"},{"name":"to","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"To"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/shift-requests":{"get":{"operationId":"getMyShiftRequests","summary":"Signed-in employee: your claim/swap requests and swaps addressed to you","tags":["Employee"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/shifts":{"get":{"operationId":"getMyShifts","summary":"Signed-in employee: published shifts","tags":["Employee"],"parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"From"},{"name":"to","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"To"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/predictions":{"get":{"operationId":"getMyPredictions","summary":"Signed-in employee: suggested hours for days without hours","tags":["Employee"],"parameters":[{"name":"today","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-19"]},"description":"Reference date"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/rewards":{"get":{"operationId":"getMyRewards","summary":"Signed-in employee: points and on-time streak","tags":["Employee"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/notifications":{"get":{"operationId":"listMyNotifications","summary":"Signed-in employee: notifications (newest first) and unread count","tags":["Employee"],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer"},"description":"Max items (1–100, default 30)"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"unread":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/notifications/read":{"post":{"operationId":"markMyNotificationsRead","summary":"Signed-in employee: mark notifications as read (all unread when `ids` is omitted)","tags":["Employee"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"ids":{"maxItems":100,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}}},"example":{"ids":["00000000-0000-4000-8000-000000000000"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"updated":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/device-token":{"post":{"operationId":"registerDeviceToken","summary":"Register a push token","tags":["Employee"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"token":{"type":"string"},"platform":{"type":"string","enum":["ios","android","web"]}},"required":["token","platform"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/inbox":{"get":{"operationId":"getInbox","summary":"Inbound client timesheets: documents and rows to review","tags":["Inbox"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/inbox/items/{id}/apply":{"post":{"operationId":"applyInboxItem","summary":"Apply a reviewed row as client hours","tags":["Inbox"],"parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"placementId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"workDate":{"type":"string"},"minutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["placementId","workDate","minutes"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/inbox/items/{id}/ignore":{"post":{"operationId":"ignoreInboxItem","summary":"Dismiss a row","tags":["Inbox"],"parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"note":{"type":"string"}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-keys":{"get":{"operationId":"listApiKeys","summary":"List API keys (admin users)","tags":["Integrations"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createApiKey","summary":"Create an API key (admin users; keys cannot create keys)","tags":["Integrations"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write"]}}},"required":["name","scopes"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-keys/{id}":{"delete":{"operationId":"revokeApiKey","summary":"Revoke an API key","tags":["Integrations"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhooks","tags":["Integrations"],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createWebhook","summary":"Create a webhook; returns the signing secret once","tags":["Integrations"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"minItems":1,"type":"array","items":{"type":"string","enum":["timesheet.submitted","timesheet.approved","timesheet.client_approved","conflict.created","export.completed","period.locked","leave.requested","leave.decided","expense.submitted","shift.published","shift.claimed","shift.swap_requested","shift.request_decided","person.created","person.updated","placement.created","placement.updated","claim.created","expense.decided","leave.cancelled","webhook.test","client.created","client.updated","conflict.resolved"]}}},"required":["url","events"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}":{"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","tags":["Integrations"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"Recent deliveries with status codes","tags":["Integrations"],"parameters":[{"$ref":"#/components/parameters/id"},{"name":"limit","in":"query","schema":{"type":"integer"},"description":"Max 200"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/test":{"post":{"operationId":"testWebhook","summary":"Queue a webhook.test event","tags":["Integrations"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/{id}/rotate-secret":{"post":{"operationId":"rotateWebhookSecret","summary":"Issue a new signing secret (returned once)","tags":["Integrations"],"parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook-deliveries/{id}/replay":{"post":{"operationId":"replayWebhookDelivery","summary":"Re-queue a delivery","tags":["Integrations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{}}}}}},"401":{"description":"Missing or invalid token/API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this role or key scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in your organisation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"Period is locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit (600/min per key); see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"timesheet.submitted":{"post":{"summary":"timesheet.submitted","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"timesheet.approved":{"post":{"summary":"timesheet.approved","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"timesheet.client_approved":{"post":{"summary":"timesheet.client_approved","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"conflict.created":{"post":{"summary":"conflict.created","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"export.completed":{"post":{"summary":"export.completed","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"period.locked":{"post":{"summary":"period.locked","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"leave.requested":{"post":{"summary":"leave.requested","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"leave.decided":{"post":{"summary":"leave.decided","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"expense.submitted":{"post":{"summary":"expense.submitted","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"shift.published":{"post":{"summary":"shift.published","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"shift.claimed":{"post":{"summary":"shift.claimed","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"shift.swap_requested":{"post":{"summary":"shift.swap_requested","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"shift.request_decided":{"post":{"summary":"shift.request_decided","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"person.created":{"post":{"summary":"person.created","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"person.updated":{"post":{"summary":"person.updated","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"placement.created":{"post":{"summary":"placement.created","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"placement.updated":{"post":{"summary":"placement.updated","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"claim.created":{"post":{"summary":"claim.created","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"expense.decided":{"post":{"summary":"expense.decided","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"leave.cancelled":{"post":{"summary":"leave.cancelled","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"webhook.test":{"post":{"summary":"webhook.test","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"client.created":{"post":{"summary":"client.created","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"client.updated":{"post":{"summary":"client.updated","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}},"conflict.resolved":{"post":{"summary":"conflict.resolved","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return 2xx to acknowledge; anything else is retried with backoff (1m → 24h, 7 attempts)."}}}}}}