{"openapi":"3.1.0","info":{"title":"SmartClocker API","version":"1.0.0","summary":"Time & attendance data for your SmartClocker projects.","description":"# Getting started\n\nThe SmartClocker API gives you programmatic access to everything in a SmartClocker project: employees, departments and roles, raw punches, paired time entries with payable hours, timesheets, schedules, shifts and devices — the same data you see in the admin dashboard.\n\n**Base URL:** `https://api.smartclocker.com/v1`\n**Format:** JSON over HTTPS. All timestamps are ISO-8601 in UTC unless stated otherwise.\n\n## 1. Create an API key\n\n1. Sign in to the SmartClocker admin, open the project you want to integrate, and go to **Settings → API Keys**.\n2. Click **Create key**, give it a name (e.g. *Payroll sync*), pick the scopes it needs, and optionally set an expiry date.\n3. Copy the key — it starts with `sc_live_` and is **shown only once**. Store it in a secret manager; SmartClocker only keeps a hash.\n\nEach key belongs to exactly one project. If you manage several projects, create one key per project.\n\n> API access is included in the Growth, Business and Enterprise plans.\n\n## 2. Make your first request\n\n```bash\ncurl https://api.smartclocker.com/v1/me \\\n  -H \"Authorization: Bearer sc_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n```\n\n```json\n{\n  \"data\": {\n    \"project\": { \"id\": \"…\", \"name\": \"Acme Warehouse\", \"timezone\": \"America/Toronto\", \"status\": \"active\", \"…\": \"…\" },\n    \"api_key\": { \"name\": \"Payroll sync\", \"scopes\": [\"employees:read\", \"timesheets:read\"], \"…\": \"…\" }\n  }\n}\n```\n\n## 3. Pull last week's hours\n\n```bash\ncurl \"https://api.smartclocker.com/v1/timesheets?from=2026-08-10&to=2026-08-16\" \\\n  -H \"Authorization: Bearer $SMARTCLOCKER_API_KEY\"\n```\n\nDates without a time are interpreted as whole days in the project's timezone, so this returns Monday 00:00 through Sunday 23:59:59 local time — matching what the weekly report emails.\n\n## Response envelope\n\nSingle resources are wrapped in `data`; lists add `meta` for pagination:\n\n```json\n{ \"data\": { \"id\": \"…\", \"first_name\": \"Ada\" } }\n```\n```json\n{ \"data\": [ … ], \"meta\": { \"has_more\": true, \"next_cursor\": \"eyJ2Ijoi…\", \"limit\": 50 } }\n```\n\nEvery response carries an `X-Request-Id` header. Include it when contacting support about a specific call.\n\n\n# Authentication\n\nEvery request must carry your project API key as a Bearer token:\n\n```\nAuthorization: Bearer sc_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n```\n\nRequests without a valid key get `401 unauthorized`. Keys can be **revoked** or given an **expiry** in the admin; revoked/expired keys are rejected within 30 seconds.\n\n## Scopes\n\nEach key carries a set of scopes chosen when it was created. An endpoint lists its required scope in the reference below (`x-required-scope`). A `:write` scope implicitly includes the matching `:read` scope. Calling an endpoint outside the key's scopes returns `403 insufficient_scope`.\n\n| Scope | Grants |\n|---|---|\n| `project:read` | Read project details, timezone and settings |\n| `employees:read` | List and read employees and their punch status |\n| `employees:write` | Create, update and deactivate employees |\n| `departments:read` | List and read departments and their time rules |\n| `departments:write` | Create and update departments |\n| `roles:read` | List and read roles |\n| `roles:write` | Create and update roles |\n| `time_entries:read` | Read punches and time entries (sessions) |\n| `time_entries:write` | Create manual time entries, edit and delete punches |\n| `timesheets:read` | Read computed hours and pay summaries |\n| `schedules:read` | Read schedules, shifts and availability |\n| `schedules:write` | Create, update and delete schedules and shifts |\n| `devices:read` | List clock-in devices and their online state |\n\nGrant the minimum set a given integration needs — e.g. a payroll export only needs `employees:read` and `timesheets:read`.\n\n## Keeping keys safe\n\n- Treat keys like passwords: never embed them in mobile apps, browser code or public repositories.\n- Use one key per integration so you can rotate or revoke them independently.\n- The API is server-to-server: there is no CORS allow-list, so browsers cannot call it directly. Proxy through your own backend.\n- If a key leaks, revoke it in **Settings → API Keys** and create a new one.\n\n\n# Conventions\n\n## Pagination\n\nList endpoints return at most `limit` items (default 50, max 200) and use **cursor pagination**:\n\n```\nGET /v1/employees?limit=100\n→ { \"data\": [...], \"meta\": { \"has_more\": true, \"next_cursor\": \"eyJ2Ijoi…\", \"limit\": 100 } }\n\nGET /v1/employees?limit=100&cursor=eyJ2Ijoi…\n```\n\nKeep requesting with `cursor = meta.next_cursor` until `has_more` is `false`. Cursors are opaque and only valid for the same endpoint + filters.\n\n## Timestamps & timezones\n\n- The API **returns** all timestamps as ISO-8601 **UTC** strings, e.g. `2026-08-17T13:00:00.000Z`. Time entries and timesheet rows also include a `date` field with the local calendar day.\n- Every project has a timezone (`GET /v1/me` → `project.timezone`). Wherever the API **accepts** a timestamp (`from`, `to`, `punch_in`, `start_time`, …) you may send:\n  - an ISO-8601 value with an offset or `Z` — treated as an absolute instant (`2026-08-17T09:00:00-04:00`);\n  - a date-time without offset — read as wall-clock time in the project's timezone (`2026-08-17T09:00`);\n  - a bare date `YYYY-MM-DD` — the start of that day for `from`, the end of that day for `to`.\n\n## Errors\n\nErrors use conventional HTTP status codes and a consistent body:\n\n```json\n{\n  \"error\": {\n    \"code\": \"validation_error\",\n    \"message\": \"The request is invalid.\",\n    \"details\": { \"in\": \"body\", \"issues\": [ { \"path\": \"pin\", \"message\": \"PIN must be 4-8 digits\" } ] },\n    \"request_id\": \"req_8f2b1c0e9a1d\"\n  }\n}\n```\n\n| Status | `code` | Meaning |\n|---|---|---|\n| 400 | `validation_error`, `bad_request`, `invalid_json` | Malformed request or parameters. `details.issues` lists the offending fields. |\n| 401 | `unauthorized` | Missing, malformed, revoked or expired API key. |\n| 403 | `insufficient_scope` | The key lacks the scope this endpoint requires (`details.required_scope`). |\n| 403 | `api_access_not_included`, `project_inactive`, `employee_locked` | Plan, project or record state forbids the action. |\n| 404 | `not_found` | No such resource in **this** project. |\n| 409 | `conflict` | Uniqueness violation, e.g. duplicate employee PIN. |\n| 422 | `unprocessable` | Semantically invalid (punch_out before punch_in, unknown department_id…). |\n| 429 | `rate_limited` | Slow down; see `Retry-After`. |\n| 5xx | `internal_error`, `upstream_error` | Something failed on our side — retry with backoff and quote the `request_id` to support. |\n\n## Rate limits\n\nEach key may make **300 requests per minute** (burst-friendly token bucket). Every response includes:\n\n```\nX-RateLimit-Limit: 300\nX-RateLimit-Remaining: 287\nX-RateLimit-Reset: 1755450000     ← unix seconds when the bucket is full again\n```\n\nOn `429` also honour `Retry-After` (seconds). Need more? Contact support.\n\n## Idempotency & retries\n\n`GET` requests are safe to retry. For `POST /time-entries` and `POST /employees`, retry only after checking whether the first attempt succeeded (e.g. list time entries for that employee/day, or look the employee up by `employee_number`), to avoid duplicates.\n\n## Versioning\n\nThe API is versioned in the path (`/v1`). Backwards-compatible changes — new fields, new endpoints, new optional parameters, new enum values — may be added at any time; write clients that ignore unknown fields. Breaking changes will ship as `/v2` with advance notice.\n\n\n# Core concepts\n\n## Punches vs. time entries\n\nA **punch** is a single event recorded on a device or entered by hand: `punch_in`, `punch_out`, `break_start`, `break_end`. Punches carry a `status`:\n\n| `status` | `status_label` | Meaning |\n|---|---|---|\n| 0 | `processing` | Just recorded, face verification pending |\n| 1 | `complete` | Normal, verified punch |\n| 3 | `missed_punch_out` | Employee never punched out; the system closed the session |\n| 4 | `edited` | A time in the session was changed after the fact (see `history`) |\n| 5 | `face_verification_failed` | Punch recorded but the face didn't match |\n| 6 | `manual_entry` | Hours added by an admin or through this API |\n| 7 | `banned` | Rejected by an admin; never counted |\n| 8 | `approved` | Reviewed and approved by an admin |\n| 9 | `pending_review` | Waiting for an admin decision |\n\nA **time entry** is a work session: a `punch_in` paired with its `punch_out` and any breaks in between (they share a `time_entry_id`). Time entries carry computed `hours`:\n\n- `worked` — punch-in → punch-out after the department's grace period and rounding are applied,\n- `breaks` — the break time the department's rule deducts,\n- `payable` — `worked − breaks`; **this is the number payroll should use** and it matches the SmartClocker reports exactly.\n\n`adjusted.punch_in / punch_out` are the rounded times used for that calculation, while `punch_in.timestamp / punch_out.timestamp` are the raw punches.\n\nBy default time-entry and timesheet endpoints only include statuses **1, 4, 6, 8** (complete, edited, manual, approved) — the same filter the reports use. Pass `status=all` or an explicit list to see everything.\n\n## Department rules\n\nEach employee belongs to a department, and the department decides how hours are computed:\n\n- **Rounding** (minutes, per punch kind): punch-in rounds **up** to the next interval, punch-out rounds **down**; 0 = no rounding.\n- **Grace period** (minutes): a punch within the grace window of an interval boundary snaps to that boundary before rounding, e.g. 9:03 with a 5-minute grace and 15-minute rounding counts as 9:00.\n- **Break option**:\n  - `none` / `pay_for_breaks` — breaks are paid; nothing is deducted;\n  - `auto_deduct_30_minute` — 30 minutes are deducted from any session longer than 5 hours, regardless of recorded breaks;\n  - `deduct_all_breaks` — every recorded break (rounded per the break rounding rules) is deducted.\n\nChange these via `PATCH /v1/departments/{id}`; new rules apply to every report and time entry computed after the change.\n\n## Employee status\n\n`Employee.status` is derived from the last punch: `punched_in`, `on_break` or `punched_out`. It's live — poll `GET /v1/employees?status=punched_in` for a \"who's in right now\" board.\n\n## Manual entries, edits and deletes\n\n- `POST /v1/time-entries` adds a full session as **manual entry** (status 6), exactly like the admin's *Add hours* dialog.\n- `PATCH /v1/time-entries/{id}` changes the punch-in / punch-out time. The previous value is appended to the punch's audit history and the session becomes **edited** (status 4).\n- `DELETE /v1/time-entries/{id}` moves the session's punches to the deleted-punches archive; SmartClocker support can restore them.\n- **Locked employees** (`Employee.locked = true`) cannot be modified through the API — only the project owner in the admin can change their hours.\n\n## Devices\n\nChanges to employees made through the API (create, update, deactivate) are pushed to all online clock devices within seconds — no restart needed. New employees still need to enrol a face photo on a device before face verification applies to them.\n\n\n# Recipes\n\n## Nightly payroll export\n\n```bash\n# Yesterday, in the project's timezone\nDAY=$(date -d \"yesterday\" +%F)   # macOS: date -v-1d +%F\ncurl -s \"https://api.smartclocker.com/v1/timesheets?from=$DAY&to=$DAY\" \\\n  -H \"Authorization: Bearer $SMARTCLOCKER_API_KEY\" \\\n| jq -r '.data.timesheets[] | [.employee_number, .full_name, .total_hours, .total_amount] | @csv'\n```\n\n## Sync your HR system's employees into SmartClocker\n\n```js\nconst base = \"https://api.smartclocker.com/v1\";\nconst headers = { Authorization: `Bearer ${process.env.SMARTCLOCKER_API_KEY}`, \"content-type\": \"application/json\" };\n\n// 1. Load existing employees keyed by your employee_number\nconst existing = new Map();\nlet cursor;\ndo {\n  const url = new URL(`${base}/employees`);\n  url.searchParams.set(\"active\", \"all\");\n  url.searchParams.set(\"limit\", \"200\");\n  if (cursor) url.searchParams.set(\"cursor\", cursor);\n  const { data, meta } = await fetch(url, { headers }).then(r => r.json());\n  for (const e of data) existing.set(e.employee_number, e);\n  cursor = meta.has_more ? meta.next_cursor : null;\n} while (cursor);\n\n// 2. Upsert\nfor (const person of hrPeople) {\n  const found = existing.get(person.badge);\n  const body = { first_name: person.first, last_name: person.last, employee_number: person.badge,\n                 email: person.email, hourly_rate: person.rate, active: person.employed };\n  if (found) {\n    await fetch(`${base}/employees/${found.id}`, { method: \"PATCH\", headers, body: JSON.stringify(body) });\n  } else {\n    await fetch(`${base}/employees`, { method: \"POST\", headers, body: JSON.stringify({ ...body, pin: person.pin }) });\n  }\n}\n```\n\n## \"Who is clocked in right now?\"\n\n```bash\ncurl -s \"https://api.smartclocker.com/v1/employees?status=punched_in&limit=200\" \\\n  -H \"Authorization: Bearer $SMARTCLOCKER_API_KEY\" | jq '.data[] | .full_name'\n```\n\n## Add missed hours for an employee\n\n```bash\ncurl -X POST https://api.smartclocker.com/v1/time-entries \\\n  -H \"Authorization: Bearer $SMARTCLOCKER_API_KEY\" -H \"content-type: application/json\" \\\n  -d '{\n    \"employee_id\": \"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c\",\n    \"punch_in\":  \"2026-08-17T09:00\",\n    \"punch_out\": \"2026-08-17T17:00\",\n    \"breaks\": [{ \"start\": \"2026-08-17T12:00\", \"end\": \"2026-08-17T12:30\" }],\n    \"note\": \"Forgot to clock in — confirmed by supervisor\"\n  }'\n```\n\nTimes without an offset are read in the project's timezone.\n\n## Publish next week's schedule\n\n```bash\nSCHED=$(curl -s -X POST https://api.smartclocker.com/v1/schedules \\\n  -H \"Authorization: Bearer $KEY\" -H \"content-type: application/json\" \\\n  -d '{\"name\":\"Week 34\",\"start_date\":\"2026-08-17\",\"end_date\":\"2026-08-23\"}' | jq -r .data.id)\n\ncurl -s -X POST https://api.smartclocker.com/v1/shifts -H \"Authorization: Bearer $KEY\" -H \"content-type: application/json\" \\\n  -d \"{\\\"schedule_id\\\":\\\"$SCHED\\\",\\\"employee_id\\\":\\\"…\\\",\\\"start_time\\\":\\\"2026-08-17T09:00\\\",\\\"end_time\\\":\\\"2026-08-17T17:00\\\",\\\"break_duration_minutes\\\":30}\"\n\ncurl -s -X PATCH https://api.smartclocker.com/v1/schedules/$SCHED -H \"Authorization: Bearer $KEY\" -H \"content-type: application/json\" \\\n  -d '{\"status\":\"published\"}'\n```\n\n## Webhooks\n\nWebhooks (punch created, employee updated, device online/offline) are on the roadmap. Until then, poll `GET /v1/punches?from=<last checkpoint>` — punches are returned newest first, and `created_at` lets you checkpoint reliably.\n\n---\n\n# API reference\n\nEverything below is generated from the live API. Each operation lists the scope it requires. Use the **Try it** panel with your own key to explore.\n","contact":{"name":"SmartClocker support","email":"support@smartclocker.com","url":"https://smartclocker.com"},"x-logo":{"url":"https://smartclocker.com/images/logo.png","altText":"SmartClocker"}},"servers":[{"url":"https://api.smartclocker.com","description":"Production"}],"tags":[{"name":"Project","description":"The project (tenant) your API key belongs to."},{"name":"Employees","description":"People who clock in and out. Employees belong to one department and optionally one role."},{"name":"Departments","description":"Groups of employees. Departments carry the rounding, grace-period and break rules that turn punches into payable hours."},{"name":"Roles","description":"Positions/job titles (Cashier, Supervisor…). Used for reporting and scheduling."},{"name":"Punches","description":"Raw punch events exactly as recorded — punch in / punch out / break start / break end."},{"name":"Time entries","description":"Work sessions: a punch-in paired with its punch-out and breaks, with hours computed the same way SmartClocker's reports do. Create manual entries, edit or delete sessions."},{"name":"Timesheets","description":"Payable hours and amounts per employee for a period — a machine-readable version of the daily / weekly / bi-weekly reports."},{"name":"Schedules","description":"Scheduling periods (Scheduling add-on)."},{"name":"Shifts","description":"Planned shifts inside a schedule, and employee availability."},{"name":"Devices","description":"Clock-in devices (kiosks/tablets) and their connection state."}],"security":[{"ApiKeyAuth":[]}],"externalDocs":{"description":"SmartClocker","url":"https://smartclocker.com"},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"sc_live_...","description":"Send your project API key as a Bearer token: `Authorization: Bearer sc_live_...`. Keys are created in the SmartClocker admin under **Settings → API Keys**."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"Employee not found."},"details":{},"request_id":{"type":"string","example":"req_8f2b1c0e"}},"required":["code","message"]}},"required":["error"]},"PaginationMeta":{"type":"object","properties":{"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` to fetch the next page."},"limit":{"type":"integer"}},"required":["has_more","next_cursor","limit"]},"Project":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":"string","example":"Acme Warehouse"},"code":{"type":["string","null"],"example":"SC8JTSK1","description":"Short code employees/devices use to find the project."},"status":{"type":"string","example":"active"},"timezone":{"type":"string","example":"America/Toronto","description":"IANA timezone. Offset-less timestamps in requests are interpreted in this zone."},"end_of_day":{"type":["object","null"],"properties":{"time":{"type":["string","null"],"example":"17:00"},"auto_punch_out":{"type":"boolean"}},"required":["time","auto_punch_out"]},"plan":{"type":["object","null"],"properties":{"id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":["string","null"]}},"required":["id","name"]},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","name","code","status","timezone","end_of_day","plan","created_at"]},"Me":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/Project"},"api_key":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":"string","example":"Payroll integration"},"prefix":{"type":"string","example":"sc_live_3fA9kQ2z"},"scopes":{"type":"array","items":{"type":"string"},"example":["employees:read","time_entries:read"]},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"expires_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","name","prefix","scopes","created_at","expires_at"]}},"required":["project","api_key"]},"Employee":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"employee_number":{"type":["string","null"],"example":"E-1001","description":"Your own identifier for the employee (badge / payroll number)."},"first_name":{"type":["string","null"],"example":"Ada"},"last_name":{"type":["string","null"],"example":"Lovelace"},"full_name":{"type":["string","null"],"example":"Ada Lovelace"},"email":{"type":["string","null"],"example":"ada@example.com"},"phone":{"type":["string","null"],"example":"(555) 010-1001"},"hire_date":{"type":["string","null"],"example":"2026-01-15"},"date_of_birth":{"type":["string","null"],"example":"1990-12-10"},"hourly_rate":{"type":["number","null"],"example":22.5},"department_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"role_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"active":{"type":"boolean"},"locked":{"type":"boolean","description":"Locked employees cannot have their punches edited except by the project owner."},"status":{"type":"string","enum":["punched_in","punched_out","on_break"],"description":"Live clock status derived from the last punch."},"has_face_reference":{"type":"boolean","description":"Whether a face-recognition reference photo is enrolled."},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","employee_number","first_name","last_name","full_name","email","phone","hire_date","date_of_birth","hourly_rate","department_id","role_id","active","locked","status","has_face_reference","created_at"]},"EmployeeCreate":{"type":"object","properties":{"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","minLength":1,"maxLength":100},"pin":{"type":"string","pattern":"^\\d{4,8}$","example":"1001","description":"Numeric PIN the employee types on the clock. Must be unique within the project."},"employee_number":{"type":["string","null"],"maxLength":50},"email":{"type":["string","null"],"maxLength":255,"format":"email"},"phone":{"type":["string","null"],"maxLength":50},"hire_date":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"date_of_birth":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"hourly_rate":{"type":["number","null"],"minimum":0},"department_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"role_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"active":{"type":"boolean","default":true}},"required":["first_name","last_name","pin"]},"EmployeeUpdate":{"type":"object","properties":{"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","minLength":1,"maxLength":100},"pin":{"type":"string","pattern":"^\\d{4,8}$","example":"1001","description":"Numeric PIN the employee types on the clock. Must be unique within the project."},"employee_number":{"type":["string","null"],"maxLength":50},"email":{"type":["string","null"],"maxLength":255,"format":"email"},"phone":{"type":["string","null"],"maxLength":50},"hire_date":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"date_of_birth":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"hourly_rate":{"type":["number","null"],"minimum":0},"department_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"role_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"active":{"type":"boolean","default":true},"locked":{"type":"boolean"}}},"Department":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":"string","example":"Warehouse"},"description":{"type":["string","null"]},"location":{"type":["string","null"]},"active":{"type":"boolean"},"manager_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c","description":"Employee id of the department manager, if any."},"budget":{"type":["number","null"]},"rounding":{"type":"object","properties":{"punch_in":{"type":"integer"},"punch_out":{"type":"integer"},"break_start":{"type":"integer"},"break_end":{"type":"integer"}},"required":["punch_in","punch_out","break_start","break_end"],"description":"Rounding interval in minutes per punch kind (0 = no rounding). Punch-in rounds up, punch-out rounds down."},"grace_period":{"type":"object","properties":{"punch_in":{"type":"number"},"punch_out":{"type":"number"},"break_start":{"type":"number"},"break_end":{"type":"number"}},"required":["punch_in","punch_out","break_start","break_end"],"description":"Grace minutes applied before rounding."},"break_option":{"type":["string","null"],"enum":["none","pay_for_breaks","auto_deduct_30_minute","deduct_all_breaks",null],"description":"How breaks affect payable hours. `auto_deduct_30_minute` deducts 30 min from any session over 5 h; `deduct_all_breaks` subtracts recorded breaks; `pay_for_breaks`/`none` deduct nothing."},"auto_punch_out_minutes":{"type":["number","null"],"description":"Legacy per-department auto punch-out (minutes)."},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","name","description","location","active","manager_id","budget","rounding","grace_period","break_option","auto_punch_out_minutes","created_at"]},"DepartmentWrite":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":["string","null"],"maxLength":500},"location":{"type":["string","null"],"maxLength":255},"active":{"type":"boolean"},"manager_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"budget":{"type":["number","null"]},"rounding":{"type":"object","properties":{"punch_in":{"type":"integer","minimum":0,"maximum":60},"punch_out":{"type":"integer","minimum":0,"maximum":60},"break_start":{"type":"integer","minimum":0,"maximum":60},"break_end":{"type":"integer","minimum":0,"maximum":60}}},"grace_period":{"type":"object","properties":{"punch_in":{"type":"number","minimum":0,"maximum":60},"punch_out":{"type":"number","minimum":0,"maximum":60},"break_start":{"type":"number","minimum":0,"maximum":60},"break_end":{"type":"number","minimum":0,"maximum":60}}},"break_option":{"type":"string","enum":["none","pay_for_breaks","auto_deduct_30_minute","deduct_all_breaks"],"description":"How breaks affect payable hours. `auto_deduct_30_minute` deducts 30 min from any session over 5 h; `deduct_all_breaks` subtracts recorded breaks; `pay_for_breaks`/`none` deduct nothing."}},"required":["name"]},"DepartmentUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":["string","null"],"maxLength":500},"location":{"type":["string","null"],"maxLength":255},"active":{"type":"boolean"},"manager_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"budget":{"type":["number","null"]},"rounding":{"type":"object","properties":{"punch_in":{"type":"integer","minimum":0,"maximum":60},"punch_out":{"type":"integer","minimum":0,"maximum":60},"break_start":{"type":"integer","minimum":0,"maximum":60},"break_end":{"type":"integer","minimum":0,"maximum":60}}},"grace_period":{"type":"object","properties":{"punch_in":{"type":"number","minimum":0,"maximum":60},"punch_out":{"type":"number","minimum":0,"maximum":60},"break_start":{"type":"number","minimum":0,"maximum":60},"break_end":{"type":"number","minimum":0,"maximum":60}}},"break_option":{"type":"string","enum":["none","pay_for_breaks","auto_deduct_30_minute","deduct_all_breaks"],"description":"How breaks affect payable hours. `auto_deduct_30_minute` deducts 30 min from any session over 5 h; `deduct_all_breaks` subtracts recorded breaks; `pay_for_breaks`/`none` deduct nothing."}}},"Role":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":"string","example":"Cashier"},"description":{"type":["string","null"]},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","name","description","created_at"]},"RoleWrite":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":["string","null"],"maxLength":500}},"required":["name"]},"RoleUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":["string","null"],"maxLength":500}}},"Punch":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"type":{"type":"string","enum":["punch_in","punch_out","break_start","break_end"]},"employee_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"time_entry_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c","description":"Groups the punches of one work session (punch in → punch out)."},"timestamp":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"status":{"type":"integer","description":"Numeric status. See `status_label` for meaning.","example":1},"status_label":{"type":"string","enum":["processing","complete","missed_punch_out","edited","face_verification_failed","manual_entry","banned","approved","pending_review"],"example":"complete"},"source":{"type":"string","enum":["device","manual","system","unknown"],"description":"`device` = clocked on a SmartClocker device, `manual` = entered by an admin or via API, `system` = auto punch-out."},"device_id":{"type":["string","null"]},"face_verified":{"type":["boolean","null"]},"location":{"type":["object","null"],"properties":{"latitude":{"type":"number"},"longitude":{"type":"number"},"accuracy":{"type":["number","null"]}},"required":["latitude","longitude","accuracy"]},"note":{"type":["string","null"]},"edited":{"type":"boolean","description":"True if the timestamp was changed after the fact."},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","type","employee_id","time_entry_id","timestamp","status","status_label","source","device_id","face_verified","location","note","edited","created_at"]},"TimeEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c","description":"Time entry id (= the punches' `time_entry_id`)."},"employee_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"department_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"date":{"type":["string","null"],"example":"2026-08-17","description":"Local calendar date of the punch-in (project timezone)."},"punch_in":{"type":"object","properties":{"punch_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"timestamp":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["punch_id","timestamp"]},"punch_out":{"type":["object","null"],"properties":{"punch_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"timestamp":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["punch_id","timestamp"]},"breaks":{"type":"array","items":{"type":"object","properties":{"start":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"end":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"minutes":{"type":["number","null"]}},"required":["start","end","minutes"]}},"open":{"type":"boolean","description":"True while the employee is still clocked in."},"status":{"type":"integer","description":"Numeric status. See `status_label` for meaning.","example":1},"status_label":{"type":"string","enum":["processing","complete","missed_punch_out","edited","face_verification_failed","manual_entry","banned","approved","pending_review"],"example":"complete"},"hours":{"type":["object","null"],"properties":{"worked":{"type":"number","description":"Punch-in → punch-out after department rounding & grace, before break deduction."},"breaks":{"type":"number","description":"Break time counted against the session under the department's break rule."},"payable":{"type":"number","description":"Hours to pay = worked − deducted breaks. This matches the SmartClocker reports."}},"required":["worked","breaks","payable"]},"adjusted":{"type":["object","null"],"properties":{"punch_in":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"punch_out":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["punch_in","punch_out"],"description":"Punch times after rounding/grace rules were applied (what payroll uses)."},"note":{"type":["string","null"]},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","employee_id","department_id","date","punch_in","punch_out","breaks","open","status","status_label","hours","adjusted","note","created_at"]},"TimeEntryCreate":{"type":"object","properties":{"employee_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"punch_in":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"punch_out":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"breaks":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"end":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"}},"required":["start","end"]},"maxItems":10,"default":[]},"note":{"type":"string","maxLength":500}},"required":["employee_id","punch_in","punch_out"]},"TimeEntryUpdate":{"type":"object","properties":{"punch_in":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"punch_out":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"note":{"type":"string","maxLength":500}}},"TimesheetEntry":{"type":"object","properties":{"time_entry_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"date":{"type":["string","null"]},"punch_in":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"punch_out":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"break_hours":{"type":"number"},"payable_hours":{"type":"number"},"amount":{"type":["number","null"]}},"required":["time_entry_id","date","punch_in","punch_out","break_hours","payable_hours","amount"]},"Timesheet":{"type":"object","properties":{"employee_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"employee_number":{"type":["string","null"]},"full_name":{"type":["string","null"]},"department_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"role_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"role_name":{"type":["string","null"]},"hourly_rate":{"type":["number","null"]},"total_hours":{"type":"number","description":"Sum of payable hours in the period."},"total_amount":{"type":["number","null"],"description":"total_hours × hourly_rate (null if no rate)."},"entries":{"type":"array","items":{"$ref":"#/components/schemas/TimesheetEntry"}}},"required":["employee_id","employee_number","full_name","department_id","role_id","role_name","hourly_rate","total_hours","total_amount","entries"]},"TimesheetReport":{"type":"object","properties":{"from":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"to":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"timezone":{"type":"string"},"statuses":{"type":"array","items":{"type":"integer"},"description":"Punch statuses included (default: complete, edited, manual, approved)."},"totals":{"type":"object","properties":{"hours":{"type":"number"},"amount":{"type":"number"}},"required":["hours","amount"]},"timesheets":{"type":"array","items":{"$ref":"#/components/schemas/Timesheet"}}},"required":["from","to","timezone","statuses","totals","timesheets"]},"Schedule":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":["string","null"]},"start_date":{"type":["string","null"],"example":"2026-08-17"},"end_date":{"type":["string","null"],"example":"2026-08-23"},"status":{"type":["string","null"],"enum":["draft","published",null]},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"updated_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","name","start_date","end_date","status","created_at","updated_at"]},"ScheduleWrite":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"start_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"end_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"status":{"type":"string","enum":["draft","published"],"default":"draft"}},"required":["name","start_date","end_date"]},"ScheduleUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"start_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"end_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"status":{"type":"string","enum":["draft","published"],"default":"draft"}}},"Shift":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"schedule_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"employee_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c","description":"Null for open shifts."},"role_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"department_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":["string","null"]},"start_time":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"end_time":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"break_duration_minutes":{"type":["integer","null"]},"is_open_shift":{"type":"boolean"},"notes":{"type":["string","null"]},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"updated_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","schedule_id","employee_id","role_id","department_id","name","start_time","end_time","break_duration_minutes","is_open_shift","notes","created_at","updated_at"]},"ShiftWrite":{"type":"object","properties":{"schedule_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"employee_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"role_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"department_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":["string","null"],"maxLength":200},"start_time":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"end_time":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"break_duration_minutes":{"type":"integer","minimum":0,"maximum":600,"default":0},"is_open_shift":{"type":"boolean"},"notes":{"type":["string","null"],"maxLength":1000}},"required":["schedule_id","start_time","end_time"]},"ShiftUpdate":{"type":"object","properties":{"schedule_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"employee_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"role_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"department_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"name":{"type":["string","null"],"maxLength":200},"start_time":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"end_time":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"break_duration_minutes":{"type":"integer","minimum":0,"maximum":600,"default":0},"is_open_shift":{"type":"boolean"},"notes":{"type":["string","null"],"maxLength":1000}}},"Availability":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"employee_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"day_of_week":{"type":"integer","minimum":0,"maximum":6,"description":"0 = Sunday … 6 = Saturday"},"start_time":{"type":["string","null"],"example":"09:00:00"},"end_time":{"type":["string","null"],"example":"17:00:00"}},"required":["id","employee_id","day_of_week","start_time","end_time"]},"Device":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"code":{"type":["string","null"]},"type":{"type":["string","null"],"example":"smart_clocker"},"online":{"type":"boolean"},"last_seen_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"},"created_at":{"type":["string","null"],"format":"date-time","example":"2026-08-17T13:00:00.000Z"}},"required":["id","name","code","type","online","last_seen_at","created_at"]}},"parameters":{}},"paths":{"/v1/me":{"get":{"operationId":"getMe","summary":"Who am I","description":"Returns the project this API key belongs to, its timezone, and the key's scopes. A good first call to verify your integration.\n\n**Required scope:** `project:read`","tags":["Project"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"project:read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Me"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/project":{"get":{"operationId":"getProject","summary":"Get project","description":"Returns the project (tenant) details: name, code, status, timezone and end-of-day settings.\n\n**Required scope:** `project:read`","tags":["Project"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"project:read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Project"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/employees":{"get":{"operationId":"getEmployees","summary":"List employees","description":"Lists employees in the project, newest first. Use `active=all` to include deactivated employees.\n\n**Required scope:** `employees:read`","tags":["Employees"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"employees:read","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size (1-200).","example":50},"required":false,"description":"Page size (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from `meta.next_cursor` of the previous page."},"required":false,"description":"Opaque cursor from `meta.next_cursor` of the previous page.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["true","false","all"],"default":"true","description":"Filter by active flag. Default `true`; use `all` for everyone."},"required":false,"description":"Filter by active flag. Default `true`; use `all` for everyone.","name":"active","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"department_id","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"role_id","in":"query"},{"schema":{"type":"string","enum":["punched_in","punched_out","on_break"],"description":"Filter by live clock status."},"required":false,"description":"Filter by live clock status.","name":"status","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":100,"description":"Case-insensitive search on first/last name, email and employee number."},"required":false,"description":"Case-insensitive search on first/last name, email and employee number.","name":"q","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Employee"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["data","meta"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postEmployees","summary":"Create employee","description":"Creates an employee. `pin` must be unique within the project. New employees start as punched out and are pushed to all online clock devices immediately.\n\n**Required scope:** `employees:write`","tags":["Employees"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"employees:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeCreate"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Employee"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/employees/{id}":{"get":{"operationId":"getEmployeesById","summary":"Get employee","description":"**Required scope:** `employees:read`","tags":["Employees"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"employees:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Employee"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchEmployeesById","summary":"Update employee","description":"Partially updates an employee. Only the fields you send are changed. Changes are pushed to online clock devices.\n\n**Required scope:** `employees:write`","tags":["Employees"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"employees:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeUpdate"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Employee"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteEmployeesById","summary":"Deactivate employee","description":"Soft-deletes an employee by setting `active=false`. Punch history is preserved and the employee disappears from clock devices. Re-activate with `PATCH { \"active\": true }`.\n\n**Required scope:** `employees:write`","tags":["Employees"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"employees:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Employee"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/employees/{id}/status":{"get":{"operationId":"getEmployeesByIdStatus","summary":"Get live clock status","description":"Returns whether the employee is currently punched in, on break or punched out, plus the timestamp of the last punch.\n\n**Required scope:** `employees:read`","tags":["Employees"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"employees:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"employee_id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"status":{"type":"string","enum":["punched_in","punched_out","on_break"],"description":"Live clock status derived from the last punch."},"last_punch":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"type":{"type":"string"},"timestamp":{"type":["string","null"]},"time_entry_id":{"type":["string","null"],"format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"}},"required":["id","type","timestamp","time_entry_id"]}},"required":["employee_id","status","last_punch"]}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/departments":{"get":{"operationId":"getDepartments","summary":"List departments","description":"Departments carry the rounding, grace-period and break rules used to compute payable hours.\n\n**Required scope:** `departments:read`","tags":["Departments"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"departments:read","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size (1-200).","example":50},"required":false,"description":"Page size (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from `meta.next_cursor` of the previous page."},"required":false,"description":"Opaque cursor from `meta.next_cursor` of the previous page.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["true","false","all"],"default":"all"},"required":false,"name":"active","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Department"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["data","meta"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postDepartments","summary":"Create department","description":"**Required scope:** `departments:write`","tags":["Departments"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"departments:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentWrite"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Department"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/departments/{id}":{"get":{"operationId":"getDepartmentsById","summary":"Get department","description":"**Required scope:** `departments:read`","tags":["Departments"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"departments:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Department"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchDepartmentsById","summary":"Update department","description":"Partially updates a department, including its rounding / grace / break rules. Affects how future reports compute hours.\n\n**Required scope:** `departments:write`","tags":["Departments"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"departments:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentUpdate"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Department"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/roles":{"get":{"operationId":"getRoles","summary":"List roles","description":"Roles (positions) that can be assigned to employees and shifts, e.g. Cashier, Supervisor.\n\n**Required scope:** `roles:read`","tags":["Roles"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"roles:read","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size (1-200).","example":50},"required":false,"description":"Page size (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from `meta.next_cursor` of the previous page."},"required":false,"description":"Opaque cursor from `meta.next_cursor` of the previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Role"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["data","meta"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postRoles","summary":"Create role","description":"**Required scope:** `roles:write`","tags":["Roles"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"roles:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleWrite"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Role"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/roles/{id}":{"get":{"operationId":"getRolesById","summary":"Get role","description":"**Required scope:** `roles:read`","tags":["Roles"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"roles:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Role"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchRolesById","summary":"Update role","description":"**Required scope:** `roles:write`","tags":["Roles"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"roles:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleUpdate"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Role"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/punches":{"get":{"operationId":"getPunches","summary":"List punches","description":"Raw punch events (punch in/out, break start/end) newest first. For paired sessions with computed hours use **Time entries** instead. `from`/`to` filter on the punch timestamp.\n\n**Required scope:** `time_entries:read`","tags":["Punches"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"time_entries:read","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size (1-200).","example":50},"required":false,"description":"Page size (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from `meta.next_cursor` of the previous page."},"required":false,"description":"Opaque cursor from `meta.next_cursor` of the previous page.","name":"cursor","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"employee_id","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"time_entry_id","in":"query"},{"schema":{"type":"string","enum":["punch_in","punch_out","break_start","break_end"]},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","pattern":"^(\\d+)(,\\d+)*$","description":"Comma-separated punch statuses to include, e.g. `1,4,6`. Default: all.","example":"1,4,6,8"},"required":false,"description":"Comma-separated punch statuses to include, e.g. `1,4,6`. Default: all.","name":"status","in":"query"},{"schema":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"required":false,"description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","name":"from","in":"query"},{"schema":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"required":false,"description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","name":"to","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Punch"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["data","meta"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/punches/{id}":{"get":{"operationId":"getPunchesById","summary":"Get punch","description":"**Required scope:** `time_entries:read`","tags":["Punches"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"time_entries:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Punch"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/time-entries":{"get":{"operationId":"getTimeEntries","summary":"List time entries","description":"Work sessions (punch in → punch out with breaks) newest first, with hours computed under the employee's department rules. `from`/`to` filter on the punch-in time. Open sessions (still clocked in) are included when `open=true|all`.\n\n**Required scope:** `time_entries:read`","tags":["Time entries"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"time_entries:read","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size (1-200).","example":50},"required":false,"description":"Page size (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from `meta.next_cursor` of the previous page."},"required":false,"description":"Opaque cursor from `meta.next_cursor` of the previous page.","name":"cursor","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"employee_id","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"department_id","in":"query"},{"schema":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"required":false,"description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","name":"from","in":"query"},{"schema":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"required":false,"description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","name":"to","in":"query"},{"schema":{"type":"string","pattern":"^(all|\\d+(,\\d+)*)$","default":"1,4,6,8","description":"Punch statuses to include (comma-separated) or `all`. Default `1,4,6,8` = complete, edited, manual, approved — the same set the SmartClocker reports use."},"required":false,"description":"Punch statuses to include (comma-separated) or `all`. Default `1,4,6,8` = complete, edited, manual, approved — the same set the SmartClocker reports use.","name":"status","in":"query"},{"schema":{"type":"string","enum":["true","false","all"],"default":"all","description":"`true` = only sessions still clocked in, `false` = only closed."},"required":false,"description":"`true` = only sessions still clocked in, `false` = only closed.","name":"open","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TimeEntry"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["data","meta"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postTimeEntries","summary":"Create manual time entry","description":"Creates a complete session (punch in, punch out and optional breaks) as a **manual entry** (status 6), exactly like adding hours in the admin. Breaks must fall inside the session. Timestamps without an offset are read in the project's timezone.\n\n**Required scope:** `time_entries:write`","tags":["Time entries"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"time_entries:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryCreate"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TimeEntry"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/time-entries/{id}":{"get":{"operationId":"getTimeEntriesById","summary":"Get time entry","description":"**Required scope:** `time_entries:read`","tags":["Time entries"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"time_entries:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TimeEntry"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchTimeEntriesById","summary":"Edit time entry","description":"Changes the punch-in and/or punch-out time of a session. The previous values are appended to the punch's audit `history` and the whole session is marked **edited** (status 4), matching the admin's behaviour. Breaks must remain inside the session.\n\n**Required scope:** `time_entries:write`","tags":["Time entries"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"time_entries:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryUpdate"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TimeEntry"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteTimeEntriesById","summary":"Delete time entry","description":"Moves every punch of the session to the deleted-punches archive (recoverable by SmartClocker support). Locked employees cannot be modified.\n\n**Required scope:** `time_entries:write`","tags":["Time entries"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"time_entries:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"No content"},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/timesheets":{"get":{"operationId":"getTimesheets","summary":"Timesheet report","description":"Payable hours per employee for a period — the same numbers as the SmartClocker daily/weekly/bi-weekly reports (department rounding, grace periods and break rules applied; only closed sessions counted). `from`/`to` are inclusive and, when given as `YYYY-MM-DD`, cover whole days in the project's timezone. Maximum range: 62 days.\n\n**Required scope:** `timesheets:read`","tags":["Timesheets"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"timesheets:read","parameters":[{"schema":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-11"},"required":true,"description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","name":"from","in":"query"},{"schema":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17"},"required":true,"description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","name":"to","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"employee_id","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"department_id","in":"query"},{"schema":{"type":"string","pattern":"^(all|\\d+(,\\d+)*)$","default":"1,4,6,8","description":"Punch statuses to include or `all`. Default `1,4,6,8` (complete, edited, manual, approved)."},"required":false,"description":"Punch statuses to include or `all`. Default `1,4,6,8` (complete, edited, manual, approved).","name":"status","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false","description":"Include deactivated employees."},"required":false,"description":"Include deactivated employees.","name":"include_inactive","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TimesheetReport"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/schedules":{"get":{"operationId":"getSchedules","summary":"List schedules","description":"Scheduling periods (e.g. a week). Requires the Scheduling add-on to be in use on the project.\n\n**Required scope:** `schedules:read`","tags":["Schedules"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:read","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size (1-200).","example":50},"required":false,"description":"Page size (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from `meta.next_cursor` of the previous page."},"required":false,"description":"Opaque cursor from `meta.next_cursor` of the previous page.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["draft","published"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Schedules ending on/after this date."},"required":false,"description":"Schedules ending on/after this date.","name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Schedules starting on/before this date."},"required":false,"description":"Schedules starting on/before this date.","name":"to","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Schedule"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["data","meta"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postSchedules","summary":"Create schedule","description":"**Required scope:** `schedules:write`","tags":["Schedules"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleWrite"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Schedule"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/schedules/{id}":{"get":{"operationId":"getSchedulesById","summary":"Get schedule","description":"**Required scope:** `schedules:read`","tags":["Schedules"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Schedule"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchSchedulesById","summary":"Update schedule","description":"**Required scope:** `schedules:write`","tags":["Schedules"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleUpdate"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Schedule"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteSchedulesById","summary":"Delete schedule","description":"Deletes a schedule and all of its shifts.\n\n**Required scope:** `schedules:write`","tags":["Schedules"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"No content"},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/shifts":{"get":{"operationId":"getShifts","summary":"List shifts","description":"Scheduled shifts, soonest first. Filter by schedule, employee, or a time window on `start_time`.\n\n**Required scope:** `schedules:read`","tags":["Shifts"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:read","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size (1-200).","example":50},"required":false,"description":"Page size (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from `meta.next_cursor` of the previous page."},"required":false,"description":"Opaque cursor from `meta.next_cursor` of the previous page.","name":"cursor","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"schedule_id","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"employee_id","in":"query"},{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":false,"name":"department_id","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"Only open (unassigned) shifts."},"required":false,"description":"Only open (unassigned) shifts.","name":"open","in":"query"},{"schema":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"required":false,"description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","name":"from","in":"query"},{"schema":{"type":"string","description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","example":"2026-08-17T09:00:00-04:00"},"required":false,"description":"ISO-8601 timestamp. With an offset/`Z` it is absolute; without one it is interpreted in the project's timezone. `YYYY-MM-DD` alone means the start of that day.","name":"to","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Shift"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["data","meta"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"postShifts","summary":"Create shift","description":"Creates a shift inside a schedule. Omit `employee_id` (or set `is_open_shift: true`) for an open shift.\n\n**Required scope:** `schedules:write`","tags":["Shifts"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftWrite"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Shift"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/shifts/{id}":{"get":{"operationId":"getShiftsById","summary":"Get shift","description":"**Required scope:** `schedules:read`","tags":["Shifts"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Shift"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patchShiftsById","summary":"Update shift","description":"**Required scope:** `schedules:write`","tags":["Shifts"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftUpdate"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Shift"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteShiftsById","summary":"Delete shift","description":"**Required scope:** `schedules:write`","tags":["Shifts"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:write","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"No content"},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/employees/{id}/availability":{"get":{"operationId":"getEmployeesByIdAvailability","summary":"Get employee availability","description":"Weekly availability windows the employee has set (day of week + time range).\n\n**Required scope:** `schedules:read`","tags":["Shifts"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"schedules:read","parameters":[{"schema":{"type":"string","format":"uuid","example":"3f1c9e0a-6b7d-4a2e-9c1b-2d4e5f6a7b8c"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Availability"}}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/devices":{"get":{"operationId":"getDevices","summary":"List devices","description":"Clock-in devices (tablets/kiosks) registered to the project and whether they are currently connected.\n\n**Required scope:** `devices:read`","tags":["Devices"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"devices:read","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"description":"Page size (1-200).","example":50},"required":false,"description":"Page size (1-200).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from `meta.next_cursor` of the previous page."},"required":false,"description":"Opaque cursor from `meta.next_cursor` of the previous page.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"online","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Device"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["data","meta"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/devices/{id}":{"get":{"operationId":"getDevicesById","summary":"Get device","description":"**Required scope:** `devices:read`","tags":["Devices"],"security":[{"ApiKeyAuth":[]}],"x-required-scope":"devices:read","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Device"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient scope or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{}}