Skip to content

Tenant API v1 — Attendance ​

Base path: /api/tenant/v1

Middleware: auth:tenant-api, tenant.user, not.suspended, verified, module:attendance, plus can:attendance.*.

Catalog version: 1.10.0.

Stats ​

GET /attendance-records/stats ​

Same filters as list (minus pagination/sort). Payload:

total, present, absent, half_day, remote, late, plus open presence counts checked_in_open, on_site_open, remote_open (today, checked in, not checked out).

Daily email digests ​

Scheduled (not HTTP): attendance:send-daily-reports every 5 minutes. Tenant settings (Attendance group):

KeyDefaultPurpose
attendance_late_report_enabledfalseSend late-today digest after local time
attendance_late_report_time09:30Workspace-local H:i gate
attendance_daily_report_enabledfalseSend yesterday digest after local time
attendance_daily_report_time08:00Workspace-local H:i gate

Recipients: non-suspended Owners / Admins get the full company digest; department managers (manager_id on an active department) get only employees in their managed departments. Spatie manager without department assignment does not receive digests. Requires attendance.view. Notification types attendance.late.digest and attendance.yesterday.digest (database + mail). Idempotent via daily_summary_deliveries.

Today / self check-in ​

MethodPathPermission
GET/attendance-records/todayLinked active employee + self-check enabled, or attendance.view
POST/attendance-records/check-inLinked active employee + self-check enabled (no attendance.create)
POST/attendance-records/check-outLinked active employee + self-check enabled (no attendance.update)

GET /attendance-records/today ​

Returns today’s record for the linked employee (or null) plus meta:

  • self_check_enabled, require_late_reason, timezone, today
  • thresholds.on_site / thresholds.remote — { start, grace_minutes, is_late_now }

POST /attendance-records/check-in ​

Body: work_mode (on_site|remote), optional reason_id / reason_notes, optional latitude / longitude.

Late on-site uses office start + grace; late remote uses remote start + grace. When late and attendance_require_late_reason is on, reason_id is required; Other requires reason_notes. Status is present/late (on-site) or late with work_mode=remote when remote is late; on-time remote stays remote. Server stamps check_in_ip; coordinates are best-effort.

POST /attendance-records/check-out ​

Body: optional reason_id / reason_notes, optional latitude / longitude. Check-out reason is optional; Other requires notes. Server stamps check_out_ip.

Attendance reasons ​

MethodPathPermission
GET/attendance-reasonsattendance.view
POST/attendance-reasonsattendance.update (admin/owner catalog)
GET/attendance-reasons/{attendanceReason}attendance.view
PUT/attendance-reasons/{attendanceReason}attendance.update (admin/owner)
DELETE/attendance-reasons/{attendanceReason}attendance.update (admin/owner)

Kinds: check_in_late, check_out. Each kind has a protected Other row (is_other).

Workspace holidays ​

MethodPathPermission
GET/attendance-holidaysattendance.view
POST/attendance-holidaysattendance.update (admin/owner catalog)
GET/attendance-holidays/{attendanceHoliday}attendance.view
PUT/attendance-holidays/{attendanceHoliday}attendance.update (admin/owner)
DELETE/attendance-holidays/{attendanceHoliday}attendance.update (admin/owner)

Body: date (required, unique per tenant), optional name. List filters: year, date_from, date_to, search. Holidays are never treated as Absent for matrix inference or payroll deduction.

Month matrix & export ​

MethodPathPermission
GET/attendance-records/matrixattendance.view
GET/attendance-records/exportattendance.export

Query params: month (YYYY-MM, required), optional search, department_id, employee_status (default active), and for export format (csv|xlsx, default csv).

Matrix JSON: month, timezone, days[] (date, label, is_today, is_work_day, is_holiday, holiday_name), holidays[] (id, date, name), employees[] (id, employee_number, full_name, job_title, departments[], status, days map of YYYY-MM-DD → { status, record_id, source, holiday_name }). source is record | inferred | holiday. Missing day keys mean off day / future (not inferred). Past/today work days with no record and not a holiday get status=absent, source=inferred. Visibility matches list (AttendanceRecordPolicy::visibleEmployeeIds).

Export: Same filters as matrix. Sheet starts with a legend row (P/A/L/R/HD/H), then header columns ID, Employee number, Name, Job title, Department, Employee status, then one column per calendar day. Filename attendance_MM_YYYY.csv or .xlsx.

Attendance records CRUD ​

MethodPathPermission
GET/attendance-recordsattendance.view
GET/attendance-records/statsattendance.view
GET/attendance-records/matrixattendance.view
GET/attendance-records/exportattendance.export
POST/attendance-recordsattendance.create
GET/attendance-records/{attendanceRecord}attendance.view
GET/attendance-records/{attendanceRecord}/timelineattendance.view
PUT/attendance-records/{attendanceRecord}attendance.update
DELETE/attendance-records/{attendanceRecord}attendance.delete
POST/attendance-records/{attendanceRecord}/restoreattendance.restore
DELETE/attendance-records/{attendanceRecord}/forceattendance.force.delete

POST /attendance-records ​

Body: employee_id, date (required; unique per employee), required change_reason (min 5), optional check_in / check_out (HH:MM or HH:MM:SS), status (present|absent|half_day|remote|late, default present), notes, optional latitude / longitude. Server stamps check-in/out IP when times are set (client IPs are ignored).

Date rules: date other than workspace today requires attendance.backdate (or workspace owner / superadmin). Actors without that privilege receive a date validation error.

Login side-effect (optional): when attendance_auto_check_in_on_login is true, successful POST /auth/login, passkey login, or 2FA challenge may upsert today’s on-site check-in for the user’s active linked employee (optional latitude / longitude on those payloads). Default is false — staff must use check-in. If the login would be late and attendance_require_late_reason is true, auto check-in is skipped (no reason can be collected on login).

PUT /attendance-records/{attendanceRecord} ​

Partial update of the same fields (plus work mode / reason fields for managers). change_reason is required (min 5). Optional latitude / longitude re-stamp location when check-in/out times change. Changing date to a day other than today requires attendance.backdate (or owner); leaving a past date unchanged does not.

GET /attendance-records/{attendanceRecord}/timeline ​

Newest-first activity rows: type (created|updated|checked_in|checked_out|deleted|restored), description, change_reason, properties (before/after diffs), ip, coordinates, actor, created_at.

Official documentation for the EloSync SaaS Platform.