What this page is for. The central reference data is not one feed — it is four sources with different
cadences, different delivery mechanisms and different owners, and two of them need a human action before they
can work at all. This page is the operating schedule: for each source, what arrives, when, by what route, who or
what acts, and what must be true afterwards. It is the answer to "what has to happen, in what order, and how often,
for the app's reimbursement data to stay correct".Read it with Central Reference Data — the plan,
decisions, licences (§5.11) and the evidence (§11–§14) — and with the two build specs:
Backend Status View and
Pharmacy View.Status 2026-09-26. Phases 1 and 2 of the plan are live and verified (the store, the loader, the release
routine and its health surface), and so is phase 4 — the Drug Tariff: release2026-09published
unattended (9.85 / 20 / 5 % and 152p), verifiers 49/49 and 16/16. D11 was answered as
fetch-and-parse. Phase 5 — price concessions — is decided and authorised for build
(spec): the data arrives as the CPE email
alert stream into a dedicated mailbox, parsed and stored centrally and admin-side only, the display gate
closed under the present licence position (D10 — no consent being sought).
| Source | What it carries | Cadence | How it arrives | Who or what acts | State today |
|---|---|---|---|---|---|
| dm+d (NHSBSA, TRUD item 24) | the medicines and devices dictionary, its pack structure, the AMPP→GTIN mapping — and the NHS Drug Tariff prices in its own block | weekly | streamed — TRUD REST API pull (/trud/api/v1/keys/{key}/items/24/releases?latest), which also carries digital signatures for the verify step |
the scheduled pull (Celery beat), then the backend upload screen's verify → preview → confirm → publish | held release 3170926 imported by hand (21/09/2026) and live; the API pull still aborts not_configured — the TRUD account is the one missing thing (D12) |
| The Drug Tariff (NHSBSA documents) | the deduction rates (9.85% appliances / 20% generics / 5% brands), the Single Activity Fee (152p), the drugs for which discount is not deducted, the appliance scale, consumables | monthly | fetched documents — the full edition (6.25 MB, 1,296 pages for September 2026), text layer clean, no OCR | the daily 03:45 task, then an unattended publish when the validation gate passes | live and verified 2026-09-26 — release 2026-09, verifiers 49/49 and 16/16 |
| Price concessions (CPE) | the month's concession prices, which override the tariff for the packs concerned and move the line to discount-not-deducted for the month | monthly, and ad hoc within the month — 7 updates in August 2026, 6 in September by the 25th, plus the rolled-over list on the first working day | email alert stream — CPE's mailing list, the values in the message body, no attachment; fetched by IMAP from a dedicated mailbox (a human upload of a message is the fallback) | the 15-minute poll, then the arithmetic gate | decided and authorised for build (spec); nothing in the store yet. Client publication is closed under the present licence position (D10 — no consent being sought) |
| Supplier price files (the pharmacy's own wholesalers) | the pharmacy's commercial cost prices | at the pharmacy's choosing — weekly or monthly in practice | manual CSV upload by the client tenant | the client's own staff (v1 has no stored credentials and no automated pull, by design) | live — pricing stages 1–4 built, deployed and verified |
| Open Data Portal (EPD, PCA, dispensing) | activity, spend and the zero-discount/standard-rate split | — | — | — | not a source (D9) — a future accuracy cross-check for the reimbursement guardrail |
The one-line summary: dm+d is streamed and blocked only on an account; the Drug Tariff is monthly documents
and blocked on a format decision; concessions are monthly, table-shaped and manual, blocked on consent and on
there being nowhere to put them; the client's supplier prices are manual by design.
⚠️ The table above is out of date — see §9 (2026-10-01). The store now carries five source rows, and two of
them are not in the table: the NHSBSA notices feed (drug_tariff_news) and the MHRA WDA register
(mhra_wda, the newest, and the first source with a daily cadence). Neither is a missing-anything problem — the
page simply predates them.
3170926 is 21/09/2026.?latest), then downloads it. Eight XML members, ~228 MB.reference_data_check passes — the manifest integrity re-proved, closure checksNOT IN subqueries over ~187k rows that cannot use the index, so they run for tens of minutes;from-hermespipefail).2026-08).pypdf reads the text layer with no OCR; the parameters are a handful of numbers and oneThe order matters, because the tariff and the concessions both amend prices and every publish generates the label
that clients see.
reference_data_check → status view green.| Action | Why it is human | State |
|---|---|---|
| TRUD account, subscribed to item 24, licence accepted, key copied to the platform secret store (D12) | TRUD has no self-service API credential — the key is derived from the account's email and password | outstanding |
| CPE written consent (D10) | CPE's terms reserve re-publication to PSNC | not being pursued — decision 2026-09-26; the consequence is that concessions stay admin-side |
| The CPE concession mailbox — created, subscribed to CPE's alerts, credentials into the app host's environment (phase 5 §3.1) | a provider-side mailbox and a mailing-list subscription cannot be created by code | outstanding |
| The monthly document check — confirming the month's tariff has actually been published | publication dates move (the September 2026 edition was uploaded in August) | not yet assigned |
All three central sources share one scheduler and one store. The routine runs on Celery beat inside the platform
application and the pattern is check often, act once — which matters because neither publisher keeps to a fixed day:
| Task (as scheduled today) | When | Behaviour |
|---|---|---|
reference_data.tasks.pull_latest_dmd_release |
daily, 03:15 | imports only if TRUD has a newer release than the live one; otherwise a no-op |
reference_data.tasks.fetch_drug_tariff |
daily, 03:45 | the month's edition if it is not already live; parses, gates and publishes unattended |
reference_data.tasks.poll_concession_mailbox |
every 15 minutes | reads unseen CPE messages, gates them and applies each to the month's release (phase 5) |
reference_data.tasks.check_reference_health |
daily, 06:00 | freshness, key status and overdue evaluation, then the alert mail |
reference_data.tasks.purge_reference_releases |
weekly | keeps the live and previous release per source, purges older rows |
Which of the sources can therefore be automatic:
Net effect on the health view: after this work all three central sources report a real routine and a real cadence
— dm+d and the tariff automated, concessions as an email stream — and only the consented one reads as awaiting
consent.
1. The store's sources, as they actually stand (read from the central database on 2026-10-01). This page was written
when there were four central sources and is now behind by two:
| Source code | Cadence / routine | Releases held | Last attempt |
|---|---|---|---|
dmd |
weekly / trud_pull |
1 (imported by hand) | not_configured — still the TRUD account (D12) |
drug_tariff |
monthly / tariff_fetch |
2 | success |
drug_tariff_news |
monthly / nhsbsa_notices |
1 | already_held |
mhra_wda |
daily / wda_fetch |
1 | already_held — new 2026-10-01 |
price_concessions |
monthly / cpe_email |
1 | already_held |
The two the §1 table omits — drug_tariff_news (the NHSBSA notices feed) and mhra_wda (the MHRA wholesale
dealer's authorisation register, spec,
daily check) — should be added to
§1 whenever this page is next revised; neither changes the one-line summary, except that mhra_wda is the store's
first daily cadence and its register has no publication schedule at all.
2. Matthew's steer, recorded verbatim (2026-10-01): "Given we now have other data feeds now with NHSBSA, Trud and
CPE concessions, we will need a central place that checks these feeds have happened as and when required." He will
open it as a dedicated topic of its own when the pharmacy app is close to completion, and it is backend work.
What that means for this page, recorded so it is not re-derived:
reference_data.tasks.check_reference_health/infrastructure/reference-data/ view already renders a row per source with a status badge, age, next expectedwda_fetch) last checked / next check due.GRACE_DAYS per cadence, and mhra_wda special-cased to ask about the check rather than a release); the otherhealth.notify() on a failed or held attempt, against §5 of its own spec) — and that is a decision to take on the