Skip to content

Roles and permissions

Crow HR checks every API request on the server against one permission matrix before it reads any data. Hiding a button in the web app is cosmetic; the server decides. The table below is generated from that matrix (packages/core/src/access/matrix.ts) when the docs are built, so it is always the version the code enforces.

  • Owner, HR admin and payroll admin are granted by an owner. One person can hold several.
  • Manager applies automatically while the member’s employee record has at least one active direct report. Their team is everyone whose current reporting chain leads to them, at any depth, but not themselves.
  • Employee applies automatically when the member is linked to an employee record.

A member’s rights are the union of their roles. See members and roles for how roles are granted.

70 permissions in 32 areas. Cells: All = every record in the organisation, Team = the member's direct and indirect reports (not themselves), Own = their own records, Assigned = only items they are the assigned approver for, and a dash = denied.

Permission matrix: scope granted to each role for each permission
PermissionOwnerHR adminPayroll adminManagerEmployee
Security settings (sign-in methods, SSO, MFA policy, timeouts)
security_settings:readAll————
security_settings:updateAll————
Members and invitations
members:readAllAllAll——
members:create
hr_admin may only invite members with no assigned roles.
AllAll———
members:update
hr_admin may not grant or revoke roles.
AllAll———
Role grants
member_roles:grantAll————
Company profile and structure
org_structure:readAllAllAllAllAll
org_structure:createAllAll———
org_structure:updateAllAll———
org_structure:archiveAllAll———
Employee directory
directory:readAllAllAllAllAll
Employment records and lifecycle
employment:readAllAllAllTeamOwn
employment:createAllAll———
employment:updateAllAll———
Personal details
personal_details:readAllAllAll—Own
personal_details:update
Employees update via change request where required.
AllAll——Own
Emergency contacts
emergency_contacts:readAllAll—TeamOwn
emergency_contacts:updateAllAll——Own
Work passes
work_pass:readAllAllAll—Own
work_pass:createAllAll———
work_pass:updateAllAll———
Compensation
compensation:readAll—All—Own
compensation:create
Nobody changes their own compensation directly.
All—All——
compensation:update
Nobody changes their own compensation directly.
All—All——
Bank details
bank_details:readAll—All—Own
bank_details:update
Own bank details change only via change request.
All—All—Own
Payroll runs and statutory files
payroll:readAll—All——
payroll:createAll—All——
payroll:updateAll—All——
payroll:finaliseAll—All——
Payslips and tax forms
payslips:read
Employees see released ones only.
All—All—Own
Leave
leave:readAllAllAllTeamOwn
leave:create
owner and hr_admin create on behalf of employees.
AllAll——Own
leave:update
Nobody adjusts their own balance.
AllAll———
Government-paid leave report
leave_claims:readAllAllAll——
Claims
claims:readAllAllAllTeamOwn
claims:create
owner and hr_admin create on behalf of employees.
AllAll——Own
claims:payAll—All——
Attendance, overtime and timesheets
attendance:readAllAllAllTeamOwn
attendance:create
owner and hr_admin create on behalf of employees.
AllAll——Own
attendance:update
Nobody corrects their own timesheet.
AllAll—Team—
Approval tasks
approval_tasks:decideAssignedAssignedAssignedAssignedAssigned
approval_tasks:reassign
hr_admin may not reassign payroll_run tasks.
AllAll———
Out-of-office cover for others
approval_delegations:manageAllAll———
Email delivery status
notification_delivery:readAll————
Rosters and shifts
rosters:read
Employees also see the published roster of their own location.
AllAll—TeamOwn
rosters:createAllAll—Team—
rosters:updateAllAll—Team—
rosters:publishAllAll—Team—
Shift templates
shift_templates:updateAllAll———
Shift swaps and open-shift pickup
shift_swaps:create
Only for the employee's own published shifts.
————Own
Documents
documents:read
payroll_admin: payroll-visible categories; manager: manager-visible categories, never medical; employee: employee-visible categories.
AllAllAllTeamOwn
documents:create
Employees upload to employee-uploadable categories only.
AllAll——Own
documents:updateAllAll———
documents:deleteAllAll———
Approval policies, leave and claim types
policies:readAllAll———
policies:updateAllAll———
Claim types
claim_types:readAllAllAll——
People reports
reports_people:read
Headcount and turnover.
AllAll———
reports_people:exportAllAll———
Leave reports
reports_leave:readAllAll—Team—
reports_leave:exportAllAll—Team—
Attendance reports
reports_attendance:readAllAll—Team—
reports_attendance:exportAllAll—Team—
Claims reports
reports_claims:readAllAllAll——
reports_claims:exportAllAllAll——
Payroll cost reports
reports_payroll_cost:readAll—All——
reports_payroll_cost:exportAll—All——
Audit log
audit_log:read
hr_admin: non-security, non-payroll events; payroll_admin: payroll, compensation and bank events.
AllAllAll——
audit_log:exportAllAllAll——
  • Records outside your scope look missing. A record of another organisation, or outside your team or own records, answers 404, so its existence is never revealed. A permission you do not have at all answers 403.
  • Sensitive fields carry a class: salary, nric, bank or medical. You receive a sensitive field only if the matrix gives you its resource (compensation, personal details, bank details). medical is further limited to owners, HR admins, the employee and the approver assigned to that item. Fields you may not read are left out and listed in redactedFields. Exports apply the same rules.
  • NRIC/FIN and bank numbers are masked; revealing one needs a recent sign-in, is limited to 30 per hour and is audited.
  • Segregation of duties: nobody changes their own compensation or bank details directly, whatever their role, and nobody decides their own approval request, except an owner when no one else is eligible (recorded as self-approved).
  • Former members keep read-only access to their own released payslips and employee-visible documents.
  • Install-level information (Settings → System) is visible to owners only.

Every resource in the matrix belongs to a built module and is enforced by the endpoints of that module.

Managers see leave status and employee numbers on rosters only for their reports. They can publish roster changes only for their reports; HR must publish changes affecting other teams. Children’s records require personal-details access; managers reviewing leave receive eligibility information without the child’s identity.