When does each scheduled action fire?
A reference for staff who need to predict — or explain to a client — exactly when an automated action will happen. All times shown in the client's timezone unless noted; the cron jobs themselves run in UTC and translate per-client.
Payroll
| Action | Fires when | Triggered by | Recipients |
|---|---|---|---|
| Cycle initiation | The client's local 09:00 on its defaultScheduleDay of the month, retried hourly for 48h | payroll.cycle-initiate hourly cron | The month's request, by email to each payroll sender; sent unless automatic messages are off (platform MSG_AUTO_REPLY_ENABLED=false, or the company's switch on Settings → Payroll), otherwise a staff task |
| Day-3 / 5 / 7 reminder | Day 3, 5 and 7 of waiting on the client, from 10:00 the company's own time (Singapore when none is on file) | check-pending-submissions, hourly (0 * * * *) | Intake: each payroll sender by email. A question: the thread it was asked on. A revision: the approver |
| Closing question (partial intake) | Day 3 when the client has sent something and has not been asked "Is anything else coming?" | Same hourly sweep | Whoever of the payroll senders wrote to us last, on that thread |
| Approval request | When staff press Send to client on the approval draft — nothing sends it by itself | Staff, from the Work page | The round's approver, by email on their thread for the month |
| Approval reminder | Every 2 days after the approval request was sent, until the pay-by date (2 / 4 / 6 when none is on record); from 10:00 the company's own time | check-pending-approvals, hourly (15 * * * *) | The approver the request went to, with a fresh approval link and the date we need approval by |
NOTE
Reminders before the due day (since 29 Oct 2026). A company can instead have its intake reminders counted back from its Inputs due by day: one to three of "7 days before" … "on the due day", chosen in onboarding (Configuration step) or on Settings → Payroll → Client reminders. Each goes at 10:00 the company's time on its date, only while nothing (or only part) has come; a reminder on or before the day the request went is skipped, and a sweep that missed a day sends only the latest one reached (ledger key D{k}, k days before). The day after the due day the reminders stop and the payroll executive gets the follow-up task (the same escalation as day 7). A month whose request went on or after its due day, and a company that chose no days, keep days 3 / 5 / 7. Reminders about a question or a revision always keep days 3 / 5 / 7 after the ask.
Every reminder above is an automatic message: with automatic messages off (the platform's
MSG_AUTO_REPLY_ENABLED=false, or the company's switch on Settings → Payroll; both on by default since 26 Oct 2026) it is not sent, and staff get a task saying it is due. The intake, question and revision reminders can then be sent from the Client reminder section of the month's Work page. A month's day-3/5/7 window is recorded once, so a reminder that falls due overnight goes on the first hourly run at or after 10:00 local and never twice.
NOTE
"Local 09:00" follows the IANA timezone on the client record. A client at Asia/Singapore initiates at SGT 09:00 regardless of UTC drift. If you change the client's timezone, the next cycle uses the new zone — already-scheduled cycles don't move.
IMPORTANT
A schedule day past the end of a short month runs on the last day instead (31 → 28/29 Feb). The cron re-checks every hour for 48 hours, so a worker restart at 09:00 doesn't cost a client its month, and it will not open a month whose previous cycle is still open — it waits for that one to close.
Bookkeeping
| Action | Fires when | Triggered by |
|---|---|---|
| Folder sync (Drive / SharePoint / Dropbox) | Configurable per channel; default hourly | bookkeeping.folder-sync cron |
| Document classify-and-draft | Within ~30 seconds of a document landing | Pg-boss queue, runs as the OCR pipeline finishes |
| Reconciliation matcher | Triggered by you, runs immediately | Synchronous from Run matcher click |
| Client portal dispatch | Triggered by you, sends within ~30 seconds | Synchronous from Dispatch unreconciled click |
Cross-cutting platform jobs
| Job | Cadence | Effect |
|---|---|---|
| Outbox dispatcher | Every 10 seconds | Drains the outbox table, sends emails / fires webhooks |
| OCR processing | On-demand per file | Vision + Claude pipeline, typically <30 s per page |
| Retention purge | 03:00 UTC on the 1st of the month | Moves cycles past the retention window from CLOSED to ARCHIVED, deletes raw uploads per the retention policy |
Token lifetimes
| Token type | TTL | Refreshed by |
|---|---|---|
| Approval request magic link | 48 hours | Each approval reminder carries a fresh link |
| Bookkeeping client portal link | 48 hours | Re-dispatch from the reconciliation run |
| Staff session cookie | Per session policy (see SLO) | Re-login |
How to figure out "when will the next email fire for this client?"
- Open the month's Work page.
- The Client reminder section says when the next reminder falls due ("Next reminder falls due … (day N of waiting)") and what it is counted from.
- It goes on the first hourly run at or after 10:00 in the company's own time that day. Example: a month opened on the 1st that is still waiting gets its day-5 reminder on the 6th, from 10:00 local.
To see what the client has already been told for the month, GET /ops/cycles/:cycleId/client-notices lists every month-level message that went, newest first, with the words each person was sent.
If the client says "I haven't received anything", see Verify an email was sent.
Related
- Verify an email was sent
- Why isn't my cycle showing?
- Notifications catalog — every email the platform can send