Skip to content

Document expiry alerts

A daily sweep looks at every document with an expiry date and raises an alert when it falls inside 30 days, inside 7 days, or has already passed. Alerts go to the organisation's Admins and Managers and to the document's owner. They repeat every day the document sits in a window, and a document with no expiry date is never chased.

What has to be true before anything is chased?

The document needs an expiry date. The sweep selects on that column. A licence uploaded without one is invisible to it, permanently, and nothing on the Documents tab will tell you that — the row simply carries no expiry badge.

The document must not be archived. Archived documents are excluded from the sweep, which is the intended way to stop a superseded licence from alerting.

The document must belong to an organisation. Agency officers' documents are alerted through the separate agency compliance sweep, not this one, and are skipped here.

The scheduled job must actually be running. The sweep is an API route, not a background timer inside the app. It fires because something calls it once a day with the shared cron secret — in this repository, a GitHub Actions workflow that runs at 03:15 UTC. A deployment where that workflow is disabled, or where the secret and the app URL are not configured, produces no alerts at all and no error anybody sees, because the step is deliberately allowed to fail without failing the run.

How do I make sure a document gets chased?

  1. Upload the document with an Expiry date filled in. See Upload employee documents.
  2. If the date was missed at upload, open the row and set it — editing the expiry of an already-verified document also re-copies the new date onto the roster's own licence expiry field.
  3. Check the badge on the row. Anything inside 30 days shows a coloured Expires in Nd badge, and the header above the sections counts them as · N expiring soon.
  4. When the replacement arrives, upload it as a new document and archive the old one. Archiving stops the alerts.

What are the three windows?

The sweep runs once a day and puts each eligible document into exactly one of three states, based on today's date.

State When Title on the alert Priority
30 days Expiry is more than 7 days away but within 30 Document expires in ≤ 30 days Normal
7 days Expiry is today or within 7 days Document expires in ≤ 7 days High
Expired Expiry is before today Document expired High

The message names the document, its type, whose it is and the date: for example SIA Licence (licence.pdf) for Kirsty Fraser expires 2026-09-30. Clicking it opens that person's record.

The sweep only looks one day into the past. A document that expired last month has already passed out of the selection window and stops being alerted — the alert marks the moment of expiry, it is not a standing report of everything currently out of date. For that, use the badges on the Documents tab, which have no lower bound and read Expired Nd ago.

Each run is capped at 1000 documents, taken in expiry order so the most urgent go first. Anything past the cap waits for tomorrow's run.

Who gets told?

Every active Admin and Manager in the organisation, plus the owner of the document. Control is deliberately not on the list, even though Control can see documents. Managers are on this list, which is worth noting because the missed check-call escalation uses a different set.

Delivery is three things at once, and only the first is guaranteed:

  • An in-app notification, visible in the bell to exactly the people named above.
  • A live push over the socket connection to anyone with the app open.
  • One queued email to the organisation's contact address — not one per recipient. That job goes through the Redis-backed notification queue, so on a deployment without Redis, or without the queue worker running, the email is the part that quietly does not arrive.

How often does the same document alert?

Once per day, every day, for as long as it stays inside a window.

This is worth being precise about, because the route's own comments claim it de-duplicates so that a document alerts only once per window. It does not: the bulk notification path that the sweep uses writes its rows directly and never reaches the de-duplication check that the single-notification path performs. In practice a licence uploaded 30 days out produces an alert every morning for over three weeks, changing title as it crosses into the 7-day window, and once more the day after it expires.

If that volume is a problem, the lever is the expiry date and the archive button, not a setting — there is no per-organisation control over the thresholds, the recipients or the frequency.

What do the badges on the Documents tab mean?

The badges are computed in your browser when the tab loads, from today's date. They are independent of the sweep, so they are correct even on a deployment where the scheduled job never runs.

Badge Condition
Expired Nd ago The expiry date has passed
Expires in Nd (red) 7 days or fewer remaining
Expires in Nd (amber) 8 to 30 days remaining
Expires 30 Sep 2026 (grey) More than 30 days remaining
No badge No expiry date recorded

If it goes wrong

What you see What it means
No alerts at all, ever The daily job is not being called, or the cron secret and app URL are not configured for it. Nothing in the interface reports this.
Alerts in the bell but no email The email leg goes through the Redis queue. Check the queue worker is up and that the organisation has a contact address on file.
A Control user says they never get these Correct. Control can read documents but is not on the alert list.
A document you know is expiring is not alerting Either it has no expiry date, it has been archived, or it belongs to an agency officer rather than an employee.
The same document alerts every morning Expected. See above — it repeats daily while it is inside a window.
An expired document stopped alerting Also expected. The sweep only reaches one day back; the row's own Expired badge is the standing record.

Last updated 2026-08-13