Occurrences overview
Every check template (frequency + type) generates occurrences— individual items a staff member completes. An occurrence has adue_at timestamp calculated from the tenant's timezone and the check's frequency slots.
Calendar slots
Each frequency maps to fixed local-time slots in the tenant's timezone (every location currently uses Europe/London):
| Frequency | Slots (local time) |
|---|---|
twice_daily | 00:00 and 12:00 every day |
daily | 00:00 every day |
twice_weekly | Monday 00:00 and Thursday 00:00 |
weekly | Monday 00:00 |
twice_monthly | 1st and 15th 00:00 |
monthly | 1st 00:00 |
quarterly | Jan/Apr/Jul/Oct 1st 00:00 |
Closed days generate no occurrences: daily andtwice_daily checks are not scheduled on days when the location is closed. Weekly and longer frequencies ignore opening days entirely —weekly, twice_weekly, twice_monthly,monthly, and quarterly checks keep their fixed calendar slots even if the location is closed that day.
Generation (lazy, on read)
Occurrences are generated when the list endpoint is called, not by a background cron. For each active check:
- If no occurrences exist: generates the next upcoming allowed slot.
- If occurrences exist: generates all slots between the latest
due_atand now, including the current open slot. Fordailyandtwice_dailychecks, closed days inside that window are skipped. - Catch-up is capped at the last 14 days of slots.
- The unique index on
(check_id, due_at)prevents duplicates.
States
Every occurrence is in one of three derived states (no status column):
| Status | Derived from | Meaning |
|---|---|---|
pending | checked_at IS NULL AND skipped_at IS NULL | Needs attention |
done | checked_at IS NOT NULL | Completed by a staff member |
skipped | skipped_at IS NOT NULL | Deliberately skipped (e.g. equipment offline) |
The API response also includes an overdue boolean — true whenstatus === "pending" and due_at is in the past.
Validation
Completion is validated server-side by check type:
| Type | Readings |
|---|---|
simple | No readings allowed |
cold_store | One reading per every active fridge, each with fridge_id |
food | ≥1 reading, each with valid food_item_id |
washer | No readings allowed |
Permissions
- Complete / Uncomplete — any tenant member
- Skip — owner or manager only
- Create / Replace / Delete templates — owner or manager only
Daily checklist (mobile app)
The home screen shows a Daily Hygiene card with a real-time progress bar and a task carousel. Swipe through pending occurrences — simple checks complete in one tap; cold_storeand food checks open a temperature-reading modal with per-unit sliders.
The Daily Checklist screen (sidebar) shows the full list grouped by status: Overdue / Pending / Done / Skipped. Tap to complete or record temperatures.
- Complete — any member; simple check = one tap; cold_store/food = slider modal with corrective-action note for out-of-range readings
- Undo — any member; reverts to pending (clears completion or skip)
- Skip — owner/manager only; requires reason note
API reference
GET /v1/tenants/{tenantCode}/check-data — list occurrences
POST /v1/tenants/{tenantCode}/check-data/{id}/complete — complete (with readings)
POST /v1/tenants/{tenantCode}/check-data/{id}/uncomplete — undo completion
POST /v1/tenants/{tenantCode}/check-data/{id}/skip — skip (admin only)All request and response bodies use snake_case keys. See theOpenAPI reference for full schema details.