{"openapi":"3.1.0","info":{"title":"CloudPay API","version":"1.0.0","description":"The CloudPay API lets your other systems — an HR system, a time clock, an accounting tool — read your employees, leave, attendance and payroll summaries, file leave requests and record clock punches.\n\n**Authentication.** Create a key in CloudPay under Settings › Integrations › Developers (Company Admin) and send it on every request as `Authorization: Bearer cpk_…`. A key belongs to your company and opens only the scopes chosen for it. It is shown once, when it is created: store it like a password. Revoke it there when it is no longer needed.\n\n**Scopes.** `employees:read` — Active employees: names, contact, department, job title, location, supervisor and dates. Never NIC, bank account or other protected fields. `leave:read` — Leave types, balances and leave requests. `leave:write` — File leave requests for employees. They wait for approval like a request from the app. `attendance:read` — Clock-in and clock-out punches with worked hours. `attendance:write` — Send clock-in and clock-out punches from a time clock, badge reader or another system. `payroll:read` — Payroll run summaries with each employee's gross, deductions and net pay, and salaries on the employee records.\n\n**Rate limits.** Each key may make 120 requests a minute unless set otherwise when it was created. Every answer carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix seconds); past the limit the API answers 429 with `Retry-After`.\n\n**Errors.** A refusal is JSON `{ \"error\": \"…\", \"code\": \"…\" }` with the HTTP status: 400 VALIDATION_FAILED, 401 API_KEY_REQUIRED / INVALID_API_KEY / API_KEY_REVOKED / API_KEY_EXPIRED, 402 TENANT_READ_ONLY, 403 INSUFFICIENT_SCOPE / FEATURE_DISABLED, 404 NOT_FOUND, 409 (conflicts), 422 EMPLOYEE_NOT_ACTIVE, 423 TENANT_SUSPENDED, PERIOD_CLOSED or PERIOD_CUTOFF (a pay period payroll has closed, or past its edit cutoff), 429 RATE_LIMITED / TOO_MANY_FAILED_ATTEMPTS.\n\n**Lists** take `page` (from 1) and `pageSize` (1–200, default 50) and answer `{ data, page, pageSize, total, hasMore }`; `updatedSince` returns only what changed since your last sync.\n\n**Dates and times.** Days are `yyyy-mm-dd` calendar days in Mauritius time; instants are ISO 8601 in UTC. Amounts are in Mauritian rupees (MUR).\n\n**Time terminals.** A badge, fingerprint or card terminal does not use an API key: it gets its own device key (`Authorization: Bearer cpd_…`) under Settings › Integrations › Time terminals, and sends its punches to `POST /punches` — by badge number, up to 200 at a time, 120 requests a minute. Every punch is kept in CloudPay's punch log and answered with what became of it.\n\n**Privacy.** National identity numbers, passports, work permits, bank accounts, addresses and medical details are never returned. Salaries and payroll figures need the `payroll:read` scope."},"servers":[{"url":"https://cloudpay.cloudbox8.com/api/v1","description":"This CloudPay deployment"}],"security":[{"apiKey":[]}],"tags":[{"name":"Employees","description":"The people of your company."},{"name":"Leave","description":"Leave types, balances and requests."},{"name":"Attendance","description":"Clock-in and clock-out punches."},{"name":"Time terminals","description":"Punches from badge, fingerprint and card terminals, sent with the terminal's device key."},{"name":"Payroll","description":"Payroll run summaries. Needs the Payroll module."}],"paths":{"/employees":{"get":{"operationId":"listEmployees","tags":["Employees"],"summary":"List employees","description":"Active employees by default, ordered by employee number. Pass `status=all` (or one status) for the others.","parameters":[{"name":"status","in":"query","description":"An employee status, or `all`. Default ACTIVE.","schema":{"type":"string","enum":["ACTIVE","ON_LEAVE","SUSPENDED","TERMINATED","RESIGNED","all"]}},{"name":"departmentId","in":"query","schema":{"type":"string"}},{"name":"locationId","in":"query","schema":{"type":"string"}},{"$ref":"#/components/parameters/updatedSince"},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"}],"responses":{"200":{"description":"A page of employees","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Employee"}},"page":{"type":"integer","description":"1-based page number"},"pageSize":{"type":"integer"},"total":{"type":"integer","description":"Rows matching the filters, all pages"},"hasMore":{"type":"boolean","description":"Another page follows"}},"required":["data","page","pageSize","total","hasMore"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["employees:read"]}],"x-scope":"employees:read"}},"/employees/{id}":{"get":{"operationId":"getEmployee","tags":["Employees"],"summary":"Get an employee","description":"By CloudPay id or employee number.","parameters":[{"name":"id","in":"path","required":true,"description":"Employee id or employee number","schema":{"type":"string"}}],"responses":{"200":{"description":"The employee","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Employee"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["employees:read"]}],"x-scope":"employees:read"}},"/leave/types":{"get":{"operationId":"listLeaveTypes","tags":["Leave"],"summary":"List leave types","description":"The leave types a request can name, by id or code.","responses":{"200":{"description":"Every leave type","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LeaveType"}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["leave:read"]}],"x-scope":"leave:read"}},"/leave/balances":{"get":{"operationId":"listLeaveBalances","tags":["Leave"],"summary":"List leave balances","description":"The balances of one leave year — the calendar year its validity window starts in — for the company or one employee.","parameters":[{"name":"year","in":"query","description":"Leave year. Default: the current year.","schema":{"type":"integer"}},{"$ref":"#/components/parameters/employeeId"},{"$ref":"#/components/parameters/employeeNo"},{"name":"leaveTypeId","in":"query","schema":{"type":"string"}},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"}],"responses":{"200":{"description":"A page of balances","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LeaveBalance"}},"page":{"type":"integer","description":"1-based page number"},"pageSize":{"type":"integer"},"total":{"type":"integer","description":"Rows matching the filters, all pages"},"hasMore":{"type":"boolean","description":"Another page follows"}},"required":["data","page","pageSize","total","hasMore"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["leave:read"]}],"x-scope":"leave:read"}},"/leave/requests":{"get":{"operationId":"listLeaveRequests","tags":["Leave"],"summary":"List leave requests","description":"Newest first. `from` / `to` return the requests that touch the period.","parameters":[{"name":"status","in":"query","description":"OPEN = awaiting a decision at either step.","schema":{"type":"string","enum":["PENDING","SUPERVISOR_APPROVED","APPROVED","REJECTED","CANCELLED","OPEN"]}},{"$ref":"#/components/parameters/employeeId"},{"$ref":"#/components/parameters/employeeNo"},{"name":"from","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-18"]}},{"name":"to","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-18"]}},{"$ref":"#/components/parameters/updatedSince"},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"}],"responses":{"200":{"description":"A page of leave requests","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LeaveRequest"}},"page":{"type":"integer","description":"1-based page number"},"pageSize":{"type":"integer"},"total":{"type":"integer","description":"Rows matching the filters, all pages"},"hasMore":{"type":"boolean","description":"Another page follows"}},"required":["data","page","pageSize","total","hasMore"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["leave:read"]}],"x-scope":"leave:read"},"post":{"operationId":"createLeaveRequest","tags":["Leave"],"summary":"Request leave for an employee","description":"Files a request as HR would on the employee's behalf: CloudPay counts the working days (work week and public holidays), checks the balance and overlapping requests, and refuses days of a closed pay period. The request then waits for approval in CloudPay. A full day, half a day, or a number of hours on one day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveRequestCreate"}}}},"responses":{"201":{"description":"The request, pending approval","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LeaveRequest"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationFailed"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"OVERLAP: the employee already has leave in that period","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"EMPLOYEE_NOT_ACTIVE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"$ref":"#/components/responses/PeriodLocked"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["leave:write"]}],"x-scope":"leave:write"}},"/leave/requests/{id}":{"get":{"operationId":"getLeaveRequest","tags":["Leave"],"summary":"Get a leave request","description":"Follow a request you filed until it is decided.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The leave request","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LeaveRequest"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["leave:read"]}],"x-scope":"leave:read"}},"/attendance/punches":{"get":{"operationId":"listPunches","tags":["Attendance"],"summary":"List punches","description":"One record per shift, with its clock-in and clock-out and the hours they make, for at most 31 days (default: the last 7). Days without a clock-in are left out. A day can have two records: an Office + WFH day has an office part and a work-from-home part (`place`, `splitDay`, `dayShare`).","parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-18"]}},{"name":"to","in":"query","schema":{"type":"string","format":"date","examples":["2026-10-18"]}},{"$ref":"#/components/parameters/employeeId"},{"$ref":"#/components/parameters/employeeNo"},{"$ref":"#/components/parameters/updatedSince"},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"}],"responses":{"200":{"description":"A page of attendance records","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AttendanceRecord"}},"page":{"type":"integer","description":"1-based page number"},"pageSize":{"type":"integer"},"total":{"type":"integer","description":"Rows matching the filters, all pages"},"hasMore":{"type":"boolean","description":"Another page follows"}},"required":["data","page","pageSize","total","hasMore"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["attendance:read"]}],"x-scope":"attendance:read"},"post":{"operationId":"createPunch","tags":["Attendance"],"summary":"Record a punch","description":"A clock-in or clock-out from a time clock, badge reader or another system, at most 7 days old. Send punches in the order they happened. Without `direction` the punch clocks out an employee who is clocked in and clocks in one who is not. The same punch sent again within 2 minutes — even after the shift has closed — is answered with `duplicate: true` and changes nothing; a clock-in that would overlap a recorded shift is refused with 409 OUT_OF_ORDER. Hours are worked out like a clock-out in the app (break, overtime, meal allowance).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PunchCreate"}}}},"responses":{"200":{"description":"The same punch was already recorded","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PunchResult"}},"required":["data"]}}}},"201":{"description":"The punch was recorded","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PunchResult"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationFailed"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"ALREADY_CLOCKED_IN (a clock-in while the employee is clocked in), NO_OPEN_SESSION (a clock-out with no clock-in before it), OUT_OF_ORDER (a clock-in inside a recorded shift, or before one that starts after it) or REGULARISED (the time falls on a record HR regularised, or a day HR marked absent: left as it is)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"EMPLOYEE_NOT_ACTIVE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"$ref":"#/components/responses/PeriodLocked"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["attendance:write"]}],"x-scope":"attendance:write"}},"/punches":{"post":{"operationId":"sendTerminalPunches","tags":["Time terminals"],"summary":"Send terminal punches","description":"One punch, or `{ \"punches\": [...] }` up to 200 (a terminal flushing its buffer), each naming the employee by `badgeNumber` (or `employeeNo` / `employeeId`) with its time — ISO 8601 with an offset, or the terminal's local time without one, read as Mauritius time. Without `direction`, CloudPay pairs the punches: one closes the open shift, else opens one; a lone punch nearer the end of the shift than its start, with nothing open, is left for HR (IGNORED, UNPAIRED). The same punch within 2 minutes, or a record id (`externalId`) sent before, is a DUPLICATE and changes nothing. Each result says APPLIED, DUPLICATE, IGNORED (REGULARISED, UNPAIRED) or REJECTED (UNKNOWN_BADGE, NO_ACCESS, EMPLOYEE_NOT_ACTIVE, PERIOD_CLOSED, OUT_OF_ORDER…), in the order sent.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/TerminalPunch"},{"type":"object","properties":{"punches":{"type":"array","items":{"$ref":"#/components/schemas/TerminalPunch"},"minItems":1,"maxItems":200}},"required":["punches"]}]}}}},"responses":{"200":{"description":"What became of each punch","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TerminalPunchResults"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/ValidationFailed"},"401":{"description":"DEVICE_KEY_REQUIRED, INVALID_DEVICE_KEY or DEVICE_REVOKED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"WRONG_DEVICE_KIND (a kiosk's key) or FEATURE_DISABLED (time terminals are off for your company)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"deviceKey":[]}],"x-device":true}},"/payroll/runs":{"get":{"operationId":"listPayrollRuns","tags":["Payroll"],"summary":"List payroll runs","description":"Run summaries, newest period first. Cancelled runs only when asked for with `status=CANCELLED`.","parameters":[{"name":"year","in":"query","description":"Pay year","schema":{"type":"integer"}},{"name":"month","in":"query","description":"Pay month (1–12): the month the period ends in","schema":{"type":"integer"}},{"name":"status","in":"query","schema":{"type":"string","enum":["DRAFT","PROCESSING","PROCESSED","APPROVED","PAID","CANCELLED"]}},{"name":"runType","in":"query","schema":{"type":"string","enum":["REGULAR","OFF_CYCLE","BONUS"]}},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"}],"responses":{"200":{"description":"A page of payroll runs","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PayrollRun"}},"page":{"type":"integer","description":"1-based page number"},"pageSize":{"type":"integer"},"total":{"type":"integer","description":"Rows matching the filters, all pages"},"hasMore":{"type":"boolean","description":"Another page follows"}},"required":["data","page","pageSize","total","hasMore"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["payroll:read"]}],"x-scope":"payroll:read"}},"/payroll/runs/{id}":{"get":{"operationId":"getPayrollRun","tags":["Payroll"],"summary":"Get a payroll run","description":"The run's summary and each employee's totals: gross, deductions, net and employer cost.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The run with its employees","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PayrollRunDetail"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiKey":["payroll:read"]}],"x-scope":"payroll:read"}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"cpk_<prefix>_<secret>","description":"An API key from Settings › Integrations › Developers."},"deviceKey":{"type":"http","scheme":"bearer","bearerFormat":"cpd_<prefix>_<secret>","description":"A time terminal's device key from Settings › Integrations › Time terminals. It only sends punches."}},"parameters":{"page":{"name":"page","in":"query","description":"Page number, from 1","schema":{"type":"integer","minimum":1,"maximum":1000000,"default":1}},"pageSize":{"name":"pageSize","in":"query","description":"Rows per page","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},"employeeId":{"name":"employeeId","in":"query","description":"One employee, by CloudPay id","schema":{"type":"string"}},"employeeNo":{"name":"employeeNo","in":"query","description":"One employee, by employee number","schema":{"type":"string"}},"updatedSince":{"name":"updatedSince","in":"query","description":"Only rows changed at or after this instant","schema":{"type":"string","format":"date-time"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests this key may make per minute","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current minute","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the current minute ends (Unix seconds)","schema":{"type":"integer"}}},"responses":{"Unauthorized":{"description":"API_KEY_REQUIRED, INVALID_API_KEY, API_KEY_REVOKED or API_KEY_EXPIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"INSUFFICIENT_SCOPE (the key lacks the scope; `requiredScope` names it) or FEATURE_DISABLED (the API or the module is off for your company)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationFailed":{"description":"VALIDATION_FAILED or a business rule (code says which)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PeriodLocked":{"description":"PERIOD_CLOSED (the days fall in a pay period payroll has closed) or PERIOD_CUTOFF (past the period's edit cutoff)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"RATE_LIMITED (the key's requests per minute) or TOO_MANY_FAILED_ATTEMPTS","headers":{"Retry-After":{"description":"Seconds to wait","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"What went wrong, for people"},"code":{"type":"string","description":"What went wrong, for programs"}},"required":["error","code"],"additionalProperties":true},"Employee":{"type":"object","properties":{"id":{"type":"string","description":"CloudPay id"},"employeeNo":{"type":"string","description":"Employee number, e.g. CP-0042"},"firstName":{"type":"string"},"lastName":{"type":"string"},"commonName":{"type":["string","null"],"description":"Usual name"},"email":{"type":["string","null"],"format":"email"},"phone":{"type":"string"},"status":{"type":"string","enum":["ACTIVE","ON_LEAVE","SUSPENDED","TERMINATED","RESIGNED"]},"employmentType":{"type":"string","enum":["FULL_TIME","PART_TIME","CONTRACT","TEMPORARY","PROBATION"]},"joinDate":{"type":"string","format":"date","examples":["2026-10-18"]},"endDate":{"type":["string","null"],"description":"Last day of employment","format":"date","examples":["2026-10-18"]},"probationEnd":{"type":["string","null"],"format":"date","examples":["2026-10-18"]},"supervisorId":{"type":["string","null"],"description":"Employee id of the direct supervisor"},"payGroupId":{"type":["string","null"]},"department":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"],"description":"Department"},"jobTitle":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"description":"Job title"},"location":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"description":"Work location"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"salary":{"$ref":"#/components/schemas/Salary"}},"required":["id","employeeNo","firstName","lastName","commonName","email","phone","status","employmentType","joinDate","endDate","probationEnd","supervisorId","payGroupId","department","jobTitle","location","createdAt","updatedAt"]},"Salary":{"type":"object","properties":{"type":{"type":"string","enum":["MONTHLY","HOURLY","DAILY"]},"currency":{"type":"string","const":"MUR"},"basicSalary":{"type":"number","description":"Monthly basic salary"},"hourlyRate":{"type":["number","null"]},"dailyRate":{"type":["number","null"]}},"required":["type","currency","basicSalary","hourlyRate","dailyRate"],"description":"Only with the payroll:read scope"},"LeaveType":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string","description":"e.g. AL, SL"},"name":{"type":"string"},"isPaid":{"type":"boolean"},"defaultDays":{"type":"integer","description":"Days per leave year when no entitlement rule applies"},"requiresProof":{"type":"boolean"},"allowHourly":{"type":"boolean","description":"May be taken by the hour"},"maxConsecutive":{"type":["integer","null"],"description":"Most working days in one request"},"carryForward":{"type":"boolean"}},"required":["id","code","name","isPaid","defaultDays","requiresProof","allowHourly","maxConsecutive","carryForward"]},"LeaveBalance":{"type":"object","properties":{"id":{"type":"string"},"employeeId":{"type":"string"},"employeeNo":{"type":"string"},"leaveType":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]},"year":{"type":"integer","description":"Leave year"},"validFrom":{"type":["string","null"],"format":"date","examples":["2026-10-18"]},"validTo":{"type":["string","null"],"format":"date","examples":["2026-10-18"]},"entitled":{"type":"number"},"carriedOver":{"type":"number"},"taken":{"type":"number"},"pending":{"type":"number","description":"Held by requests awaiting a decision"},"available":{"type":"number","description":"entitled + carriedOver − taken − pending"},"visibleToEmployee":{"type":"boolean"}},"required":["id","employeeId","employeeNo","leaveType","year","validFrom","validTo","entitled","carriedOver","taken","pending","available","visibleToEmployee"]},"LeaveRequest":{"type":"object","properties":{"id":{"type":"string"},"employeeId":{"type":"string"},"employeeNo":{"type":"string"},"leaveType":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]},"startDate":{"type":"string","format":"date","examples":["2026-10-18"]},"endDate":{"type":"string","format":"date","examples":["2026-10-18"]},"totalDays":{"type":"number","description":"Working days charged"},"isHalfDay":{"type":"boolean"},"halfDayPeriod":{"type":["string","null"],"enum":["MORNING","AFTERNOON"]},"hours":{"type":["number","null"],"description":"Hourly leave: hours off"},"startTime":{"type":["string","null"],"description":"HH:MM"},"endTime":{"type":["string","null"],"description":"HH:MM"},"reason":{"type":["string","null"]},"source":{"type":"string","enum":["FORM","ON_BEHALF","SICK_REPORT_CLOCK","SICK_REPORT_LEAVE","API"],"description":"Where it was filed"},"status":{"type":"string","enum":["PENDING","SUPERVISOR_APPROVED","APPROVED","REJECTED","CANCELLED"]},"rejectionReason":{"type":["string","null"]},"supervisorApprovedAt":{"type":["string","null"],"format":"date-time"},"decidedAt":{"type":["string","null"],"description":"When it was approved or rejected","format":"date-time"},"hasAttachment":{"type":"boolean","description":"Proof was attached in the app"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","employeeId","employeeNo","leaveType","startDate","endDate","totalDays","isHalfDay","halfDayPeriod","hours","startTime","endTime","reason","source","status","rejectionReason","supervisorApprovedAt","decidedAt","hasAttachment","createdAt","updatedAt"]},"LeaveRequestCreate":{"type":"object","properties":{"employeeId":{"type":"string","description":"Give employeeId or employeeNo"},"employeeNo":{"type":"string"},"leaveTypeId":{"type":"string","description":"Give leaveTypeId or leaveTypeCode"},"leaveTypeCode":{"type":"string"},"startDate":{"type":"string","format":"date","examples":["2026-10-18"]},"endDate":{"type":"string","format":"date","examples":["2026-10-18"]},"isHalfDay":{"type":"boolean","default":false},"halfDayPeriod":{"type":"string","enum":["MORNING","AFTERNOON"]},"hours":{"type":"number","description":"Hourly leave on one day: the hours off (or startTime / endTime)"},"startTime":{"type":"string","description":"HH:MM"},"endTime":{"type":"string","description":"HH:MM"},"reason":{"type":"string","maxLength":2000}},"required":["startDate","endDate"],"additionalProperties":false},"AttendanceRecord":{"type":"object","properties":{"id":{"type":"string"},"employeeId":{"type":"string"},"employeeNo":{"type":"string"},"date":{"type":"string","description":"The day the shift started","format":"date","examples":["2026-10-18"]},"status":{"type":"string","enum":["PRESENT","ABSENT","HALF_DAY","ON_LEAVE","PUBLIC_HOLIDAY","REST_DAY"]},"clockIn":{"type":["object","null"],"properties":{"time":{"type":"string","format":"date-time"},"method":{"type":["string","null"],"enum":["GPS","FINGERPRINT","MANUAL","QR_CODE","WFH"]},"status":{"type":["string","null"],"enum":["ON_TIME","LATE","EARLY"]}},"required":["time","method","status"]},"clockOut":{"type":["object","null"],"properties":{"time":{"type":"string","format":"date-time"},"method":{"type":["string","null"],"enum":["GPS","FINGERPRINT","MANUAL","QR_CODE","WFH"]}},"required":["time","method"]},"open":{"type":"boolean","description":"Clocked in, not out yet"},"totalHours":{"type":["number","null"]},"regularHours":{"type":["number","null"]},"overtimeHours":{"type":["number","null"]},"breakMinutes":{"type":["integer","null"]},"gpsVerified":{"type":"boolean"},"manualEntry":{"type":"boolean","description":"Entered or corrected by HR"},"location":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"description":"Work location"},"place":{"type":"string","enum":["office","home"],"description":"Where this record was worked: home for a work-from-home record (clock-in method WFH), else office"},"dayKey":{"type":"string","description":"The day the record belongs to (yyyy-mm-dd): group records by employee and dayKey to get days","format":"date","examples":["2026-10-18"]},"splitDay":{"type":"boolean","description":"The day has an office record and a work-from-home record (an Office + WFH day): two records, one day. The first part's hours are provisional until the other part starts or ends"},"dayShare":{"type":"number","description":"The share of a day this record stands for: 1, or 0.5 per place on an Office + WFH day (split between that place's records)"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","employeeId","employeeNo","date","status","clockIn","clockOut","open","totalHours","regularHours","overtimeHours","breakMinutes","gpsVerified","manualEntry","location","place","dayKey","splitDay","dayShare","createdAt","updatedAt"]},"PunchCreate":{"type":"object","properties":{"employeeId":{"type":"string","description":"Give employeeId or employeeNo"},"employeeNo":{"type":"string"},"timestamp":{"type":"string","description":"When the punch happened, with its offset (e.g. 2026-10-18T08:02:11+04:00)","format":"date-time"},"direction":{"type":"string","enum":["IN","OUT"],"description":"Omit to let CloudPay decide from the employee's open session"},"method":{"type":"string","enum":["FINGERPRINT","QR_CODE","MANUAL"],"description":"How the employee punched: FINGERPRINT, QR_CODE, or MANUAL for any other terminal"},"locationId":{"type":"string","description":"The site the terminal is at (default: the roster's, else the employee's)"},"note":{"type":"string","maxLength":500}},"required":["timestamp","method"],"additionalProperties":false},"PunchResult":{"type":"object","properties":{"direction":{"type":"string","enum":["IN","OUT"]},"duplicate":{"type":"boolean","description":"The same punch had already been recorded"},"attendance":{"$ref":"#/components/schemas/AttendanceRecord"}},"required":["direction","duplicate","attendance"]},"TerminalPunch":{"type":"object","properties":{"badgeNumber":{"type":"string","description":"The badge / card / user number the terminal knows the employee by (or give employeeNo or employeeId)"},"employeeNo":{"type":"string"},"employeeId":{"type":"string"},"timestamp":{"type":"string","description":"ISO 8601; without an offset it is read as Mauritius time (e.g. 2026-10-18 08:02:11)"},"direction":{"type":"string","enum":["IN","OUT"],"description":"Omit to let CloudPay pair the punches"},"externalId":{"type":"string","description":"The terminal's own record id: sent again, it is answered, not recorded twice","maxLength":100}},"required":["timestamp"],"additionalProperties":false},"TerminalPunchResults":{"type":"object","properties":{"device":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"summary":{"type":"object","properties":{"received":{"type":"integer"},"applied":{"type":"integer"},"duplicate":{"type":"integer"},"ignored":{"type":"integer"},"rejected":{"type":"integer"}},"required":["received","applied","duplicate","ignored","rejected"]},"results":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","description":"Position in the request"},"punchId":{"type":"string","description":"The punch in CloudPay's punch log"},"status":{"type":"string","enum":["APPLIED","DUPLICATE","IGNORED","REJECTED","PENDING"]},"code":{"type":["string","null"],"description":"Why it was not applied (UNKNOWN_BADGE, NO_ACCESS, REGULARISED, UNPAIRED, DUPLICATE…)"},"reason":{"type":["string","null"]},"direction":{"type":["string","null"],"enum":["IN","OUT"],"description":"What it was recorded as"},"attendanceId":{"type":["string","null"],"description":"The attendance record it opened or closed"},"employeeId":{"type":["string","null"]},"replay":{"type":"boolean","description":"The same externalId was sent before"}},"required":["index","punchId","status","code","reason","direction","attendanceId","employeeId","replay"]}}},"required":["device","summary","results"]},"PayrollRun":{"type":"object","properties":{"id":{"type":"string"},"month":{"type":"integer","description":"Pay month: the month the period ends in"},"year":{"type":"integer"},"periodStart":{"type":"string","format":"date","examples":["2026-10-18"]},"periodEnd":{"type":"string","format":"date","examples":["2026-10-18"]},"periodNo":{"type":"integer"},"payFrequency":{"type":"string","enum":["WEEKLY","FORTNIGHTLY","MONTHLY"]},"runType":{"type":"string","enum":["REGULAR","OFF_CYCLE","BONUS"]},"runNo":{"type":"integer"},"payGroup":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]},"status":{"type":"string","enum":["DRAFT","PROCESSING","PROCESSED","APPROVED","PAID","CANCELLED"]},"processedAt":{"type":["string","null"],"format":"date-time"},"approvedAt":{"type":["string","null"],"format":"date-time"},"periodClosedAt":{"type":["string","null"],"format":"date-time"},"currency":{"type":"string","const":"MUR"},"employeeCount":{"type":"integer"},"totals":{"type":"object","properties":{"gross":{"type":"number"},"deductions":{"type":"number"},"net":{"type":"number"},"employerCost":{"type":"number"}},"required":["gross","deductions","net","employerCost"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","month","year","periodStart","periodEnd","periodNo","payFrequency","runType","runNo","payGroup","status","processedAt","approvedAt","periodClosedAt","currency","employeeCount","totals","createdAt","updatedAt"]},"PayrollRunDetail":{"allOf":[{"$ref":"#/components/schemas/PayrollRun"},{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PayrollItem"}}},"required":["items"]}]},"PayrollItem":{"type":"object","properties":{"employeeId":{"type":"string"},"employeeNo":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"status":{"type":"string","enum":["DRAFT","CALCULATED","VALIDATED"],"description":"Payslip state in the payroll workbench"},"gross":{"type":"number"},"deductions":{"type":"object","properties":{"total":{"type":"number"},"paye":{"type":"number"},"csg":{"type":"number"},"nsf":{"type":"number"},"npf":{"type":"number"}},"required":["total","paye","csg","nsf","npf"]},"net":{"type":"number"},"employer":{"type":"object","properties":{"total":{"type":"number"},"csg":{"type":"number"},"nsf":{"type":"number"},"npf":{"type":"number"},"trainingLevy":{"type":"number"},"prgf":{"type":"number"}},"required":["total","csg","nsf","npf","trainingLevy","prgf"]},"work":{"type":"object","properties":{"daysWorked":{"type":"integer"},"regularHours":{"type":"number"},"overtimeHours":{"type":"number"},"leaveDays":{"type":"number"},"absentDays":{"type":"integer"}},"required":["daysWorked","regularHours","overtimeHours","leaveDays","absentDays"]}},"required":["employeeId","employeeNo","firstName","lastName","status","gross","deductions","net","employer","work"]}}}}