Skip to content
HRTEQ

DocsAutomation

Notifications, email and scheduled jobs

Selected email templates, in-app notifications, staff messaging, and the five nightly sweeps that keep expiry, retention and stalled work visible.

Three separate channels carry information out of the pipeline: templated email to applicants, in-app notifications to staff, and staff-to-staff chat. Behind them a background worker runs five scheduled sweeps that keep expiry, retention and stalled work from going unnoticed.

Email templates#

Email templates are referenced by code from automation rules and from write paths. Each is editable per organization. The table below shows selected examples.

Selected email template examples.
CodeSent when
APPLICATION_RECEIVEDAn application is created. The default acknowledgement.
PORTAL_INVITEA portal account is offered to an applicant or principal contact.
SCREENING_INVITEAn applicant is asked in for agency screening.
INTERVIEW_SCHEDULEDAn interview is booked.
OFFER_EXTENDEDA job offer is made.
MEDICAL_SCHEDULEOn entering the Medical stage, alongside the Liaison Officer task.
DOCUMENT_REQUESTA document is being chased.
DOCUMENT_EXPIRINGA verified document is 30 days from expiry.
DEPARTURE_DETAILSThree days before expected departure.
BALANCE_REMINDERAn outstanding worker balance is being chased.
REJECTED_POLITEAn application is declined.

Sending is queued rather than synchronous: the write path enqueues, and the worker delivers. Each attempt produces an EmailMessage row with a status.

EmailStatusMeaning
QUEUEDAccepted, not yet sent.
SENTHanded to the provider. Writes email.sent to the audit trail.
FAILEDDelivery failed.
BOUNCEDThe provider reported a bounce.
SUPPRESSEDNot sent because the address is on the unsubscribe list.

Unsubscribes are recorded and honoured. Marketing campaigns are a separate object — EmailCampaign with CampaignRecipient rows and a status of DRAFT, SENDING, SENT or CANCELLED — and dispatching one writes campaign.sent.

In-app notifications#

Notifications are per-user, with a type, a title, an optional body and a link to the thing they concern. The API returns the 50 most recent plus an unread count, and marking one read is a separate call.

The NOTIFY automation action is what creates most of them, optionally scoped to a role so an entire queue is told at once. See Automation rules.

Staff messaging#

Chat is real-time over Socket.IO, sharing the same HTTP server as the API. A client obtains a one-shot handshake ticket and connects with it, rather than sending the session cookie over the socket.

ConversationTypeBetween
STAFF_DMTwo staff members. This is what the Messages screen creates.
STAFF_GROUPSeveral staff members.
APPLICANT_THREADStaff and an applicant, through the applicant portal.
EMPLOYER_THREADStaff and a principal contact, through the employer portal.

Message bodies are between 1 and 4000 characters. Attachments become FileObject records and are subject to the same access rules as documents — a participant in the conversation may open them, and nobody else. See Documents.

The Messages screen does not open a socket yet. The socket infrastructure and the ticket endpoint exist, and the portal messenger uses them. The staff Messages screen currently refetches instead of subscribing, so a new message appears on the next fetch rather than instantly.

The five scheduled sweeps#

A separate worker process consumes the job queue and owns every scheduled sweep. It is deliberately not the web server, so a long résumé parse or a two-thousand-recipient campaign cannot block HTTP requests. Repeatable jobs are keyed, so a redeploy does not duplicate them.

All times are Asia/Manila by default, following the organization timezone setting.
TimeJobWhat it does
01:00 dailydocument.expiry.scanFlips verified documents whose expiry has passed to EXPIRED, and feeds the DOCUMENT_EXPIRING trigger for documents approaching their date.
01:15 dailyjoborder.expiry.scanMoves job orders past validUntil to EXPIRED.
01:30 dailyautomation.cronEvaluates the date-driven triggers: DATE_APPROACHING and CHECKLIST_OVERDUE, including the 21-day stale-application nudge.
01:45 dailyretention.scanFinds applicants past their retention date with no applications and raises a review task. Deliberately does not delete. See Data privacy.
08:00 weekdaysnotification.digestSends each active organization a morning digest of overdue tasks, linking into the task queue.

Alongside the crons the worker handles on-demand jobs: resume.parse for résumé extraction, email.send for transactional delivery, and campaign.send for bulk marketing.

The timezone setting is load-bearing. Every sweep runs in the organization timezone, defaulting to Asia/Manila. Set it wrongly and your expiry scans, your retention review and your morning digest all shift — the digest in particular stops arriving in the morning. Check it during setup. See Quickstart.

Why expiry is a sweep and not a query#

The gate already refuses a document whose expiry date has passed, so in principle a nightly status change is redundant. It exists for two reasons: the status an officer sees in the documents queue should say EXPIRED rather than VERIFIED, and something has to notice the day a document crosses a warning threshold in order to fire the 30-day and 180-day rules. A sweep does both, once, at a time when nobody is working.

Was this page helpful?