Skip to content
HRTEQDocs
Documentation API

DocsAutomation

Notifications, email and scheduled jobs

The twelve 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#

Twelve templates are seeded, referenced by code from automation rules and from write paths. Each is editable per organization.

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.
PDOS_SCHEDULEPDOS attendance is being arranged.
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?