# Changelog

User-visible changes to the Verzi Healthcare Data API and dashboard. Format follows [Keep a Changelog](https://keepachangelog.com/); dates are ISO-8601 UTC.

The full commit history is at [github.com/verzihealth/cms-star-ratings](https://github.com/verzihealth/cms-star-ratings). Anything not user-visible (refactors, test-only changes, internal tooling) is deliberately omitted from this log.

---

## Unreleased

### Added
- `GET /vbc/acos/{aco_id}/clinicians` and `GET /vbc/reach/{entity_id}/clinicians` list the clinicians Verzi links to an ACO, one row per NPI, with the best link confidence, every match method and the performance years. Filter by `min_confidence`, `year` and `match_method`; page with `limit` and `offset`.
- **The public code check says whether the HCPCS code was valid on the date of service.** `/check/{hcpcs}` and `/v1/check/{hcpcs}` gain a `code_validity` block with the code warrant's answer (#414). `status` is `valid`, `not_valid` or `unknown`. `reason` names the cause, for example `terminated`, `not_yet_effective` or `no_file_in_force`. `cms_fields` quotes CMS's `ADD_DT`, `EFCTV_DT`, `TERM_DT` and `ACTN_CD` as the HCPCS file states them. `file` cites that HCPCS file: its quarter and sha256. When the file cannot be read, the block says `unknown` with reason `source_unavailable`. The page shows a "Code valid on the date of service" section with these labels and values only.
- **CMS's per-provider totals beside the detail on `/provider/{npi}`.** The `prescribing` and `utilization` blocks gain `cms_summary`: CMS's own totals per data year from "Medicare Part D Prescribers - by Provider" (claims, drug cost, beneficiaries) and "Medicare Physician & Other Practitioners - by Provider" (services, beneficiaries, Medicare payments), 2013 onward. They include the rows CMS suppresses from the per-drug and per-service detail (fewer than 11 claims or beneficiaries), so they can be higher than the detail figures. A provider whose detail rows are all suppressed now shows the summary. Both are rows on `/sources`.
- **DMEPOS fees inside a CMS revision file's window.** Three held CMS revision files now answer in the window their authority states: the CMS-1687-IFC file (June 2018), the CARES Act file under CMS-5531-IFC (2020-03-06 to 2020-06-30) and the revised 2022 file (2022). Inside the window, a state cell that the quarter file holds answers with the revision file's amount. `as_of.rule` is `revision_window`, and `as_of.revision` names the window, the authority's own sentence, the authority file, how the window ends, and the quarter cell it revises. A cell found only in the revision file answers only in 2020-04-01 to 2020-06-30, where the 2020Q2 zip holds no state table. A cell found only in the revised 2022 file answers no cell, because that file lists codes added through October 2022. The window rows are written by `--reversion` after the quote is found in the archived authority (migration 186). Until they are written and the files are parsed, the `revision_not_served` answer stays. In 2022 Q2 and Q3 no quarter state table is held: the revised 2022 file answers there only over the January 2022 state table that #470 continues into those quarters (member continuations, migration 174), and `as_of.revision.revises` names that continuation. Until #470's continuations are written, those dates keep the `revision_not_served` answer.
- KB code pages show "Code valid on the date of service": the code's status and reason code, the HCPCS file's own fields as CMS states them (`ADD_DT`, `EFCTV_DT`, `TERM_DT`, `ACTN_CD`), the file's quarter and a link to the archived file. It is the same answer as `/check`. When the HCPCS file cannot be read, it says unknown with the reason `source_unavailable`. The free E0601 page shows it too.
- **BPCI Advanced anchor providers as an employment signal.** The attribution ledger gains `bpcia_crosswalk` rows: the hospital (by its PECOS PAC id) where CMS's BPCI Advanced data crosswalk lists the physician as attending or operating physician on anchor episodes, per model year, confidence 0.60. `GET /provider/{npi}/employment` shows them on an employer the physician reassigns benefits to, and `GET /employers/{pac_id}` counts them in that employer's attribution summary. Hospitals only; the crosswalk carries no CCN for physician group initiators.
- **TEAM joins the Innovation Center models.** `GET /vbc/models` lists the Transforming Episode Accountability Model with CMS's stage; `/vbc/models/team/participants` pages the 721 acute care hospitals on CMS's participant list (CCN, mandatory or voluntary, CBSA state), with the list date as the period. TEAM is a row on `/sources`.
- **ACO membership by NPI.** `GET /provider/{npi}/acos` and `GET /vbc/provider-lookup?npi=` answer which MSSP ACOs and ACO REACH entities an NPI is linked to. Every row carries `match_method` and `confidence`: `cms_qpp_participation` (CMS's QPP participation status names the APM entity for the NPI; `high`, or `stated_unresolved` when the name resolves to no single ACO), `organization_name_match` (`medium` when the name belongs to one PECOS organization and one participant row, else `low`), `individual_name_match` (`low`). A name match is never presented as exact. The name search on `/vbc/provider-lookup` stays and marks each row as a participant name search. The `qpp_mips_tin` source now loads CMS's QPP participation status per NPI (practice and APM entity; the TIN stays masked by CMS). Rows appear after the first QPP crawl and the next ACO load on production.
- **DMEPOS fee answers quote CMS's words when ceiling and floor are both 0.00.** `GET /rules/dmepos/fee` adds `ceiling_floor_statement`: CMS's README text for fields 6 (CEILING) and 7 (FLOOR), word for word, with the README file's id, sha256 and name. A zero in those fields is CMS's value, not a missing number. The statement appears only when the quarter's README is one Verzi has read; otherwise the field is absent.
- **Six Innovation Center models.** `GET /vbc/models` lists BPCI Advanced, CJR, Kidney Care Choices, Primary Care First, Making Care Primary and ACO Primary Care Flex with CMS's stage and what we hold. `/vbc/models/{model_id}/participants` pages the participants CMS lists (BPIDs, CCNs, KCC ids, ACO ids). `/vbc/models/{model_id}/results` pages the financial and quality results CMS publishes (CJR reconciliation PY1 to PY7, KCC PY2022 to PY2024, PCF PY2022 to PY2024) as one row per participant, period and metric. `/vbc/models/by-npi/{npi}` returns the model participations CMS states for an NPI (BPCI Advanced anchor providers), each marked `cms_crosswalk_npi`. Each model is a row on `/sources`.
- **The warrant's ordering block states the ordering (not referring) physician's eligibility on the date of service.** `warrants.w0_ordering.order_referring_eligible.eligibility_on_dos` answers `valid` (on the CMS Order and Referring roster in force, with DME = Y), `not_valid` (`dme_n`, `not_in_file_in_force`) or `unknown`, with a reason from the same vocabulary as the other validity facts. It quotes the roster's columns under CMS's own header names, PARTB, DME, HHA, PMD and HOSPICE, as Y or N, and null for any other value. It cites the edition in force and the edition that established the row. Provider standing's unknown answers for the roster now carry a `reason_code`.
- **DMEPOS fee answers for the PEN codes (enteral and parenteral nutrition, B-codes and E0776).** `/rules/dmepos/fee` answers a state fee for these codes from CMS's PEN fee schedule, 2016 to 2026, in the quarter of each PEN file (January, and the quarters CMS revised the file). Other quarters answer no fee cell until CMS's own statement that the PEN file stays in force is recorded. The 2015 PEN file is a national fee and is not served yet. The file states no ceiling or floor, so those fields are null.
- **The warrant says whether the HCPCS code was valid on the date of service.** The new `warrants.code` block answers `valid`, `not_valid` (`terminated`, `not_yet_effective`) or `unknown` with a reason. It quotes CMS's own `ADD_DT`, `EFCTV_DT`, `TERM_DT` and `ACTN_CD` from the HCPCS file in force, and cites that file. It also carries `COV`, `PRCNGCD` and `BETOS` as the file in force states them, and cites that file in `admin_citation`. From 2015 to 2020 only the January file is held, so a code added or ended later in those years is answered from the next file, which states the date. That answer is marked `stated_in_later_file`. `TERM_DT` is the last date of use, with no grace period.
- **`POST /contact`** sends the verzi.health contact form as one email (SES, the KB login sender) to the Verzi team, with the visitor as Reply-To. It accepts posts from verzi.health only and is rate limited per IP. The site's form moves here from its Resend function in a follow-up site PR.
- **An admin can set one API key's daily limit.** `PATCH /admin/api/users/{user_id}/rate-limit` with `{"rate_limit": N}` stores that key's daily cap (1 to 1,000,000). `{"rate_limit": null}` clears it, and the key uses the free-tier default. The daily check and the `X-RateLimit-Limit` header both use that cap.
- **Check any DME code.** `/check` and `/check/E0601` are public pages. A visitor enters a HCPCS code and an optional state and date of service and sees the governing LCD, how many documentation requirements Verzi serves (counts only, with a link to the code's KB page), and the CMS source citation. `GET /v1/check/{hcpcs}` is the same answer as JSON. Both are limited per IP. The signed receipt needs a paid plan.
- **Usage dashboard.** `/admin/growth` shows signups, active keys, calls per day and week, unique organizations, top endpoints and codes, MCP calls, and the week-over-week growth rate. The page reads a daily rollup. Weekly CSV and JSON exports sit on that page. Set `PUBLIC_USAGE_STATS=1` to serve the same numbers at `/usage`.
- **MCP listing pack.** The remote MCP endpoint takes the API key as `Authorization: Bearer` or `X-API-Key`, and rejects a browser `Origin` that is not allowed. `api/mcp/server.json` matches the official registry schema. `docs/mcp-listings.md` has the fields and steps for the MCP Registry, Smithery, mcp.so, PulseMCP, Glama, the Cursor directory, and Claude and ChatGPT connectors. Nothing was submitted.
- **DMEPOS fees for 2015 and for 2016 to 2017 and 2025 to 2026 dates.** `GET /rules/dmepos/fee` answers state fees for the quarters whose main fee file was not read before: 2015 Q1, Q3 and Q4, 2016 Q1 and Q3, 2017 Q1 and Q3, 2020 Q3 and 2025 Q2 to 2026 Q1. The 2015 files state one fee per state and no rural fee, so a rural ZIP on a 2015 date gets that state fee, and the answer carries a `note` that says so.
- **The KB has its own address: `kb.verzi.health`.** The root opens the KB. The site menu and `app.verzi.health` now point there. Links to `api.verzi.health/kb/...` keep working.
- KB policy and code pages: a Contents column on the left (CMS's section headings under "Policy text", then the passages in the other documents, version history, revision history and receipts; Verzi's own sections carry a "Verzi" tag). Each section can close to its heading, with Expand all and Collapse all; sections start open. A table longer than 15 rows shows its first 10, with a button for the rest. Find in policy, a Requirements at a glance click and a Contents link open what they point into, and print shows every section and row. Below 1200px the Contents sit above the text behind a Contents button.
- KB search (`/kb`): one box finds a HCPCS code, an LCD or Article number (L33718, A52467, or 33718), or words from a CMS document title or HCPCS Level II descriptor ("oxygen", "CPAP"). A full code or number opens its page. The document list filters as you type. Links with `?code=` keep working.
- **KB toolbar and side rail.** Each LCD, Article and code page has one toolbar under the header. It holds the date of service and State. In the paid view it also holds Find in policy, Copy + cite (copies selected CMS text with its version citation) and the appeal exhibit. On a wide screen it stays at the top while you scroll, with the not-in-force banner. A right rail holds "Requirements at a glance": the approved labels on the CMS text on the page, grouped by document and kind, each a button to its highlighted passage. The related documents CMS lists moved to the rail. A one-line legend explains the teal highlight.
- **Two more lists in provider standing.** `GET /provider/{npi}/standing` returns `sam_exclusion` (listed on the SAM.gov exclusion list on the date of service) and `medicare_opt_out` (opted out of Medicare on the date of service), each from the archived list in force on that date and each with its own receipt that names the list (`source_list`). They never feed the OIG `exclusion` block. A date no held version covers answers `unknown` with a reason. SAM history starts at our first capture in 2026; the opt-out list starts at the April 2023 release, in force from its release day (about three weeks after the month).
- **The revised January 2021 DMEPOS fee schedule file answers 2021Q1 dates.** CMS posted the January 2021 file twice: `dme21.zip` and, on 2021-03-01, `dme-21ar.zip` ("Revised January 2021 DMEPOS Fee Schedule ... Revised 3/1/21"), filed under the same quarter. The archive now follows that page and versions the revised file as the quarter's second version, with CMS's own marker as its version. On the same start a stated version now ranks above none (migration 164). So `/rules/dmepos/fee` and the warrant's `w1` answer a 2021Q1 date from the revised file once it is captured and versioned, and the first file stays visible as superseded. Before, the revised file was not held and the first file answered.
- **A W1 fee answer says when the file was continued by the publisher.** CMS released no July 2021 DMEPOS fee schedule file; its July 2021 transmittal (R10865) states there are no updates for July 1, 2021, so the April 2021 file stayed in force. `/rules/dmepos/fee` and the warrant's `w1` now answer a 2021Q3 date of service from the April 2021 file. Their `as_of` carries rule `file_quarter_continued` and a `continuation` block: the through date, the archived transmittal's doc id, version id and sha256, and CMS's sentence. Before, those dates answered `not_found` with the gap reason. Nothing changes for a date inside a file's own quarter.
- **KB policy pages carry the main template's version boxes, receipts table and jurisdiction row.** Each version chip now shows its window by the as-of rule ("2020-01-01 to 2021-08-07", "2024-01-01 to today"). Versions superseded the same day they took effect sit behind a toggle, closed by default; it opens when one of them is the version shown. The receipts toggle lists every archived version of the document with its window, capture date and archived bytes sha256 (linked to the file's provenance), then the live CMS pages with the same-version rule and the window rule. A DME MAC jurisdiction row (JA to JD with their states and contract numbers, from CMS's pages) sits under the title; two letters in the State box open the governing jurisdiction.
- **Past NCD versions are captured by number.** The CMS Coverage API lists every version of a National Coverage Determination (`/v1/data/ncd/other-versions/?ncdid=N`) and serves each one with its own effective and end dates. The archive now fetches every past version the list names that it does not hold (206 versions across 112 NCDs on 2026-10-08), so `GET /archive/docs/ncd:{id}/as-of` answers dates before the current version's start once the capture runs.
- **A W2 PTP answer can carry a correction.** A later CMS file can delete a pair on or before a date that the edition in force still lists open. `/rules/ncci/ptp` and the warrant's `w2.ptp` block keep answering from the edition in force. The fact now also carries a `corrections` entry: the later edition, its deletion date, and a note. The entry stays only while every edition after it still agrees. A still-later file that lists the pair open again, or moves the deletion date past the date of service, removes the entry.
- **A free E0601 page.** `/kb/free/E0601` shows the code page in the signed-in view with no key: LCD L33718's full text, for the version in force today and the version before it, with Find, Copy + cite and the appeal exhibit. Every other version, document and code still needs a KB account. The page is not indexed.
- **Find in policy on KB pages.** Once the full text loads for a customer, a search box above it highlights every match inside CMS's verbatim text, shows "N of M matches" with next and previous, and clears with Escape. It runs entirely in the browser (no network call), skips receipts and page labels, and leaves the approved-passage highlights untouched.
- **The paid KB view.** Signed in to the KB (or calling with an API key), a code page loads every approved passage for the code on the chosen date (the same passages the warrant API serves), each with its label, CMS's words, the version, and a receipt behind a toggle; the free view still shows one sample. On a policy page, the loaded verbatim text highlights each approved passage where its words match exactly; a passage that does not match exactly is listed under the text instead, never forced. Pages never ask for a pasted key. An "Appeal exhibit (print)" button prints the page as an exhibit with every receipt open.
- **Why a passage did not serve.** When policy passages resolve, the `w3` block of the signed warrant response (`GET /v1/warrant`) carries a new top-level `withheld` field: the count of approved passages for the version in force that were not served, by reason. `verification_failed` means the words did not verify against the archived bytes. `not_on_serving_registration` means the passage quotes an earlier rendering of that version. Additive; existing fields are unchanged. KB code pages state the same reason in one sentence.
- **ICD-10-CM code lists on KB Article pages.** The current version of a policy Article now shows CMS's ICD-10-CM lists ("Codes that Support Medical Necessity" and "Codes that DO NOT Support Medical Necessity"): each group's paragraph, quoted, and its code count, free. The full code table serves customers with an API key. Read verbatim from CMS's Medicare Coverage Database download, with the file's sha256 as the receipt, and refreshed with each weekly capture. A group paragraph that cites CPT codes is withheld because it cites CPT codes. No list is ever attached to a HCPCS code page by inference.
- **Sample integration page: one code, every payer.** An unlisted, noindex page for E0601, K0606, and A4239 in Utah, with Medicare, Utah Medicaid, and a commercial payer side by side. Columns: rate, prior authorization, edits, coverage passage. Each cell is a table fact with its receipt, a quoted passage, or "Not held yet". Commercial rates come from the payer's Transparency in Coverage files, with each rate-carrying NPI checked against CMS's 2024 Medicare DME supplier file. The date-of-service picker matches the KB pages.
- **Watch a code from its KB page.** Every `/kb/code/{code}` page carries a follow box: "One email when any policy cited for {code} changes or gains a future effective date. Nothing else, ever." It uses the same double opt-in subscription as the updates page.
- **Historical versions get their own chip.** Versions pulled from the past (backfill or Wayback copies) no longer show as news. The updates feed and RSS show what scheduled runs observed; the "Historical versions" chip shows the rest. `GET /v1/archive/changes` takes `origin=live|historical` and returns `truncated` when the window holds more rows than the limit, and the page says so.
- **RSS by chip.** `/v1/archive/changes.rss?category=claim_policy` (or `fee_schedule`, `code_set`, `regulatory`) carries one chip's sources. The page's RSS link follows the chip.
- **Date of service on KB pages.** KB document pages and KB code pages have a "Date of service" calendar picker. It sets `?dos=YYYY-MM-DD`. The page shows the policy version in force on that date. The warrant API's as-of rule chooses the version. One line names it: "Version in force on 2024-03-15: version 48, effective 2021-09-27 to present". A date of service wins over `?v=`. Some dates have no archived version in force. The page then says so and shows no policy content. A bad date gives a plain error line. On code pages, each mapped policy shows its own version in force. The sample passage follows the date.
- **CMS's Required Prior Authorization List for DMEPOS, as of the date of service.** `GET /v1/rules/warrant` adds a `prior_authorization` block. `required_prior_authorization` is `listed`, `not_listed`, `listed_in_stated_states`, or `not_determinable`. It states list membership on the date of service for the request's `state`, from the list version in force by the archive as-of rule. The block carries the list's own row: descriptor, Effective Date cell verbatim, phases with their printed dates and state scope. A state that no phase in effect covers is `not_listed`; the reason quotes the later phase that will cover it, as the list prints it. With no state and only state-limited phases in effect, the value is `listed_in_stated_states`, `scope_on_dos` quotes the scope, and the rollup grades it partial. When any part of the cell the answer needs is unreadable, the answer is `not_determinable`, never a confident `listed` or `not_listed`: `not_determinable_kind` names the cause (`date_not_readable`, `scope_not_readable`, `not_parsed`, `no_version_in_force`, `state_not_recognized`). A scope reads as Nationwide only when its words are exactly "Nationwide", with at most one trailing `:`, `;`, `,`, or `.` (the list prints "Nationwide:" once). A `state` that is not a state, DC, or territory code gives this block `not_determinable` and leaves the rest of the warrant answered. A CBA gives the block no state; `provider_context.prior_authorization_state` echoes the state used. When no list version is in force on the date, or the table is absent, the answer stays in its block and does not feed `confidence.overall`. Every other `not_determinable` stays in the rollup as unknown. Every answer carries a receipt: version, sha256, observed window, and the CMS URL. KB code pages show one line for the picked date of service or today: the date it answers for, the list's Effective Date cell quoted verbatim, and a receipt link. When only state-limited phases are in effect, the line says "for these states only, as the list prints them" with the scope verbatim.
- **Transmittal subject lines, quoted.** Every CMS transmittal entry on the updates feed, the calendar, the weekly digest, the watch-a-code alerts, RSS, and webhook events now carries the cover page's SUBJECT line verbatim, labeled "CMS's subject line, quoted". Extracted deterministically from the archived rendering, joined across its wrapped lines, nothing reworded. MLN Matters articles carry no SUBJECT field and keep the plain statement that none was published. New transmittals get their line in the same weekly load that archives them.
- **Sections changed, by the publisher's headings.** Feed entries and webhook events (`sections_changed`, `sections_changed_vs`) name the sections whose words changed versus the prior version of the same document, using the publisher's own section headings in document order: "Sections changed vs version 48: Coverage Indications, Limitations and/or Medical Necessity · Revision History". Words are compared whitespace-insensitively; no diff text is written. The slot stays empty where no comparable prior version exists.
- Warrant W3 free tier: one sample item per code serves its verbatim passage text, marked `sample: true` (parity with the KB sample slot).
- **One-click unsubscribe headers.** Digest and watch-a-code emails now carry the RFC 8058 headers (`List-Unsubscribe`, `List-Unsubscribe-Post`), so a mailbox provider's own unsubscribe button revokes the subscription in one click. The in-body link keeps the scanner-safe two-step page.
- **Policy KB pages (`/kb`).** Every Medicare DME policy document (LCDs and Articles), rendered verbatim from the archived bytes, one page per document version. An independent fidelity gate proves every served section is word-identical to the source bytes; a section that fails the gate is held, never served. Free, no key: the document title, every version with its effective window, the section outline, counts, CMS's own revision-history notes, and the byte-level receipt (sha256, fetch date). Entitled keys additionally load the full verbatim section text. Thin per-code pages at `/kb/code/{hcpcs}` link each labeled code to its governing documents, and unlock ONE sample passage in full (the first approved item for the code, marked as a sample); unmapped codes say so plainly. `GET /kb/metrics` publishes the corpus numbers (codes, documents, versions, archive depth, fidelity percentage), every number computed live from the database.
- **Bounce and complaint suppression.** An address that bounces permanently or files a complaint never gets another subscription email, including the confirm email after a re-signup. Suppression is per address, written automatically from SES feedback, and checked before every send.
- **Upcoming-changes calendar.** `updates.verzi.health/calendar` lists every document version with a future effective date, soonest first, grouped by date: fee-schedule quarters, dated policy revisions, transmittal batches. `GET /v1/archive/upcoming` serves the rows (public, no key), each with `affected_codes` where a mapping exists. The dates are the publisher's own; a version with no stated effective date is never listed.
- **RSS feed.** `GET /v1/archive/changes.rss` renders the change feed as RSS 2.0; linked as the alternate representation on `updates.verzi.health`. Item text holds the publisher's facts and the sha256 receipt only.
- **Signed change webhooks.** A webhook subscription gets one signed POST per daily run with the matching changes: `X-Verzi-Signature` is `hex(hmac_sha256(secret, body))` with the per-subscription secret from registration. Every event carries its receipt (doc_id, version, sha256 of the archived bytes) and is auditable against `/v1/archive`. At-least-once delivery; dedupe on `version_id`. Documented at `/resources/webhooks`.
- **Watch a code.** Subscribe to one or more HCPCS codes on `updates.verzi.health`. A daily check emails you when a document that governs a watched code gets a new version, including a version with a future effective date ("takes effect <date>"). Same double opt-in, same one-click unsubscribe.
- **Weekly digest email.** Verified subscribers get one plain-text email a week with the window's new document versions: titles, versions, effective dates, and counts, the publisher's facts only, scope-filtered per subscription. Signup form on `updates.verzi.health` (double opt-in). A subscriber with no matching changes gets no email. Non-live versions say so (`backfill`, `wayback`).
- **Change subscriptions.** `POST /v1/subscriptions` subscribes a destination to the archive change feed: an email address (public, double opt-in; nothing sends until the confirm link is opened) or a webhook URL (API key required; deliveries are HMAC-signed with a per-subscription secret, returned once). Scope by HCPCS codes, by source ids, or neither for the full weekly digest. Unsubscribe is one click from every email.
- W3 label queue: `GET /warrant/w3/labels/pending` (verbatim quote + context per item) and `POST /warrant/w3/labels/{id}/decision` (same 409 contract as the requirement seam).
- Warrant W3: the slot now serves verbatim policy passages with Verzi labels (`policy_passages`) when the passage layer resolves for the code and DOS; the legacy requirement shape remains as fallback. Migration 128.
- Warrant W1: an exact fee-cell miss now names the modifier combinations that do exist for the code and area (`available_cells`); status stays `not_found`.
- **Updates feed.** `GET /v1/archive/changes?days=N` lists every new document version the archive observed in the window, with the publisher version, effective window, observation time, and sha256 (public, no key). `updates.verzi.health` serves it as a readable change feed: new LCD versions, fee-schedule quarters, NCCI edits, and code-set releases, grouped by day. Nobody writes these entries; the pipeline observes them.
- **Product selector on signup.** The API-key signup form now asks what you are here for: provider intelligence (the data API), warrants for RCM (paid), or both. The answer routes your access request to the right review; it does not change what a free key can do.
- **Coding rules, parsed from the archive (`/rules/*`).** The first parsed layer over the source archive turns NCCI edits (warrant W2) and DMEPOS fee cells (warrant W1) into as-of lookups, each citing the exact archive file it came from. `GET /rules/ncci/ptp?setting=&code_a=&code_b=&dos=` answers whether a code pair is procedure-to-procedure edited for a setting on a date of service, with its modifier indicator; the row's own effective and deletion dates gate the answer (the deletion date is the first day out of force). `GET /rules/ncci/mue?setting=&hcpcs=&dos=` returns the Medically Unlikely Edit value and its adjudication indicator from the quarter in force on the date. `GET /rules/dmepos/fee?hcpcs=&state=&rural=&dos=` (or `cba=` for a former competitive-bidding area) returns the allowed amount, national ceiling, and floor. Every answer carries its receipt: the version id, the archive member file id, and that file's sha256. The tables hold codes and amounts only, no CPT descriptors. Spec: `docs/PARSING_SPEC.md`.
- **Source archive (`/archive/*`).** The raw, immutable layer under every parsed table: every byte a publisher served, stored exactly as served and hashed (sha256) before any parsing, with zip members as first-class files, versioned per document, and resolvable to the version in force on a date of service. Sources so far: HHS OIG LEIE (full file and monthly supplements), NCCI PTP and MUE tables (with the DME supplier MUE table and the MUE archive back to 2020), the DMEPOS fee schedule (every quarter CMS posts, 1998 to 2026), and HCPCS Level II quarterly files (2023 to 2026). Endpoints: `GET /archive/sources`, `GET /archive/{source_id}/files`, `GET /archive/{source_id}/docs`, `GET /archive/docs/{doc_id}/versions`, `GET /archive/docs/{doc_id}/as-of?date=`, `GET /archive/files/{sha256}` (metadata and a one-hour link to the original bytes), `GET /archive/files/{sha256}/text` (the stored text rendering with its renderer version), `GET /archive/runs`. The as-of answer names the rule that decided it (the publisher's stated window, or when the archive first saw the version) and never guesses. `/sources` gains `warrants`, `archive_file_count`, `last_fetch_at`, `last_change_at`, and `latest_publisher_version`. Text from files that carry CPT descriptors (AMA license) is not served. Spec: `docs/ARCHIVE_SPEC.md`.
- **DailyMed labels, the official SPL record.** `GET /rx/labels` searches human prescription drug labels from DailyMed (the official Structured Product Labeling record, not openFDA's copy) by drug name, labeler, or a required section, returning set_id, version, effective date, title, labeler, and which of the four key sections are present. `GET /rx/labels/{set_id}` returns one label's boxed warning, indications, dosage and administration, and contraindications section text (parsed by LOINC code, nested subsections included), its RxNorm concepts, and its pharmacologic classes. Every `dailymed_url` is pinned to the version we hold (`lookup.cfm?setid=...&version=N`), so a citation never shows newer text than we stored. `GET /rx/drug-classes` serves the pharmacologic classes from DailyMed's official class mapping (1,539 classes) with the number of labels in each, filterable by name and FDA class type (EPC, MoA, PE, CS). Chosen over openFDA's label copy because DailyMed updates daily, keeps version history, and publishes the official SPL-to-RxNorm and pharmacologic class mapping files, while openFDA's copy is weekly, flattens tables, and attaches drug codes only on an exact match. New source `rx_dailymed` at `/sources`. Migration 097.
- **Drugs@FDA: the approved-drug application record.** `GET /rx/drugsfda` searches every FDA-approved drug application (29,336 NDA, ANDA, and BLA applications) by sponsor, drug name, active ingredient, application type, and first-approval-date window, returning the sponsor, products, ingredients, first and latest approval dates, and submission and product counts, ordered by first approval date. `GET /rx/drugsfda/{appl_no}` returns one application's products (form, strength, ingredient, therapeutic equivalence code, marketing status) and its complete submission history, the original plus every supplement with approval dates. The data comes from the FDA's own Drugs@FDA data files, not openFDA's copy. `first_approval_date` is the earliest approved submission; a submission status of TA is a tentative approval (approved on the merits but blocked by another product's patent or exclusivity). New source `rx_drugsfda` at `/sources`, refreshed monthly. Migration 096.
- **FDA drug recalls, matched to the NDC spine.** `GET /rx/recalls` serves the openFDA drug enforcement dataset (about 18,000 recall enforcement reports) filterable by NDC, ingredient, recalling firm, hazard class (Class I, II, III), status (Ongoing, Completed, Terminated), and report-date window. Each recall carries its classification, status, firm, reason, distribution pattern, and report, initiation, and termination dates. NDCs are extracted from three places, because the openFDA drug-code block is present on only about a fifth of records: the openFDA block, the free-text product description, and the code-info lot list. Each extracted NDC is normalized to 11 digits and matched against the current NDC directory; matched NDCs also resolve to their RxNorm ingredient. The match is fuzzy, so the response reports it honestly: `match_stats` and the `methodology_note` state that about 51% of recalls carry a parseable package NDC and about 19% match at least one NDC to the current directory (an older recall cites a discontinued NDC no longer listed). New source `rx_drug_recalls` at `/sources`, refreshed weekly. Migration 095.
- **FDA drug establishments are now queryable.** The FDA Drug Establishments Current Registration Site (DECRS) has been loaded since migration 089 and no endpoint could read it. `GET /rx/establishments` searches all 9,878 registered drug plants worldwide by operation, ISO-3 country, and firm or registrant name, returning FEI number, firm, country, and the registered operations. `operation=API MANUFACTURE` isolates the 2,400 plants that make an active ingredient, the supply-side view behind a drug shortage. `GET /rx/establishments/operations` lists the 15 operation types with counts. The `operations` array is now split into one element per operation (the source file separates them with semicolons, not commas), so a plant that registers "ANALYSIS; API MANUFACTURE; PACK" now matches an API-maker filter; the previous load stored the whole string as one element and missed those plants. The public file does not link a plant to the NDCs it makes; the `methodology_note` says so.
- **CMS Medicare fee schedules (public domain), loaded for internal rate math.** The Medicare Physician Fee Schedule (`medicare.mpfs_rvu`, `medicare.mpfs_gpci`, and the conversion factor) and the Ambulatory Surgical Center payment rates (`medicare.asc_rates`). With these, "a percentage of Medicare" becomes dollars per CPT code and locality. The non-facility allowed amount is `CF x [(work_rvu x work_gpci) + (pe_rvu x pe_gpci) + (mp_rvu x mp_gpci)]` for the named locality. Loaded for the recent years CMS publishes, refreshed quarterly. Today the only reader is the admin-only `/wellness/rates`, so these two sources are held off the public `/sources` catalog until a public fee-schedule endpoint ships (they will return to the catalog in the same change). Note: a CMS 2-digit locality code is not unique nationally, so resolve a locality by name where the code is shared.
- **FDA Orange Book and the generic launch radar.** The Orange Book has been loaded since migration 089 and no endpoint could read it. `GET /rx/orange-book` searches all 48,664 approved products by trade name, ingredient, applicant, application type (N brand, A generic), product type, and therapeutic equivalence code. `GET /rx/orange-book/launch-radar` serves `rx.v_generic_launch_radar`: the 2,457 brand prescription products with no approved generic of the same ingredient, dosage form, and strength, ordered by the date the last listed barrier falls, filterable by ingredient, applicant, and date window, with `unprotected=true` for the 866 products carrying no patent and no exclusivity at all. `loss_of_exclusivity` is an outer bound rather than a forecast: it is the latest listed patent or exclusivity date, so a late device or formulation patent can sit years past the compound patent, and most generics arrive earlier through challenges and settlements that this data does not carry. The `methodology_note` says so on every response.
- **Drug economics spine (schema `rx`).** The platform now speaks NDC. Seven new sources at `/sources`: the FDA NDC Directory (116K products, 218K packages, normalized to 11-digit NDCs), RxNorm prescribable content (NDC-to-RXCUI links and ingredient relationships, no UMLS license needed), NADAC weekly acquisition cost stored as change-only price history per NDC from 2013 forward, CMS Spending by Drug for Part D, Part B, and Medicaid (unpivoted to one row per drug, manufacturer, and year) plus the quarterly Part D file and Part D by geography and drug, Medicaid State Drug Utilization Data from 2019 forward, openFDA drug shortages, and the Mark Cuban Cost Plus Drug Company public price list captured as dated, change-only snapshots. Refreshed on the existing weekly, monthly, and quarterly pipeline cadences. API endpoints follow in a later release.
- **AHRQ MEPS Prescribed Medicines, 2018 to 2024.** Fill-level prescription events with 11-digit NDC, quantity, days supply, payer splits, and person weights, joined to the Full-Year Consolidated file for age and insurance coverage (`rx.meps_rx_fills`, about 200K fills per year). `rx.mv_meps_ingredient_mix` gives the weighted drug mix by year, payer segment, and active ingredient; `person_segment` `private_under_65` is the employer-covered population, which is the honest basis for commercial drug-mix questions that Part D cannot answer. `GET /rx/meps/mix` serves it with median generic NADAC beside the per-unit amount payers actually paid.
- **`/rx/*` Drug Economics API.** `GET /rx/summary` (counts and freshness), `GET /rx/ingredients` and `/rx/ingredients/{rxcui}` (RxNorm ingredients with NDC counts, median generic NADAC, CMS spending, Cost Plus matches, shortages), `GET /rx/ndcs/{ndc11}` and `/rx/ndcs/{ndc11}/nadac` (package profile and change-only price series), `GET /rx/nadac/movers` (largest price moves in a window), `GET /rx/spending` and `/rx/spending/top` (CMS Spending by Drug for Part D, Part B, Medicaid), `GET /rx/shortages`, `GET /rx/cost-plus` and `/rx/cost-plus/{slug}` (Cost Plus public price beside median NADAC, with the implied acquisition cost from the published formula and dated history), `GET /rx/employers` and `/rx/employers/{ein}` (DOL 5500 health plans with funding class, PBM, disclosed PBM fees, and drug-coverage carriers; default sort is active participants, largest first), and `GET /rx/pbms` (PBM rollup by plans, covered participants, and disclosed compensation). Every response carries a `methodology_note`.
- **DOL Form 5500 now covers every industry, with Schedule C and Schedule A.** `dol_5500_filings` previously held healthcare-NAICS sponsors only. It now loads all filings and records the welfare-benefit and funding flags, with a derived `health_funding_class` (self-funded only, self-funded with stop-loss, insured only) for health plans. New `dol_5500_service_providers` (Schedule C Part 1 Item 2: every service provider a plan paid, with direct and indirect compensation, DOL service codes, and a PBM flag with a normalized PBM family) and `dol_5500_insurance_contracts` (Schedule A: carrier, persons covered, health, drug, and stop-loss benefit flags, premiums earned, claims paid). The healthcare-only `financial_filings` aggregation is unchanged.
- **Which self-funded employers buy pharmacy and medical from one conglomerate.** Every DOL 5500 health plan now carries an `integration` class: `same_parent` when the PBM on Schedule C and the health carrier or medical administrator belong to one company (Caremark with Aetna, Express Scripts with Cigna, Optum Rx with UnitedHealthcare or UMR, CarelonRx with Anthem), `blues_affiliated` for Prime Therapeutics with a Blue Cross plan, `different`, or unclassified when a plan names no PBM or no carrier. Self-funded plans file no Schedule A for their medical administrator, so the administrator is read from Schedule C as well. For 2023 that is 295 same-parent self-funded plans covering 3.29M employees. `GET /rx/employers?integration=same_parent` and `GET /rx/employers/integration` (counts, covered employees, and disclosed PBM fees by class and by parent, plus the largest plans).
- **Medicare Part B ASP payment limits and the NDC-to-HCPCS crosswalk, 27 quarters since 2020.** What Medicare pays per billing unit for clinician-administered drugs (`rx.asp_pricing`) and the CMS crosswalk from every NDC to its HCPCS code with billing units per package (`rx.asp_ndc_hcpcs`), loaded quarterly with preliminary files flagged and replaced by the final. `GET /rx/asp` searches by description, HCPCS, or NDC. `GET /rx/biosimilars` puts each FDA Purple Book biosimilar beside its reference product's payment limit (Stelara J3358 against its seven biosimilars, Avastin, Remicade, Prolia and Xgeva) and the cheapest Cost Plus product of the same molecule.
- **Healthcare construction signals — new product surface.** Early construction and expansion signals from public building-permit portals, state Certificate of Need / Determination of Need programs, and statewide project registries across 19 states, refreshed daily by a new pipeline. Permits are keyword-screened and LLM-verified for healthcare relevance; CON/DoN filings are healthcare by definition. Each signal is conservatively resolved (state-scoped exact and fuzzy matching) to a facility, health system, and an organizational contact from public NPPES data where possible. Three endpoints: `GET /signals` (search by state, city, category, system, minimum value, recency), `GET /signals/summary` (counts and dollar volume by state and category), and `GET /signals/systems` (per-system rollup ranked by dollar volume). ~15.6K signals at launch spanning a 400-day window, including hospital towers, ASC build-outs, imaging centers, and senior-care projects with reported values from thousands of dollars to nearly $1B.
- **MSSP financial and quality results, PY2013 to PY2024.** We held 5,361 Shared Savings Program ACOs and 182K participants but no financial results for any of them. The gap is closed. New `mssp_financial_results` holds **5,476 ACO-years** with benchmarks, expenditures, shared savings and losses, quality scores, provider counts by type, beneficiary demographics, risk scores, and utilization. New `mssp_quality_measures` holds **127,374 per-measure results** across 74 distinct measure codes. Three endpoints: `GET /vbc/financials` (search and rank, including `sort=losses`), `GET /vbc/financials/summary` (program rollup by year and track), and `GET /vbc/acos/{aco_id}/financials` (one ACO's full history plus cumulative earned savings). The PY2024 load reconciles exactly with the CMS fact sheet on four independent figures: 476 ACOs, 10,326,340 assigned beneficiaries, $4.12B in performance payments, and 75% of ACOs earning them.
- **Rates are normalized to percent across all 13 MSSP performance years.** CMS switches these columns between a 0-1 fraction and a 0-100 percentage from file to file, and the `%` suffix does not track the switch: PY2024 dropped the suffix but kept the 0-100 scale. Left as published, a PY2018 savings rate of 0.0144 and a PY2024 savings rate of 4.215 describe comparable performance but sort 300 places apart. Savings rate, minimum savings and loss percentages, share and loss rates, quality score, and the dual and long-term-institutionalized percentages are all resolved to percent. Every row also carries `source_row`, the CMS row exactly as published, so the raw values remain available.
- **`qual_score_note` records why a quality score is absent.** PY2014 publishes "P4R" for 119 first-year ACOs, meaning they were scored on reporting the measures rather than on performance. That token now has its own column, so it is distinguishable from CMS small-cell suppression.
- **ACO REACH refreshed to PY2024, with PY2021 to PY2024 history and the full published column set.** REACH was frozen at one performance year (PY2023) since launch. It loaded a fraction of what CMS publishes. The layer now holds **421,523 participant records across four performance years** (was 128,548 for one year), **399 financial and quality records** (was 132), and **578 entity-years** covering PY2021 to PY2026 (was 74 for one year). Participant records gain participation dates, the APO election, and all 14 benefit-enhancement waiver flags. The flags cover telehealth, the SNF 3-day stay, post-discharge home visits, concurrent hospice care, and the rest. Financial results grow from 39 columns to 115. The new columns give provider counts by type (PCP, specialist, NP, PA, CNS, FQHC, RHC, CAH), every quality subscore including CAHPS and the 11 stewardship measures, beneficiary demographics, the inpatient expenditure split, and utilization rates. `GET /vbc/reach` and `GET /vbc/reach/{entity_id}` accept a `year` parameter. The participant list is now paged with `provider_limit`, `provider_offset`, and `provider_total`. One entity reached 18,506 participants in a single year.
- **`GET /accuracy/discipline-coverage`** — we now publish how much of the nation's practitioner discipline we actually hold, per state, benchmarked against the NPDB Public Use File (the only national count of state licensure actions and exclusions). Two classes are reported separately because they are different populations: exclusions (federal LEIE + state Medicaid lists) currently cover **52.1%** of NPDB-reported excluded practitioners for 2023–2025, while state board licensure actions cover **0.3%** — Colorado is the only board feed loaded so far. The gaps are published alongside the wins. Unauthenticated, like the rest of `/accuracy`. The benchmark file carries no names or NPIs; only aggregate counts are published, and state counts below 11 are withheld.
- **Primary-source state license data: Washington, Connecticut, Colorado** — the first state-board licenses loaded from the states' own files rather than NPPES self-attestation. All three come from official state open-data portals with daily refresh, no accounts, fully scriptable. New sources `wa_doh` (2,444,206 credentials), `ct_dph` (785,467 health-profession credentials), and `co_dora` (1,524,923 individual licenses) at `/sources`, with NPI linkage backfilled from NPPES license-number matching (digits + last name, confirmed against first name in WA; digit + name match in CT/CO).
- **Colorado disciplinary actions** — the CO DORA file carries structured discipline (case number, action, effective/complete dates). Of 51,848 distinct disciplinary records, the 3,266 that resolve to an NPI load into `license_disciplinary_actions`; the rest carry no NPI and are not matched by name. First state-board disciplinary source beyond exclusion lists.
- **Washington NPI coverage tripled** — WA licenses now resolve to 136,556 providers, up from 43,373, lifting NPI-linked active clinicians across the three states from 22.9% to 28.7% (40% within WA). The previous match required NPPES and WA DOH to agree on the license prefix, but they use different codes for the same credential, so most licenses could never link. Precision improved at the same time: 47 NPIs that had been attached to two different people are gone, and every linked row now agrees with NPPES on first name. Affects `/licenses/{npi}`, `/licenses/{npi}/disciplinary`, and the `/provider/{npi}` dossier.
- **WA/CT/CO refresh daily** — the three state license sources now run on a new daily pipeline cadence (11:00 UTC), each downloading its own file. `/sources/{id}/history` shows the daily trail.
- **IRS 990 coverage extended to tax years 2020–2023** — the bulk XML parser previously reached only 2024-index filings because the IRS index CSVs for earlier years omit the batch-file column needed to locate each filing's XML. That mapping is now recovered by reading each archive's directory listing directly, without downloading the multi-gigabyte archives. Result: **21,439 Form 990 filings across 5,270 healthcare EINs** (was 3,541 / 3,375) and **144,944 executive-compensation records** (was 24,027), feeding `/systems/{id}/financials` and `/systems/{id}/financials/executives`.
- **Executive-to-provider links now populate** — `executive_compensation.matched_npi` and the `irs_990_schj` attribution signals had been empty since launch (two bugs: candidates were filtered by a state that was never recorded for the filing organization, and names were parsed in the wrong order). Fixed: **44,763 executives now resolve to an NPI** (14,070 high-confidence, 30,693 medium) producing **44,025 new employment-attribution signals**. Matching stays conservative — an executive whose name fits more than one provider is left unmatched rather than guessed, so common names like "Robert Grossman" (17 NPIs nationally) produce no link.
- License Intelligence: primary-source verification links. Each license on `GET /licenses/{npi}` and each alert on `GET /licenses/alerts/recent` now carries a `verify_at` block with the state board lookup and disciplinary-page URLs. `GET /licenses/state/{state}` gains a `board_directory` block. Directory: 222 board portals across all 50 states + DC, link-checked; dead links are excluded.
- **Oregon joins the exclusion layer (34 states)** — OR publishes names + per-person exclusion-letter PDFs only (no dates, no NPI), served from a SharePoint list the loader reads through the public SOAP endpoint. Thin, but official — and it completes coverage of every state with an obtainable list. Letter URLs are kept in each record's raw payload.
- **`GET /ma-events/recent` now returns data** — a new SEC 8-K parser scans every logged healthcare 8-K for M&A item codes (Entry into Material Agreement, Completion of Acquisition/Disposition) and loads typed events into `ma_events`: acquisition announced, acquisition completed, and divestiture, each linked to its SEC filing. Target names and deal values are extracted only when confidently matched — otherwise the event carries the filing link and item facts without guessing. Initial load: 24 events (2023–2026) across Tenet, CHS, UHS, Acadia, Select Medical, Surgery Partners, Addus, Astrana, Pennant, Encompass, and Enhabit, including Astrana's $708M Prospect Health acquisition.
- **Physician employer-move detection is live** — `physician_employer_moves` now carries 207,725 arrival/departure events, derived from three PECOS reassignment snapshots (2026-06-10, 2026-08-13, 2026-08-17). The view had been empty since launch because only one snapshot existed; this week's DAC/PECOS refreshes supplied the comparison points.
- **Exclusion Screening page on the dashboard** (`/exclusions` at app.verzi.health) — screen a provider by NPI against the federal LEIE + all 33 state lists with a single verdict banner, or search state lists by name (most state records carry no NPI). Shows live coverage stats (states loaded, record counts, share of state-excluded NPIs absent from the federal list). Backed by new unauthenticated `/demo/exclusions/*` endpoints (check, search capped at 25 rows, coverage).
- **Exclusion Screening published at `/resources/exclusion-screening`** — product overview for the combined federal + state exclusion layer: per-state coverage table, methodology (primary sources, conservative NPI validation, `oig_reciprocal` handling), and the API surface. Listed in the `/resources` catalog.
- **State exclusion endpoints under `/exclusions`** — the state Medicaid exclusion layer is now queryable directly: `GET /exclusions/comprehensive/{npi}` (one-call federal LEIE + all 33 state lists; the top-level `excluded` flag covers both), `GET /exclusions/state/{npi}` (state-list records for an NPI), `GET /exclusions/state/search` (name/state/entity-type search — most state records carry no NPI, so name search is the primary screening path), and `GET /exclusions/state/coverage` (which states are loaded, per-state counts, LEIE overlap, last-load timestamps). `POST /exclusions/screen` now also returns `state_excluded` hits alongside the federal ones, and `GET /exclusions/crossref/{npi}` carries `state_exclusion_records` plus a risk flag when an NPI is state-excluded but absent from the federal LEIE.
- **State Medicaid exclusion lists — batch 2: 12 more states, 8,598 records (33 states, 85,697 total)** — DE, NE, GA, NV, IA, AK, HI, AZ, KS, NC, WV, WY join the `state_exclusions` layer from their primary state sources. Seven of these publish PDF tables; the pipeline runtime now bundles pdfplumber, so all 33 states — including NJ from batch 1 — sync monthly. Minnesota is the one newly-confirmed dead end (Radware captcha on the DHS page); Oregon stays deferred (names-only SharePoint list). Only remaining unbuilt states are the five blocked ones (MA, NH, MN, ME, FL) and thin OR.
- **State Medicaid exclusion lists — 21 states, 77,099 records** — new `state_exclusions` table loaded from each state's primary public source (CA 22.5K, TX 13.4K, NY 9.0K, PA 6.7K, LA 5.9K, MI, NJ, AR, OH, AL, MD, SC, KY, CO, MO, WA, MS, ND, MT, IN, TN). These lists are additive to the federal OIG LEIE: 69.8% of the 11,286 distinct state-excluded NPIs do not appear in LEIE at all. Records with a valid NPI also materialize into `license_disciplinary_actions` (visible in `/licenses/{npi}/disciplinary` and `/licenses/alerts/recent`); rows a state marks as federal-OIG mirrors are flagged `oig_reciprocal` and excluded from that materialization to avoid double-counting. Each state is a registered source (`{st}_medicaid_exclusions`) with independent load history at `/sources`; 20 states sync monthly in the pipeline (NJ's PDF source loads via script until the Lambda bundles a PDF parser). NPI handling is conservative: state-published NPIs only, validated by check digit — no name matching.
- **`facility_affiliations` on DAC provider records and the `/provider/{npi}` dossier** — all facility types a clinician is affiliated with (hospital, home health, hospice, nursing home, dialysis, IRF, LTCH), with CCN and facility type. The existing `hospital_affiliations` field keeps its shape and now carries hospital names joined from our facilities data.
- **Authorized Official employment signals** — every NPPES Type 2 organization names an Authorized Official; when that name uniquely matches exactly one individual NPI nationally, an attribution signal now links the two (687,084 signals, source `nppes_authorized_official`, confidence 0.60). Signals carry a new `employer_npi` identifier field — the first signal source whose employer is identified by NPI rather than EIN or PAC ID — visible in `/provider/{npi}/employment.attribution_signals[]`.
- **License-state corroboration signals** — 1.24M signals (source `nppes_address_match`, type `state_license_address`, confidence 0.70) marking providers whose active license state matches their PECOS employer's primary state. This signal class existed in code but had never successfully emitted — its signal type was never whitelisted and its upsert broke when the license natural key changed; both fixed.
- **The warrant says whether each HCPCS modifier was valid on the date of service.** When a request names `modifier` or `modifier2`, the new `warrants.modifiers` block has one item per modifier: `valid`, `not_valid` (`terminated`, `not_yet_effective`) or `unknown` with a reason, by the same rule as `warrants.code`. Each item quotes CMS's own `ADD_DT`, `EFCTV_DT`, `TERM_DT` and `ACTN_CD` from the modifier records of the HCPCS file in force, gives CMS's description of the modifier with `description_citation` (the file the description is quoted from), and cites the file that decides. A CPT modifier (digit first) answers `unknown` with `license_scope`, and input that is not a modifier answers `not_a_modifier`. Until the modifier records are parsed, each Level II item answers `not_yet_available`, and the confidence summary does not weigh the block. The weekly HCPCS parse now also parses the modifier records.

### Changed
- **The MCP endpoint is `https://mcp.verzi.health/mcp`.** The root of `mcp.verzi.health` is an alias for it (Streamable HTTP). A browser that opens the root gets a short page with the config to connect. `https://api.verzi.health/mcp/` and a bare `/mcp` keep working, now with no redirect.
- The applicability warrant no longer quotes the PIM chapter 5 section 5.2.6 sentence. The text rendering of the PIM PDF is not verified to keep CMS's word order. The `authority` entry keeps its document, version, file, rendering hash and span, and says `text_withheld: "rendering_not_in_document_order"`. The verdict does not change. For a code with no served exemption, `applies` stays null with `no_cms_statement_held`. The quote returns when the PDF renderer is verified.
- **The applicability block quotes CMS's whole refill-scope sentence.** The `authority` entry in `warrants.applicability.basis` now quotes the full PIM chapter 5 section 5.2.6 sentence that sets the scope of the refill rules (for example "Per 42 CFR 410.38 related to DMEPOS products that are supplied as refills to the original order/prescription, suppliers must contact the beneficiary prior to dispensing the refill ..."), with its `span` in the stored rendering. Before, it quoted only the phrase "supplied as refills to the original order". The quote serves only where the receipt's license rule allows it. Otherwise the entry carries `text_withheld` `license` and keeps its source proof. The sentence is a fact in force on the date, not a verdict. `applies` stays null when no held CMS sentence states one.
- **Refill applicability answers only where CMS states it (strict verbatim).** `warrants.applicability` gives a verdict only when a held CMS sentence states it for the code. Today that is a served LCD or Article passage whose own words exempt the code from the refill rules. It answers `applies` false and `completeness` `cited`. The first basis entry is that passage's source proof (`doc_id`, `version_id`, `render_sha256`, `span`).
- The exemption passage's `quote` carries CMS's words only where the receipt's license rule allows the span: any span of a public-domain source, or a span inside a narrative section of an LCD or Article. A span with licensed code text inside it serves no quote. Otherwise the entry carries `text_withheld` (`license`, or `unavailable` when the rendering cannot be read). The words stay reachable at `/warrant/receipt`.
- Every other code and date answers `status` `unknown`, `reason_code` `no_cms_statement_held` and `applies` null. Verzi no longer maps a payment category, an HCPCS descriptor or a code letter to a verdict, so `proxy_only` no longer occurs.
- The basis still lists CMS facts in force on the date: the payment category as the fee file states it (no Verzi label) and the PIM chapter 5 section 5.2.6 refill rule. Removed: `basis[].label`, `basis[].tiebreaker` and the exemption's `requirement_text`.
- The confidence rollup does not weigh an applicability block with `no_cms_statement_held`, as it already does for the exclusion warrant's `none_served`.
- In `/rules/warrant/changes`, the applicability block's `changed` is `null` unless both dates carry a cited verdict. A block with no CMS statement held on either date does not count toward `any_change`.
- `GET /archive/files/{sha256}/text` no longer serves the text of an archived HTML page or PDF, such as a CMS manual chapter, a transmittal or a DME MAC page. The text rendering of these files can put the publisher's words out of order. The answer has `text: null`, `text_withheld: "rendering_not_in_document_order"`, the file's receipt and the link to the original bytes. CFR XML text still serves.
- `GET /warrant/receipt` serves no passage from those same files, for the same reason. It answers `found: false` with `withheld: "rendering_not_in_document_order"`. Receipts for LCD and Article passages are unchanged.
- The KB code page no longer shows the whole text of an in-force LCD or Article with no approved label, for the same reason. It shows the version, its receipt and CMS's copy. The fidelity gate checks the approved passages against CMS's word order, so they still show.
- **A new capture of a Medicare Coverage Database page whose policy sections are unchanged no longer adds a version.** The bytes are still stored. Only page furniture (the version-history panel, help text, scripts) had differed. Two captures of one version that already exist are kept, and the later one serves.
- **A Stripe subscription turns the paid plan on and off.** When any of an org's Stripe subscriptions is active or trialing, the org's paid flag is on. That flag opens the KB paid view today; the signed-answer gate follows in a later change. When none is, past_due or incomplete keeps the flag, and canceled, unpaid, incomplete_expired, paused or deleted turns it off. A discounted subscription counts the same as full price. Only the org whose Stripe customer matches changes, so a manual grant on another org stays. Every change is written to the org access audit. When Stripe cannot be read, nothing turns on: the webhook answers 503 and Stripe sends the event again.
- **Checkout accepts a promotion code.** The Stripe checkout page for Starter and Pro has a promotion code field. A discounted subscription turns on the paid plan the same as full price.
- **The plan tier follows Stripe's prices.** When a subscription ends, the org's tier is the highest tier among its remaining active, trialing or held subscriptions, read from their Stripe prices. With none left, the tier is free. A price that maps to no tier reads as Starter, never above the org's current tier. An Enterprise tier is not lowered while a subscription is still held. A checkout never sets a tier below another active or held subscription.
- **The docs home says the TypeScript SDK is available to design partners on request.** The sample no longer reads as if `@verzihealth/sdk` were published. It is not on npm yet.
- **`GET /rules/dmepos/fee`: an area string the fee schedule does not carry is a 422 that lists the valid strings.** Before, `cba=Salt Lake City` answered 404 "No fee cell for that key and date", while the file carries `Salt Lake City, UT`. Now a `state` or `cba` value that no fee cell carries answers 422. The detail has `message`, `valid` (every area string the fee cells carry for that area type, sorted) and `nearest` (the valid strings that contain the given one or are contained by it, case-insensitive), when any exist. A lower-case state such as `ut` still passes.
- KB code pages (`/kb/code/{hcpcs}`) load faster for a visitor with no cookie and no API key. The page is kept for up to 5 minutes for each code, date of service, LCD and version. The page is kept per day (US Eastern), so a new day shows that day's answers. A signed-in reader, a request with an API key, and a request with any cookie always get a fresh page.
- KB code pages send a `Server-Timing` header with the milliseconds of each build stage.
- **The free E0601 page is served from a 5-minute cache.** A visitor with no KB sign-in and no API key gets the page from the cache when the URL has only `dos` and `vid`. A new CMS version shows within 5 minutes. The response carries `X-KB-Cache: hit` or `miss`. Signed-in readers and keyed callers always get a fresh page.
- **"Today" is no longer the server's UTC date.** `/v1/check` with no `dos` uses today's US Eastern date and says so: `date_of_service_source` is `default_today_us_eastern` (or `given`), and the page reads "(today, US Eastern time)". A KB page says which date "today" is. It renders for the US Eastern date, and a browser with another local date moves to its own date. "Generated" times show in the reader's local time. The change feed's "Takes effect" titles and the upcoming-changes calendar use the US Eastern date too; feed timestamps stay UTC with their zone.
- **The signed answer needs a paid plan.** `/rules/warrant`, `/rules/warrant/changes`, `/rules/warrant/appeal`, `/warrant/receipt`, `/provider/{npi}/standing`, `/rules/ncci/ptp`, `/rules/ncci/mue` and `/rules/dmepos/fee` (also under `/v1`) need a paid plan or a granted entitlement: the key's own flag, or an active organization with the plan. Without one they answer 402 with `error: capability_required` and an upgrade link. The source archive, as-of reads, the catalog, the public code check and `/rules/warrant/signing-key` stay free. A paying organization's members also skip the free tier's row cap.
- **KB: the free E0601 page and search.** A sample date chip names its document ("Article A52467, version 35"), and on the free page it stays on the free page. A search for E0601 by a reader without the KB plan opens the free page. The KB sign-in page offers a path for a reader who is not a customer: the free E0601 page, a free API key, or a demo.
- **The refill applicability warrant reads the HCPCS descriptor from the file in force on the date of service.** Before, it read the newest HCPCS file. The IN-category tiebreaker now cites the quarter file in `basis[].tiebreaker.descriptor_source` (document, quarter, version, member file, sha256). When no descriptor resolves, `descriptor_reason` says why: `before_first_held_file` (before 2015), `no_file_in_force` (2021 Q4), `file_in_force_not_parsed` (with `unparsed_version_id` and, when its zip holds its own code table, `unparsed_member`) or `code_not_in_file_in_force`. A changed file not parsed yet is never skipped for an older copy. The descriptor text is still not served.
- The `/check` page and JSON, and the KB code page, still show the current HCPCS descriptor. They now name its file: `descriptor_quarter`, and on the check page the note "Current HCPCS file (2026Q4), not the file in force on the date of service." For a code CMS has dropped, the note says "Last HCPCS file listing this code (2023Q2); the current file is 2026Q4."
- **A request with no API key gets a 401 that says what to do.** A keyed route called without `X-API-Key` used to answer FastAPI's raw 422 validation body. It now answers 401: "A Verzi API key is required. Send it in the X-API-Key header. Get a free key at https://docs.verzi.health/signup." A request with other validation errors still gets the 422.
- The archive's `/as-of?date=` answers carry that date in `meta.as_of`, not "current".
- The docs samples call `/v1/provider/{npi}`, not the missing `/v1/npi/{npi}`.
- **The API's pages say Verzi Health API, not Verzi Healthcare Data API.** This covers the OpenAPI title, the docs, status, sources, accuracy, usage and resources pages, the quickstart, the data disclosure statement and the code samples. The privacy policy and terms of service keep the old name until their legal text is updated.
- **The MCP server's instructions no longer say that the signed answer decides whether a claim is supported.** They now say what it returns. Each block names its source file and the version in force on the date of service. Otherwise it gives a status (unknown, not_found or not_requested) and, where known, the reason. A requirement found in CMS's text carries source proof. The signed-answer tool does not decide whether a claim is supported, covered or paid.
- **Signed-in KB readers with a plan are not counted by the KB page limit.** A KB session or an API key with the KB plan reads `/kb/code` and `/kb/free` without the 30-a-minute per-IP limit, so a team behind one office address does not share it. The check spends no daily request. A cookie or key that is not valid or has no plan still counts.
- **The public code check says when a source could not be read.** `/check/{hcpcs}` and `/v1/check/{hcpcs}` no longer leave out a source that failed. When the documentation requirements fail, `documentation_requirements.status` is `unknown` (it was `unavailable`) with `reason` `source_unavailable`. When the Required Prior Authorization List fails, `prior_authorization` is present with `status` `unknown` and the same reason (it was left out). Each LCD gains `in_force_status` and `in_force_reason`; a failed version lookup says the version in force could not be read. The page shows a "could not be read" line for each. When the list was read but cannot answer, `prior_authorization.reason` is the list's own cause (`no_version_in_force`, `not_parsed`, ...). Otherwise `reason` is `null`. The quickstart sample response shows the new keys.
- **A busy service answers 503, not 500.** When no database connection comes free within 3 seconds, the API answers `503` with `Retry-After: 5` and `{"error": "service_busy"}`. Retry after the stated delay. A database query that runs longer than 30 seconds now ends with an error.
- **The KB code and free pages allow 30 requests a minute per IP.** `/kb/code/{hcpcs}` and `/kb/free/{hcpcs}` share one counter of 30 per minute per IP, on top of the general limit of 120. Above it they answer `429`. Every `429` from a public per-IP limit now carries `Retry-After`, the seconds until the window resets. Crawlers stay allowed.
- **`GET /vbc/reach` and `GET /vbc/ngaco` page their lists.** Both take `limit` (default 50, max 500) and `offset` and answer `{total, count, limit, offset, results}`. Before, both ignored `limit` and returned every row. `GET /vbc/reach/{entity_id}` already paged its participants with `provider_limit` and `provider_offset`.
- **`GET /provider/{npi}` reads Open Payments and answers by year.** The dossier gains an `open_payments` block (records, payments, the general plus research amount as `total_amount`, the ownership interest value apart, the split by payment type, manufacturers, one row per program year) and `data_available.open_payments`; the docs had listed Open Payments but the code never read it. `prescribing` and `utilization` carry `by_year` rows beside their totals. The whole dossier now runs on one database connection, so a busy pool no longer fails it five times over, and a dossier slower than 5 seconds logs the time of each block.
- **`/sources` marks a source stale when the publisher lists a newer release.** Each yearly source (Open Payments, Part D Prescriber, Physician Utilization, HCRIS, MA Stars) now carries `latest_release_available`, `latest_release_loaded` and `release_checked_at`. A source whose publisher lists a newer release than the newest loaded one shows `stale` with both releases in `freshness.detail`, whatever the clock says. The pipeline asks the publishers once a day. A newly listed year loads once it is listed in the registry, after its layout is checked.
- **The KB index says how many documents are in force and how many are retired.** The line above the table reads "151 LCDs and Articles (143 in force today, 8 retired)" instead of the total alone. `docs/CORPUS_COUNTS.md` defines each corpus count once.
- **Design partner contact mail goes to both inboxes.** `POST /contact` with interest `partner` or `design_partner` sends one email to robert@verzi.io and adrian@verzi.io. The subject is "Verzi design partner request". Other interests still go to `CONTACT_TO` (default adrian@verzi.io). `CONTACT_TO` and `CONTACT_PARTNER_TO` may list several addresses, separated by commas. No Railway change is required for the default.
- **The quickstart's first example is a DME code check.** The curl, Python, and JavaScript samples call `GET /v1/check/E0601?state=CO&dos=2026-08-15`. The sample response is a real free-key answer: coverage `lcd_listed`, LCD L33718 version 52, and `signed_receipt.included` false. The signed receipt comes with a paid plan or a design partner plan.
- **The public code check counts the KB's approved labels.** `/check/{hcpcs}` and `/v1/check/{hcpcs}` count the approved passages on the version in force (the KB's label layer, quoted from CMS), not the older requirement rows. Where labels are proposed but not yet approved, the page says "N proposed requirements, under coder review." `documentation_requirements` gains `pending`. Counts only: no passage text.
- **Signup shows the key only after an email code.** `POST /signup` no longer shows a key on the page. It emails a 6-digit code to the address, and the key appears once, after you enter the code at `/signup/verify`. Free-mail providers (Gmail, Yahoo, Outlook, Hotmail, iCloud, AOL, Proton, GMX, Mail.com, Yandex, Zoho, Fastmail, Hey, Tuta, QQ, Comcast and the country variants of the first group) are refused: use your work email.
- The free organization daily limit is 10,000 again. The free per-key limits stay 1,000 requests a day and 100 rows a request.
- **A manual chapter revision is dated by its transmittal's Effective date.** `GET /archive/docs/{doc_id}/as-of` for a CMS manual chapter (Claims Processing, Program Integrity, Benefit Policy, NCD manual) now starts a revision on the Effective date of the transmittal it names, not on the Implementation date. This moves 216 of 792 revision dates, almost always earlier (median 60 days); the Implementation date is kept when the Effective date is missing, conflicting, or more than 365 days before the transmittal's issue date. The answer carries `effective_from_source` (`transmittal_effective` or `transmittal`) and, when the Effective date precedes the transmittal's issue date, `transmittal_issue_date`.
- **A manual chapter answers with its highest Rev in force, and says when a lower Rev is still ahead.** `GET /archive/docs/{doc_id}/as-of` for a CMS manual chapter now returns the highest Rev that has started on the date of service. The transmittal dates are not in Rev order (82 chapter version pairs on 2026-10-14 started in the wrong order, 114 with the Effective dates), so a lower Rev could answer after a higher one. New field `inverted_window`: set when a lower Rev starts after the date of service. It names the answering Rev and each such lower Rev with the transmittal's issue, effective and implementation dates and its own wording quoted from the parsed transmittal. Null otherwise.
- **The rest of the public pages use the Ledger brand.** Quickstart, data disclosure, terms, privacy, signup, and the other public pages that used the midnight theme now share the docs landing's ink, paper, green, and gold, IBM Plex, the Ledger lockup, and the dark-mode toggle. Marketing copy says receipt and signed answer. The terms keep the legal phrase "represent and warrant."
- **A work email gets a free API key immediately.** Gmail, Yahoo, Outlook, Hotmail, iCloud, AOL, and Proton are still reviewed by hand. The message on a key that is waiting no longer says approval takes usually within 1 business day.
- **The free tier is 1,000 requests per day and 100 rows per request.** Both numbers come from one setting (`FREE_DAILY_LIMIT` and `FREE_ROW_CAP`). The signup page, the quickstart, and the pricing page use them.
- **Developer docs use the Ledger brand.** The docs landing page uses ink, paper, green, and gold, IBM Plex, and a dark mode that follows the system and a toggle. Public copy says receipt and signed answer. The favicon is the Ledger mark, and the social cards are redrawn in the same palette.
- KB pages (right margin): Requirements at a glance on a code page lists every approved passage for the code on the page, from each document the page shows (the LCD first, then the other documents that hold the code), grouped by document and version, then by kind; a click opens its quote. The version line names each companion Article version in force on the LCD version's start date, linked when the KB holds its page. The receipts table lists the page's own document only; one line links each related document's own receipts.
- KB Copy + cite copies CMS's words only. Words Verzi writes inside the policy text (the section headings Verzi names, table column names, N/A) are left out, with a "[...]" line where they were; the citation names each section the quote spans. Paragraphs and list items end with a line break and table cells with a tab. Copy + cite refuses a selection in several pieces when CMS text lies between them. A browser that splits one drag around the section toggles still copies it as one quote.
- **KB links now name `kb.verzi.health`.** The `kb_url` field in the archive and as-of JSON, the updates feed's KB links, and the links in watch alert and digest emails use `https://kb.verzi.health/kb/...`. Old `api.verzi.health/kb/...` links keep working. Each KB page names its `kb.verzi.health` address as canonical in a `Link` header (with only the version and date-of-service parameters), and the KB sign-in pages carry `X-Robots-Tag: noindex`.
- **NCCI PTP answers for dates before 2022 cover HCPCS Level II pairs only.** For a date of service before 2022-01-01, `GET /rules/ncci/ptp`, the edits block of `GET /rules/warrant` and `GET /rules/warrant/appeal`, and `GET /rules/warrant/changes` answer a pair only when both codes are public HCPCS Level II (first character A to V, not D). A pair with any other code, such as a CPT code or a D code (CDT), answers `unknown` (`edited: null` on `/rules/ncci/ptp`) with a reason. On `/rules/ncci/ptp` and the edits block, `checked.rule` is `license_scope`. No answer changes today: Verzi holds no PTP file read for a date before 2024-04-01 yet.
- **NCCI MUE answers for dates before 2022 cover HCPCS Level II codes only.** For a date of service before 2022-01-01, `GET /rules/ncci/mue`, the MUE part of the edits block of `GET /rules/warrant` and `GET /rules/warrant/appeal`, and `GET /rules/warrant/changes` answer only for a public HCPCS Level II code (first character A to V, not D). Any other code, such as a CPT code or a D code (CDT), answers `unknown` (`mue_value: null` on `/rules/ncci/mue`) with a reason. In the edits block, `checked.rule` is `license_scope`. This changes answers today: Verzi has read the 2020Q1 to 2021Q4 tables, and CPT and D codes in the practitioner and outpatient hospital tables for those quarters now answer `unknown`. The DME supplier table holds no such codes, and a HCPCS Level II code such as E0601 answers as before.
- **A KB page says when the version shown is not the one in force today.** A quiet banner sticks under the top bar: "You are viewing version 20 (2020-01-01 to 2021-08-07). Version 52 is in force today.", with the in-force version as a link when the KB holds its page. It shows for an older version, a version not yet in force, and a document with no version in force today; it does not print. CMS's quoted revision note at the top now shows only on the version in force today; CMS's full log stays in the revision history section.
- **KB receipts are built for an auditor.** The "Receipts for auditors" table reads Document, Version, In force, Archived on and Verify. Verify links CMS's own copy of that version and Verzi's archived file. The table carries no hash. The table lists the page's own document only; one line links each related document's own receipts. Every small receipt toggle on the page is now a "source" toggle with the same two links. A new page, `/kb/file/{sha256}`, shows one archived file: its document, capture time and URL, size, sha256 fingerprint and how to check it, the structured rendering's sha256 and the archive file id. A signed-in KB account gets a one-hour download link for the original file there; an anonymous reader gets a sign-in link, never the download. Files from sources whose license carries CPT descriptors (the AMA license) offer no download. The appeal exhibit keeps the full receipt under each passage.
- **KB policy and code pages end with the main template's bottom sections.** Under the text, each page shows its version history (one box per version, "v20" over its window, with a button that shows the versions superseded the same day they took effect), then CMS's revision history as its own open section, then one "Receipts for auditors" box. A receipt line under the title names the version shown and its dates. On a code page, the free list of in-force documents no longer repeats the LCD the page shows, a signed-in customer gets those documents' text without a second list, and CMS's code-to-policy list moves into the receipts. A document version with no stored text rendering now says so, instead of a hash failure line.
- **A KB code page now holds its policy, in the main template.** `/kb/code/E0601` shows the code large, the LCD's title, CMS's descriptor, and then the LCD itself: the DME MAC jurisdiction row, CMS's code list, the revision note, the date of service picker, the version chips, the sections, the customer line and the receipts. The picker and the chips reload the code page, so one date drives every block. When CMS lists the code in more than one LCD with a KB page, chips switch between them (`?doc=`). A signed-in customer gets the LCD's verbatim text with its highlights, and below it the approved passages and CMS text of the code's other documents, such as its Article. A version id from another document shows the newest version and says so. A code with no LCD page keeps the old page. The policy page `/kb/lcd/{num}` is unchanged.
- **The applicability warrant answers faster on a cache hit, and fails closed on rendering drift like `w3` already does.** It now reads a document's text rendering through `w3`'s existing cache, instead of fetching it from the store, unchecked, on every request. A stored rendering that no longer matches its recorded hash now leaves that basis without a span or quote instead of quoting a span from drifted text. No field or shape changed otherwise.
- **The `w3` and `applicability` warrant blocks answer faster.** Both resolve a code's governing documents (an LCD and its Article, commonly two) in one batched as-of lookup instead of one query per document. Response bodies are byte-identical; no field or shape changed.
- **The warrant endpoint answers faster.** `/rules/warrant`'s seven to eight blocks (`w0`, `w1`, `w2`, `w3`, `exclusions`, `applicability`, `prior_authorization`, and `w0_ordering` when `ordering_npi` is set) run concurrently instead of one after another. They are independent reads; no field or shape changed. Measured on the five demo calls: 44 to 60% faster (for example 12.8s to 5.1s).
- **W2 PTP answers from CMS's PTP file in force on the date of service.** `/rules/ncci/ptp`, the warrant's `w2.ptp` block and the change timeline read the complete quarter file for the date's quarter, from the change-only store, and name it in `checked` (rule `ptp_edition_in_force`) and in `as_of.version_in_force`. Before, the newest held file answered for every date and the citation always named it; a pair CMS dropped from a later file answered `not_edited` for dates on which the edit was in force, and a date before the first held file answered from a 2026 file. A date whose quarter has no complete file now answers `edited: null` with a `reason` that names the quarter and the nearest files read; no other quarter's file answers for it (on 2026-10-09 the files read are 2024Q2 to 2025Q3 and 2026Q3 to 2026Q4, both settings). A `not_edited` answer cites the pair's newest row, with `present: false` and `recorded_in` when CMS's file no longer lists it. The fact's `citation` names the archived part file whose bytes carry the row (the file that recorded it, never a newer one).
- **Codes are checked and upper-cased on every rules and warrant route, and on the MCP warrant tools.** A code is five characters, digits or capital letters. `hcpcs=e0601`, `code_a=e0601` and `modifier=rr` now read as `E0601` and `RR`; before, the fee route answered 404 for a lowercase key. A value that is not a code (`1312`), or a code paired with itself (`code_a=13121&code_b=13121`, `companion` equal to `hcpcs`), is a 422; before, PTP answered `edited: false` for them.
- **The fee route states what 0.0 means.** `/rules/dmepos/fee` and the warrant's `w1` serve the file's own ceiling and floor; CMS's DMEPOS README says the fields are filled with zeros for codes adjusted using competitive bidding information, so `0.0` is CMS's value (E0601 RR), and `null` means the file leaves the cell blank or the area is not subject to them.
- **`/kb/metrics` serves its cached numbers at once and refreshes them in the background.** The first request after a deploy no longer waits about 20 s: the cache fills at app start, and an expired value serves while one refresh runs (`meta.stale`, `meta.refresh_error`).
- **W3 serves the in-force LCD or Article verbatim, marked unlabeled, when no approved label is in force.** A missing label no longer makes `warrants.w3` answer `not_yet_available`. Each in-force document now carries `label_status` (`labeled` or `unlabeled`). An unlabeled document carries its version, its title, and a `receipt` (archived file, sha256, rendering sha256, fetch time, publisher URL). `warrants.w3` never carries the document text. Entitled KB readers and keys get `document_text`, CMS's full text for that version, from `/kb/code/{hcpcs}/passages`, served only when it matches its stored hash (else `text_available: false`). `coverage.completeness` is `unlabeled` when no approved label serves, and `coverage.documents_unlabeled` counts these documents. The confidence rollup grades an unlabeled W3 as `partial`. The KB code page lists the in-force documents with their receipts; signed-in customers get the full CMS text.
- **The sample integration page needs a signed-in KB session.** It moved under the KB path (the KB sign-in reaches it) and shows only to readers whose plan includes the warrant; anyone else gets 404. The old path is gone.
- **Private beta wording on the docs landing and signup.** Badge says "private beta" instead of "self-serve keys"; signup says "Free key on request" instead of "Self-serve key"; the quickstart notes that keys are approved by hand during the beta.
- **Footer updated across all public pages.** The copyright line is now "(c) 2026 Verzi"; the verzi.health pages carry "Rules data, not PHI" and the provider-data pages (/sources, /platform/stats, /accuracy, /status, /my-usage) carry "Public data, not PHI".
- **The free tier card on the docs landing now lists archive and catalog items** instead of "Provider intelligence lookups and aggregates", matching the signup page.
- **CPT rationale blocklist comparison is now case-insensitive and whitespace-tolerant.**
- **The KB's receipts table shows the companion Article under each LCD version.** For every Article CMS relates to the LCD, a row under each LCD version names the Article version in force on that LCD version's stated start date (the as-of rule the warrant uses), with its window, a link to that version's page and the sha256 of the archived bytes. An LCD version with no stated start gets no row.
- **The KB opens on a code search.** Type a HCPCS code or the start of one at `/kb`: the page lists the codes that an LCD lists in CMS's own code-to-document table, with CMS's descriptor, and each code links to its page with the policy documents. The document table stays below it.
- **A KB code page shows no descriptor for a code that is not public HCPCS Level II.** A D code (`/kb/code/D0120`) is CDT, the ADA's licensed text. Its page says so in one line and does not show the descriptor. Public HCPCS Level II pages are unchanged.
- app.verzi.health now opens the KB sign-in. The provider-data dashboard stays at www.healthcaredata.io.
- Responses on api.healthcaredata.io no longer carry Deprecation or successor-version Link headers. healthcaredata.io stays the home of the provider-data product.
- **Pricing is by contact during the private beta.** `/pricing` shows one line: contact adrian@verzi.io for access and pricing. The page is out of the sitemap and marked noindex. `/my-usage` shows the same contact line to Free and Starter accounts instead of prices and a checkout button, and the quickstart's rate-limit note points to it.
- **KB customer log in.** KB pages no longer ask you to paste an API key. "Customer? Log in" opens `/kb/login`: enter your work email, then the 6-digit code we email you. The session lasts 14 days. Signed in, the page shows your organization and a Log out link, and loads the verbatim text when your plan includes it. Access is by organization: when the organization's plan includes the KB paid view, every member reads the verbatim text. API clients keep the `X-API-Key` header on `/kb/{kind}/{num}/v/{ver}/sections`; a key with the paid view still works when the browser session's plan does not include it. The login email is matched without regard to case, and Log out is a button (a POST), not a link.
- **Dashboard login signs in approved accounts only.** The dashboard login no longer creates an account for a new email. It sends a code only to an existing, approved account, and it gives the same answer for every email. Get an account at `/signup`. Dashboard sessions last 14 days; earlier sessions end and need one new sign-in.
- **One account per email, in any case.** Signup and the dashboard login store emails lowercase and find an existing account without regard to case: `Pat@Example.com` and `pat@example.com` are the same account.
- **Sample integration page: CMS's words for the rate types.** The rate-type legend now quotes CMS's own Transparency in Coverage schema definitions, linked to the pinned schema file. Receipts name their documents in words ("CMS DMEPOS fee schedule file, quarter 2026Q4") beside the file hash.
- **No empty sample box.** A KB code page with no approved label for the code shows no "Sample passage" box at all; the box appears with the first approval. Where approved passages exist but none is in force on the chosen date, the page still says so. On the one-code-every-payer sample page, the empty policy-passage cell reads "Not held yet" like every other empty cell.
- **Sample integration page in plain words.** A fee cell where CMS's file prints 0.00 reads "CMS's file lists 0.00 for this cell" instead of $0.00. The commercial payer's rate ranges say how many rows its own file publishes at 0.00 ("The payer's file lists 0.00 for 404 rows"). The edit cell names the MUE adjudication indicator (MAI) in CMS's own term. Rate types keep their published names, with a one-line legend. The Medicare check on commercial providers reads as three short sentences. Raw CMS column names and internal version ids move out of the cells; receipts keep the column names behind the "receipt" toggle.
- **KB pages say whose code list it is.** LCD pages list the codes CMS's own LCD code table names for the current version ("Codes CMS lists in this LCD"); Article pages, for which CMS publishes no DME code table, label their list as Verzi's coverage-label mapping. Receipts under quotes and lists now sit behind a "receipt" toggle. A governing LCD outside the Verzi KB links CMS's own page and says so. On a date-of-service view, the version-in-force line sits directly above the note it explains.
- **Code pages list governing policies as CMS lists them.** `/kb/code/{hcpcs}` now names every LCD whose current version lists the code in CMS's own LCD code table (from the Medicare Coverage Database download), with CMS's description of the code quoted, the group and version it sits in, the Articles CMS relates to each LCD, and the file's sha256 as the receipt. HCPCS Level II only. Nothing is inferred, and the list refreshes with each weekly capture. Code pages also answer faster (cached lookups).
- **Clearer code-page and receipt wording.** The prior-authorization line labels CMS's Effective Date cell as a quote. A code with no approved label keeps the "unlocks when the first label is approved" line on every date. Receipts drop the renderer tool string and state plainly when CMS gave a version no dates.
- **Status page reasons in plain words.** A failing source shows one fixed sentence for its state ("The latest run failed.", "The latest run found no files.", and so on) and the run date, never the raw error text. Raw errors stay in `GET /v1/archive/runs`. Empty status tiles read "not reported" instead of a dash.
- **Watch-a-code alert subjects use the document's headline**: "E0601: Positive Airway Pressure (PAP) Devices changes 2026-11-01", the same headline rule as the feed.
- **KB policy pages lead with CMS's newest note.** The top block quotes the selected version's own revision note. On the newest version without its own note, it quotes CMS's newest note on file and names the version it belongs to. The "last changed" date comes from that same entry, so the two agree. Where no note is extracted, the page says exactly that instead of claiming CMS wrote none.
- **CMS's revision history and the related documents come before the key prompt.** The full revision history is its own section above the "Already a customer?" line, no longer inside the receipt. LCD pages list the Articles CMS relates to them, and Article pages list their LCDs, from CMS's own related-documents file. Each links to its KB page where one exists.
- **The "open on CMS's site" link works.** For versions captured from the Coverage API, the receipt links the public Medicare Coverage Database page; the API address (which needs a key) is shown as text.
- **Version links are exact.** KB version chips link each captured version by its archive id, so two captures of one CMS version, or a version CMS did not number, each open their own page. The verbatim-sections response names the version it actually served.
- **KB index:** documents without CMS dates say so, titles show CMS's characters ("/", not "&sol;"), and the count says which documents have archived history pages. Version chips read "version 48".
- **The archive page opens on a worked example** (every version of LCD L33718) instead of an empty box.
- **Status page platform numbers read as labels**, not a raw JSON block.
- **Plain words on free pages.** "backfill" reads "pulled from the past" and "wayback" reads "Wayback Machine copy" on the feed and in the digest. Feed chips are keyboard buttons. Pages carry a favicon.
- **A colon, never a dash, in every headline and subject.** "Takes effect 2026-10-30: Claims Processing Manual transmittal R13954" on the feed, calendar, digest, alerts, and RSS; alert subjects read "{code}: {document} changes {date}". The Order and Referring file reads "Order and Referring", CMS's own dataset title.
- **Readable names for regulatory entries.** Price Transparency, No Surprises Act, Interoperability and Prior Authorization, and 42 CFR entries now carry a label from CMS's own program name and the publisher's URL slug. `pt:hospital-price-transparency-enforcement-updates` reads "Price Transparency: hospital price transparency enforcement updates", and `cfr:42-414` reads "42 CFR Part 414". The label comes from the id only, never from content, and an unmapped id keeps its raw form.
- **The free surfaces, to spec.** Every change surface now shares one entry anatomy: headline (a future effective date leads, bold), gain line (document reference, version, effective date, "cited by warrants for" the mapped codes), the publisher's revision note quoted and labeled or the plain statement that none was published, then a collapsed receipt with the KB and CMS source links. Applies to the updates feed, the calendar, the weekly digest, the watch-a-code alerts, and RSS.
- **Filter chips and the pipe rule.** The feed defaults to all sources with chips (All / Claim policy / Fee schedules / Code sets / Regulatory). Notification pipes (digest, watch-a-code, webhooks) carry claim-policy and fee-schedule sources only; regulatory overlays, code sets, and provider files never ride in an inbox, whatever the subscription says. Regulatory entries on the feed say so.
- **Calendar, date first.** `updates.verzi.health/calendar` leads with the effective date, claim-policy sources only, the quoted note under each item, and an "Add to my calendar" export (`GET /v1/archive/upcoming.ics`).
- **Status page freshness.** `status.verzi.health` adds a per-source freshness table: every archived source with its last capture, cadence, and a fresh or stale pill. Stale is shown as plainly as fresh.
- **The engineer door says so.** `GET /v1/archive/docs/{doc_id}/as-of` now carries a notice naming itself a machine surface, with the KB page link for the readable version. Payload otherwise unchanged.
- **KB doc pages lead with CMS's words** (the approved free-surfaces layout): title, one-line identity (versions, span, last-changed date), then the version's revision note quoted and expanded; the version strip and a one-sentence section summary follow; the API-key box is a one-line "Already a customer?" link after the free content; receipts collapse behind one toggle at the bottom. When CMS published no note, the page says so plainly. Code pages show the sample passage before anything else asks for a key.
- **Readable names for transmittals and MLN Matters entries.** Feed, calendar, digest, and RSS entries derive a human label from CMS's own id structure (the transmittal suffix names the manual it amends): `transmittal:r13954cp` reads "Claims Processing Manual transmittal R13954". Metadata only, never content; anything unmapped keeps its raw id.
- **Policy documents first.** The updates feed defaults to entries with document titles or revision notes (the DME policy corpus), with counts for both views; "All sources" shows every observed version. The digest lists policy sources first.
- **KB pages wear the shared brand.** `/kb` pages now use the one site component (light default with a persisted theme toggle, standard nav, brand type). Serif is reserved for verbatim document text: passages, the sample quote, and CMS's revision notes; interface text is sans. No words changed; the fidelity gate's guarantees are untouched.
- **One brand across the public pages.** The status, archive, updates, calendar, and subscription pages now share one brand component: light theme by default, dark via the toggle in the nav (your choice persists per browser), Reddit Sans interface, turquoise accent, and a serif reserved for verbatim document text. Same nav row everywhere, now including the Coverage KB.
- **Corpus metrics on the status page.** `status.verzi.health` now leads with the Medicare DME corpus numbers, read live from `GET /kb/metrics`: codes answering, policy documents and versions, the fidelity percentage with its section counts, archive depth, A/B MAC captures, and Medicaid states tracked.
- **Revision notes on the feeds.** Where the renderer extracted one, every change on `updates.verzi.health`, the calendar, the weekly digest, the watch-a-code alerts, and the webhook events now carries the publisher's own revision note verbatim (`revision_note`, with `rev_number` and `rev_date` where stated). Null when absent; never a paraphrase.
- **Readable updates feed.** Each entry on `updates.verzi.health` now leads with the document title, the effective date, and the HCPCS codes the coverage layer maps to the document, beside the version, origin, and sha256. `GET /v1/archive/changes` carries the codes as `affected_codes` on every change row (empty when no mapping exists). The entries stay machine-observed; the titles and dates are the publisher's own words from the archived bytes.
- **Pending keys say so.** A key awaiting access review now gets a 403 that says it is pending review, instead of the generic "deactivated" message. (Signup has been approval-gated since the private-beta lock; access requests are now reviewed in the admin portal.)
- **Source archive now holds A/B and HHH MAC coverage documents (all jurisdictions).** `mcd_ab_mac` adds 4,569 archived LCDs and Articles (29,237 dated versions, 2015 to 2025) alongside the DME MAC set, so `GET /archive/docs/{doc_id}/as-of` resolves the local coverage document in force on a date of service for A/B and HHH jurisdictions too. Proposed and draft versions are archived but carry no effective date (they were never in force). Older CMS transmittals back to 1999 were also backfilled, so more manual-chapter revisions resolve a stated effective date.
- **DAC, PECOS reassignment, and NPPES refreshed** after 66 days stale — DAC to the 2026-07-31 file (2.01M rows), reassignment re-derived (1.77M), NPPES to the August file (9.73M providers, now including Authorized Official name/title/credential on 1.96M organizations). `/sources` freshness reflects the reloads.
- **Source freshness now distinguishes "no refresh commitment" from "late"** — sources with no expected rhythm (hand-curated mappings, static reference crosswalks, as-needed enrichment, declared-but-not-yet-live loaders) previously fell back to monthly staleness thresholds and showed permanently stale badges at `/sources`. They now report an explicit `exempt` freshness with the reason, and never degrade `/status`. A genuinely late scheduled source still turns `/status` amber or red exactly as before.
- **`ca_medical_board` source entry declares its real cadence** — the entry is NPPES-derived (no state loader exists yet) but declared weekly cadence, so it drifted to "critical" by construction and kept `/status` red for two months. Now monthly, matching the NPPES reload cycle that actually feeds it; the notes field says so explicitly.
- **Hospital Financial & Regulatory Deep-Dive** — a coordinated set of additions closing several hospital financial/regulatory gaps in one pass. See `docs/HOSPITAL_FINANCIALS.md` (published at `/resources/hospital-financials`) for the full writeup.
  - **HCRIS deep worksheet parse: balance sheet, charity care, cost centers, DSH** — the HCRIS loader previously extracted 8 numeric coordinates from the same NMRC file we already download quarterly; it now extracts across four more worksheet domains. New tables: `hcris_balance_sheet` (Worksheet G — full balance sheet with derived current/quick/debt-to-equity ratios), `hcris_uncompensated_care` (Worksheet S-10 — charity care charges/costs, bad debt, total uncompensated care), `hcris_cost_centers` (Worksheet A + C Part I — charges, total costs, and salary/non-salary split for ~20 named cost centers plus all other reported lines). New `hcris_reports` fields: `operating_income`, `other_income`, `total_salaries`, `dsh_patient_pct`, `dsh_payment_amount`, `medicare_days`/`medicaid_days`/`medicare_discharges`/`medicaid_discharges` (populating the previously-unused `medicare_days_pct`/`medicaid_days_pct` columns). New endpoints: `GET /cost-reports/{ccn}/balance-sheet`, `GET /cost-reports/{ccn}/charity-care`, `GET /cost-reports/{ccn}/cost-centers`. Every coordinate was empirically validated by reconciling internal worksheet subtotals and cross-checking against a real hospital's published financials — every headline figure matched exactly or within trivial rounding.
  - **Hospital Readmissions Reduction Program (HRRP) penalties** — new `hrrp_penalties` and `hrrp_measures` tables loaded from the CMS IPPS Final Rule HRRP Supplemental Data Files (FY2021–FY2026). Three new endpoints under `/vbc/`: `GET /vbc/hrrp/{ccn}` (payment adjustment factor history), `GET /vbc/hrrp/{ccn}/measures` (per-condition excess readmission ratios for AMI, COPD, HF, pneumonia, CABG, THA/TKA), and `GET /vbc/hrrp` (search/rank hospitals by penalty severity). `payment_reduction_pct` is always derived from the payment adjustment factor rather than trusted from CMS's own column, whose unit is inconsistent across fiscal years in the source files.
  - **Case Mix Index (CMI) and wage index** — new `ipps_impact` table loaded from the CMS IPPS Final Rule Impact File (FY2023–FY2026). New endpoint `GET /hospitals/{ccn}/case-mix-index` returns the full fiscal-year history (CMI, transfer-adjusted CMI, wage index, DSH patient percentage, resident-to-ADC ratio); `GET /hospitals/{ccn}` now includes a compact `case_mix_index` block with the latest year. CMI is always resolved to the current MS-DRG grouper version for that fiscal year, so a schema change isn't needed when CMS increments the grouper annually.
  - **Hospital service lines, accreditation, and teaching status** — CMS Provider of Services (POS) file now feeds a new `facility_services` table (~50 curated clinical service-line flags — cardiac cath lab, open heart surgery, NICU, transplant services, imaging, behavioral health, and more) plus new `facilities` columns for accreditation agency/dates and medical school affiliation. New endpoint `GET /hospitals/{ccn}/services`; `GET /hospitals/{ccn}` now includes `accreditation_agency`, `accreditation_expiration_date`, and `medical_school_affiliation`. Accreditation-organization and teaching-status code labels were empirically validated (not pulled from an official crosswalk, which wasn't accessible) against known accreditor market share before shipping — unverified codes are stored raw rather than guessed. `scripts/enrichment/enrich_from_pos.py` gains two new always-refresh enrichment passes (service availability and accreditation legitimately change over time, unlike the existing backfill-only legal-name/bed-count passes).
  - **CBSA (metro/micro area) population estimates** — new `cbsa_population` table loaded from Census Bureau annual estimates (2020-2025). `GET /hospitals/{ccn}` now includes a `cbsa_population` block (latest year + year-over-year growth); new endpoint `GET /market-intelligence/cbsa/{cbsa_code}/population` for the full history.
  - **ClinicalTrials.gov integration** — new `clinical_trials`, `clinical_trial_sites`, and `clinical_trial_investigators` tables loaded from the free ClinicalTrials.gov API v2 (193,782 US-located studies, 2.37M site records, 188K investigators). Site-to-facility and investigator-to-NPI matching uses a conservative normalized name + location match (109,684 sites and 82,639 investigators matched with zero ambiguous guesses — ties are left unmatched rather than resolved arbitrarily). New endpoints: `GET /clinical-trials` (search), `GET /clinical-trials/{nct_id}` (detail with matched sites/investigators), `GET /hospitals/{ccn}/clinical-trials` (facility rollup by status/phase), `GET /provider/{npi}/clinical-trials` (investigator's trials). `GET /provider/{npi}` now includes a compact `clinical_trials` dossier block.
  - **Facility square footage, depreciation, and EBITDA** — extends the same HCRIS Worksheet A/B-1/C parse: `hcris_cost_centers` now carries `square_footage` per cost center (Worksheet B-1 reuses the identical cost-center line numbers already validated), and `hcris_reports` gains `total_facility_sqft`, `depreciation_expense` (Worksheet A-7 Part III), and a derived `ebitda` (operating income + depreciation). Closes the EBITDA gap explicitly left open in the initial HCRIS deep-parse work, which needed a depreciation figure not yet located at the time. All figures validated exactly or within trivial rounding against a real hospital's published numbers.
- **Enterprise contact-sales tier** on the dashboard pricing section — the $1,499/mo tier is renamed "Scale" and a new custom-priced Enterprise tier covers the employment + financial intelligence lines, custom data cuts, security review, and MSA. Procurement note added: 100% public and licensed data, no PHI, no BAA required.
- **Dashboard redesigned on the Verzi corporate brand** (verzihealth.com): deep green-black theme (`#00110F`), turquoise accent, pill CTAs, JetBrains Mono as the data voice. The homepage hero now shows a **live pipeline ledger** — real per-source refresh dates and freshness straight from the public `/sources` registry — and a full **data atlas** table of all 45 sources with license, cadence, and last-load date. Every headline stat carries a provenance chip linking to the registry. PDF/print export keeps the light palette.
- **Dashboard homepage hero** rewritten verify-first: "Provider data you can actually verify," with direct links to `/sources` and `/accuracy` and a "Check Our Accuracy" call-to-action. Site metadata updated to match.
- **A DMEPOS fee miss says why, and names the files it checked.** In the signed answer, `warrants.w1` no longer answers a bare `not_found`. Each answer that is not `ok` carries `reason_code` and `reason`. A miss also carries `checked`: the fee schedule versions in force on the date and each fee table read, with its `doc_id`, `version_id`, `member_file_id` and `sha256`. `not_found` now means the tables in force are read and do not hold the cell. `code_not_in_file` means the table does not list the code. `cell_not_in_file_in_force` means it lists the code but not this key; `available_cells` names the keys it has. A miss that does not show that no fee exists is `unknown`: `no_file_in_force`, `file_in_force_not_parsed` or `no_table_in_force` (for example, a quarter whose files hold only CBA tables, asked for a state cell). It was `not_found`. A quarter that keeps an earlier quarter's state or PEN table in force (a CMS statement that the file has no update) reads that table too, as the fee lookup does. `checked` lists it with `continuation`: the quarter, the authority and the sentence. A miss there is `not_found`. The 404 from `GET /rules/dmepos/fee` carries the same `status`, `reason_code`, `reason` and `checked`. When `status` is `unknown`, its `message` says that the miss does not show that no fee exists. In `/rules/warrant/changes`, such a side is `unknown`, and `changed` is `null`. A missing state answers `reason_code` `state_not_supplied`.
- **The paid warrant serves CMS's coverage-indications text for an unlabeled LCD.** When no approved coder label serves for a code on the date of service, `warrants.w3` now serves the in-force LCD's "Coverage Indications, Limitations, and/or Medical Necessity" section. Each section carries CMS's words exactly as stored, its source proof (file, rendering hash and span), `label_status: "unlabeled"`, and the note "Unlabeled: CMS text as published. No Verzi coder label points into this section." The KB already showed this text. A section that touches licensed text, or holds a CPT-shaped code, serves no text. Coder-approved items stay the layer on top.
- **The free check names the section.** `/v1/check` lists each unlabeled section in `documentation_requirements.unlabeled_sections`: CMS's heading, the version, the archived file and a link. It carries no section text and no span.
- **Warrants serve CMS's words only, never a Verzi paraphrase.** `warrants.w3` always serves the passage layer: CMS's passages, with any approved coder label beside its passage. It no longer serves the older requirement list. That list's `requirement_text` was Verzi's paraphrase.
- **`requirement_text` is now always null.** This holds in `warrants.exclusions` and in `/warrant/changes`. The applicability block no longer carries the key at all (see the applicability entry). The field will be removed later. Each W3 citation now carries `quote_sha256`, the hash of CMS's words at its span.
- **`/warrant/changes` compares W3 requirements by CMS's words.** The identity is `quote_sha256` plus the coder labels (kind, rental month, service context). Each item in `added` and `removed` is now an object with those four fields. Before, each item was a paraphrase string.
- **The appeal exhibit states no Verzi conclusion.** It carries CMS's quotes, dates and source proof only. The sentences that said a requirement "does not apply" or that a denial "is therefore not supported" are gone. The draft says "Exhibit" where it said "Argument".
- **The appeal lists labeled CMS passages.** The requirements part lists the CMS passages that carry an approved coder label, each with its receipt, and shows the label as a coder label. `verzi_summary` is now always null and will be removed later.

### Deprecated
- **Three always-null warrant fields are deprecated, and will be removed on or after 2026-12-01.** `requirement_text` (W3 requirements and exclusions, and the change timeline), `verzi_summary` (the appeal exhibit's evidence) and the legacy `requirements[]` list have been null or absent since strict verbatim (2026-10-16). The OpenAPI schema now marks them `deprecated`.
- **What to read instead.** In `warrants.w3`, read CMS's words from `documents[]` and each citation's source proof. In `/rules/warrant/changes`, the legacy list still appears in `blocks.w3_requirements.from` and `.to`. Read `added[]` and `removed[]` there instead: each names a requirement by `quote_sha256`, the hash of CMS's words.

### Removed
- **`warrants.applicability.statement`.** The block no longer carries a Verzi sentence that restates the verdict ("Refill and recurring-supply documentation does not apply to X."). Strict verbatim: `status`, `applies` and the quoted basis carry the meaning, and the basis entry quotes CMS's own words with their source proof.
- **Named-system case study retired from `/resources`** — the catalog now carries methodology + product docs only. The accuracy benchmark at `/accuracy` continues to publish per-system variance; systems under private evaluation are excluded from public surfaces.

### Fixed
- **The refill authority quote resolves again for dates from 2024.** From PIM Rev. 12512 (in force from 2024-01-01), a line break falls inside "supplied as refills to the original order" in the stored rendering, so the `authority` entry carried no span or quote. The reader now finds the phrase with line breaks between its words.
- **The daily archive ledger no longer posts a version loss alert for versions held through the MCD coverage API.** It was a false alert. The real check comes once revision histories are parsed.
- **A file the archive already holds, captured again as a version of another document, now takes the date its own text states.** Before, a `text_regex` rule found nothing on that path, so the version was stored undated (the DMEPOS PA list of 2018-06-01, 2026-10-14). Versions created from now on read the stored text rendering; one with no readable rendering stays undated, as before. Stored rows are not changed.
- **The updates feed, the upcoming-changes calendar and a source's document list skip defective versions.** A version whose archived file is recorded as defective (truncated, migration 166) or that is recorded as wrongly filed (migration 167) no longer appears in `/archive/changes` or `/archive/upcoming`, and no longer counts in a document's `version_count` or effective-date range. The as-of answers already skipped them.
- **`/v1/check` says why it serves no documentation requirement when approved ones quote a policy version that is not in force on the date.** `documentation_requirements.reason` is `approved_labels_not_in_force` when nothing serves on the date of service but approved requirements exist on a version of the policy that is not in force on that date. The W3 passage block adds `coverage.labels_not_in_force`, the count of those labels. What serves does not change.
- **The status page judges a source by its latest scheduled or live run.** A one-off backfill run (for example a Wayback history fill) or a run scoped with `--only` no longer marks a healthy source as failing, and the daily ledger no longer raises a failing alert from two failed backfill runs. NCCI PTP and Medicare Order and Referring showed as failing only because their latest run was a manual backfill. A failed scheduled run still shows as failing. The second tile now reads "DME policy documents with archived history (60 LCDs, 63 Articles) · 1,444 archived versions", so it no longer looks like a count of all policy documents. The first tile of the status page now says what it counts: "DME codes answering (HCPCS codes with a DME supplier MUE edit)".
- The coder review queues no longer count a rejected or sent-back label or requirement as pending. Before, an item that was approved, then rejected, and whose text then changed, showed again as pending. The pending counts drop by those items.
- Licensed code text that CMS quotes (the ADA's CDT descriptor for D7880) is blanked in more shapes: when the code is written in another letter case, and when the code appears twice in a row. In Article A52512 they still hold the text, as before.
- The W3 requirement check reads every document for that descriptor, not only the listed one. A requirement whose span overlaps it is withheld.
- **The appeal exhibit no longer shows Verzi's summary of a requirement as cited text.** When the documentation-requirement block serves its older shape, each requirement's text is Verzi's summary, not CMS's words. `GET /rules/warrant/appeal` and the MCP `warrant_appeal` tool now put that text in a new evidence field, `verzi_summary`, and leave `quote` empty. The draft prints it on a line that starts "Verzi summary (not CMS's words):", with no quote marks. The next line points to CMS's words at the archived receipt. "Cited text" now appears only before a slice of the archived rendering.
- **An older LCD version in the KB no longer shows the newest version's lists.** CMS's download lists codes and related Articles for the newest version only. On an older version, the KB now says that no list is held for that version, and shows none. The version line names no companion Article from that list. The receipts line names the version its related Articles are for.
- **Eleven held CMS Order and Referring editions get versioned** (after their reversion runs). Ten 2021-07 to 2022-03 Wayback files and one 2024-05-13 live file were stored but not versioned, because their names were variants: lower case, `(1)`, `(2)`, `_0` or `_2`. The earliest edition the ordering physician answer can use becomes 2021-07-26.
- **The DMEPOS fee lookup answers a PEN code in a quarter where CMS stated there is no PEN update.** CMS publishes the PEN fee file only in some quarters. In 13 other quarters a CMS transmittal states there is no PEN update. The fee answer for those quarters comes from the last PEN file, and its `as_of` says `rule: member_continued` with the transmittal and its exact sentence. A quarter with no such statement still answers nothing.
- **The appeal exhibit no longer prints "None" for a policy-text exemption.** An exemption's evidence named its authority as "lcd:X §None (None)" and its version as None. It now names only the fields the basis has (for an exemption, the document id), takes the version in force from the exemption's as-of, and leaves out the version line when no version is known.
- **No DMEPOS fee is served where a CMS revision file covers the date and is not parsed yet.** Before, the fee answer served the unrevised amount. Three revisions are covered: CMS-1687-IFC (rural and non-contiguous state cells, June 2018), CMS-5531-IFC under the CARES Act (non-rural contiguous state cells from 2020-03-06, and every state cell from 2020-04-01 to 2020-06-30; for example, E0601 RR in Connecticut on 2020-03-15 served 40.37, while the July 2020 file states 55.98), and the revised 2022 file (AK, HI, PR and VI cells, 2022). The warrant's fee block answers `unknown` with `reason_code: revision_not_served`, naming the rule and the held file. `/rules/dmepos/fee` answers 404 whose detail has `status: "unknown"` and names the revision. The appeal timeline marks such a date unknown, not changed.
- **Leapfrog grades no longer disappear after one failed table check.** `GET /hospitals/{ccn}/leapfrog` and `/leapfrog/history` check once that the Leapfrog table exists. A busy pool or a dropped connection during that check used to be remembered as "no table" until the next restart, so the grades were hidden. A failed check is now not remembered, and the next request checks again.
- **The ordering physician answer no longer says "not on the roster" for a date the roster change table does not reach.** Nine CMS Order and Referring editions (2023-06-01 to 2023-07-03) are parsed, but the change rows begin with the 2023-07-06 edition. For a date of service in that window, provider standing and the warrant's ordering block answered "not on the roster" for every NPI. They now answer `unknown` with `reason_code: edition_before_change_baseline`.
- **The April 1 ICD-10-CM updates of 2022, 2023 and 2024 are versions of their fiscal year.** CDC NCHS posted April 1 updates during FY2022, FY2023 and FY2024 (the 2022 order file, and the 2023 and 2024 code description, table and index, and addendum files). The `icd10cm_nchs` source now stores each as a version of its fiscal year document from April 1 to September 30, so `GET /v1/archive/docs/{doc_id}/as-of` answers those dates from the April file once the approved `--backfill` run stores them. Stored rows do not change until that run.
- **ICD-10-CM code files named with a slash in the update date get a version.** The FY2023 and FY2024 files of the `icd10cm` source have names such as `...-updated-01/11/2023.zip`, and the versioning rule read none of them (174 files had no version). The rule now reads the update date with its slashes, so `GET /v1/archive/docs/{doc_id}/as-of` answers dates from 2022-10-01 to 2024-09-30 once the approved `--reversion` run gives those documents a version. Stored rows do not change until that run.
- **An MUE table that CMS posted for two quarters with the same bytes now answers for both.** CMS posted the dme_supplier tables for 2020Q3 and 2020Q4 byte for byte. `/rules/ncci/mue` and the W2 edits block answered 2020Q4 dates as held but not read. They now answer from that table, citing the 2020Q4 version and its own file.
- The free E0601 KB page (`/kb/free/E0601`) keeps a signed-in reader's org name and Log out. A signed-in reader without the KB plan, or a key over its daily limit, reads the free view without spending a daily request. A shared cache never keeps a served sections response.
- **A KB code page no longer prints an empty rendering hash.** The receipt of an in-force document with no approved label said "Text rendering sha256 ." when the archived file has no stored text rendering (a Coverage API version before its render pass, such as LCD L33718 version 52). The receipt now says "No text rendering is stored for this file yet." The signed-in receipt says it in similar words.
- **W0 keeps a reinstatement that OIG posted after the LEIE snapshot.** The archive dates the monthly reinstatement files by their month since 2026-10-08. The exclusion reader took that month start as the version's posting date. A LEIE snapshot taken inside the month then outranked the file, and a provider reinstated after the snapshot date stayed "excluded". We found this on 2026-10-09 with the 2026-09 file: the reader still excluded a provider on 2026-09-24 whom OIG reinstated on 2026-09-23. The reader now takes the later of the version's stated date and the first day of the following month as its posting date. So the reinstatement file wins over any snapshot taken in its own month. A snapshot from a later month still wins. Versions that carry a Last-Modified date from before the rule keep that date.
- **Coverage API documents carry their titles.** `archive_doc.title` was never set for LCDs, Articles and NCDs captured from the CMS Coverage API, so the KB pages and the coverage warrant showed ids only (for example `article:52467`). A one-time reader records the title each document's own stored JSON states (`data[0].title`), the publisher's string trimmed at the ends and nothing else, from the highest held version. A document first captured later stays untitled until the reader runs again.
- **W2 PTP answered not_edited for pairs CMS edits, when CMS lists the pair twice with the same effective date.** CMS's cumulative PTP files carry, for some pairs, a line deleted the day it starts (modifier indicator 9) and a live line. The stored table kept one row per (pair, effective date), so the live line was dropped at load: 3,948 live edits across the 2026Q4 files, 3,847 of them hospital. The row identity is now (pair, effective date, deletion date), both lines are stored, and the reader picks the one in force. Parts loaded before this fix answer `unknown` ("parsed before the duplicate-key fix; re-parse pending") until they are re-parsed; they never answer `not_edited` from the old rows.
- **Coverage API Article versions answer by the dates CMS states in them.** Article versions captured from the CMS Coverage API were stored without their effective dates, so a date of service could find no version in force (for example article:52467, the CPAP article, on 2026-09-15). Once the stated-dates reader runs after this release, the archive reads each held version's own stored JSON for the window CMS states (article_eff_date, article_rev_end_date) and uses it where the stored row has no date. Stored rows are not changed. Article versions whose JSON states no effective date stay undated.
- **Coverage API Articles carry their effective dates, and missing DME LCD and Article versions are captured by number.** Articles from the CMS Coverage API were stored without their effective dates, because the Article fields were not read. A DME LCD or Article version that CMS's own revision history names, and that the archive did not hold, is now fetched by its version number (131 of the 134 such versions were served on 2026-10-08). A document that still holds an undated version is held back until that version is dated, because a dated past version beside an undated current one would answer for today. Until the capture runs, some dates answer an older version or no version (for example A52457 on 2025-06-15 answers none; version 63 was in force).
- **Excel members of archived NCCI and DMEPOS files now parse.** The parser stores each archived file under a name with no extension, and the Excel reader refused those names, so every Excel-only member was skipped. For NCCI PTP that meant practitioner parts f1 and f2 of 2026Q3 and 2026Q4, which W2 answers as `unknown`. The reader now opens the file directly. The affected quarters answer once they are re-parsed.
- **W0 exclusion answers past windows for reinstated providers.** A provider excluded and later reinstated is no longer in the current LEIE file, so a date of service inside the old exclusion window used to answer "not excluded". The OIG monthly reinstatement files (every month from 2015-01 is retrievable, most from the Wayback Machine) answer it once they are captured and parsed. For the same exclusion, the newer of the LEIE file and the reinstatement file wins. When the archive cannot prove the answer (a provider who left the LEIE file with no reinstatement file held, or a date before the first live snapshot with a reinstatement month not yet parsed), `excluded_on_dos` is null with `status: "unknown"` and a reason. Each exclusion record names its archived file in a new `source` field. A W0 block whose exclusion answer is `unknown` rolls the warrant's `confidence.overall` up to `unknown`.
- **The sample integration page no longer shows CPT-licensed rationale text.** The blocklist filters "Code Descriptor / CPT Instruction" from the MUE cell display.
- **A long KB search uses its first 5 words.** A pasted CMS title of more than 5 words found nothing. The search now uses the first 5 words and says so. The help line under the box says that spaces inside a code do not count (e 0601 finds E0601) and that a word of one letter is left out (x-ray finds ray). A KB page's canonical link carries `dos` only when it is a date (YYYY-MM-DD).
- **Receipts no longer show an observed window that ends before it starts.** A version captured from the Wayback Machine (or loaded by a backfill), and any version that states its own dates, now shows no `observed_to`; only undated live captures carry an observed window. A Wayback or backfill version's `observed_from` is the time we stored it, and a dated version can be captured out of stated order, so an end date computed from a later live capture could fall before its start. Answers do not change: the observed window decides an answer only for live captures.
- **Manual chapter dates come only from the same manual's transmittal.** The archive dates a CMS manual chapter revision from the transmittal its "Rev." number names. Before the 2020 unified sequence, each manual numbered its own transmittals, so one number can name a Benefit Policy, a Claims Processing and a State Operations Manual transmittal. Two Benefit Policy chapter 15 revisions (Rev. 235 and Rev. 241) carried State Operations Manual dates; they now show no date until their own transmittals are held. Pre-2019 transmittals with upper-case file names (R3444CP.pdf) are now read as the same documents as the live ones.
- **Edits fail closed when a CMS file is missing.** The warrant's edits block (`w2`), `/rules/ncci/ptp` and `/rules/ncci/mue` answer "not edited" or "no MUE" only from files that are held and parsed. PTP answers from the latest quarter held when it is complete; otherwise from the newest held file that carries the pair; otherwise `unknown` (PTP `edited: null`, MUE `mue_value: null`) with a `reason` naming the missing parts. Every answer, negative included, now names the files it relied on in `checked` (rule, quarter, doc_ids, version_ids). Before this fix, a pair in an unparsed part (for example practitioner part 1 in 2026) answered "not edited" with no receipt. **Client change:** the `/rules/ncci/mue` 404 `detail` is now an object (`message` plus `checked`), not a string. An unknown edits part now makes the warrant's `confidence.overall` read `unknown` (the `w2` entry of `confidence.blocks` adds `mue_status` and `ptp_status`), and `/rules/warrant/changes` reports `changed: null` (and `any_change: null` when nothing else moved) when either date is unknown, instead of `false`.
- **The printed appeal exhibit carries no site chrome.** Printing a KB page no longer prints the top bar (brand, theme button, "Get a key") or the find-in-policy box and its yellow marks.
- **The updates feed shows one entry per document per capture run.** When one run captures several new versions of a document, only the newest is news; the others move to Newly archived. RSS, the weekly digest, alerts, and webhooks follow the feed. This removes about 1,750 identical Noridian rows and the skipped versions of MCD Articles from the 30-day feed.
- **Updates feed headlines name files and pages in words.** Quarterly files read "OPPS Addendum Q, 2026Q4" and "DMEPOS fee schedule, 2026Q4"; downloads carry the publisher's file name ("Medicare Coverage Database download current_lcd.zip", "FDA NDC Directory file ndctext.zip"); CMS pages read "CMS page: code sets". Word slugs show their hyphens and underscores as spaces. Citations and host names keep their characters.
- **One feed entry for an LCD change, not two.** CMS's revision-history file for an LCD or Article, captured in the same run as the document, no longer shows as a separate news entry; the document's entry carries CMS's note. It reads "LCD L33824 revision history" under Newly archived.
- **Source archive page in plain words.** The search box asks for a "document id", and an effective date CMS did not state reads "not stated" instead of a blank cell.
- **Better free samples on KB code pages.** The one free sample passage per code is now chosen by a stated rule: an LCD before an Article, a passage that stands on its own before a fragment (never one that ends pointing at text below it), and coverage criteria before order and documentation rules. E0601 now shows the re-evaluation rule (days 31 to 91); K0606 shows its first coverage criterion.
- **KB pages after hostile pass 3.** A code page whose approved passages are not in force today names the dates that show the sample, as links ("See the sample at: 2020-04-03, version 35"). The sample integration page's policy-passage cell uses the same sentence instead of "Not held yet" when approved passages exist on other dates. The sample caption states CMS's window: "in force since 2020-01-01" for an open window, "in force 2020-04-03 to 2021-08-07" for a closed one. CMS's revision notes keep their line breaks. The KB index range ends with "today" when a version of the document is in force today, so a current version with no CMS date no longer reads as retired.
- **The sample integration page loads fast after the first visit.** Its three code tables cache for five minutes per date of service, so a warm load skips about 9 seconds of database reads. A render where any cell could not be read is never cached.
- **A KB code page names each related Article once.** CMS's related-documents file can list one Article twice for an LCD version. The code page's list of Articles for each LCD now names it once, in CMS's order.
- **A retired document no longer answers "in force today".** A version with a stated effective window is now in force only inside that window. Before, a fresh capture of a retired LCD, Article, or quarterly file (stated end in the past) resolved for dates after its end. The as-of answer (`GET /v1/archive/docs/{doc_id}/as-of`, warrants, KB pages) for such a date is now `none`, with a new `reason` field: `before_first_stated_start`, `after_last_stated_end`, `gap_between_stated_windows`, `not_observed_on_date`, or `no_versions`. Versions with no stated date still resolve by their observed window.
- **A real effective date on the capture day still counts.** Only sources whose archive manifest dates versions by our own fetch treat that date as no date. Any other source's effective date is the publisher's, so a quarterly file effective October 1 and first captured October 1 shows as news.
- **The latest pipeline run names its source** on the status page.
- **`GET /v1/archive/changes` rejects an unknown `source_id`** with HTTP 422 instead of returning an empty list.
- **The updates feed shows changes, not first captures.** A version is news when the document was held before and this version is newer than every version held before it, in the publisher's order (effective date, then version number, then observed time). A retroactive revision is still the newest version, so it is news. A document's first capture is news only when its effective date falls in the window or later. First captures and older versions filled in by a later crawl sit behind a new "Newly archived" chip, out of "All", and never ride RSS, the digest, watch-a-code alerts, or webhooks. `GET /v1/archive/changes` takes `kind=news|newly_archived` and `category=`, both filtered before the row cap, and every row carries `news` and `newly_archived`. RSS guids are unique per version.
- **Readable names for the rest of the feed.** Transmittals for the Medicare Secondary Payer, Financial Management, General Information, State Operations, Communications, Managed Care, and Demonstrations manuals carry their manual name; any other transmittal reads "Transmittal {number}". Competitive Bidding pages read the site's own category path ("Competitive Bidding: Educational Information / News and Bulletins"), and pages known only by a document key read "Competitive Bidding document {key}".
- **Status ledger in plain words.** Each run shows its source name, the time in UTC, and one fixed sentence ("Checked. Nothing changed.", "The latest run failed."). Raw errors stay in `GET /v1/archive/runs`, which adds `source_name` and `summary`.
- **Faster feed and status.** The change feed, the upcoming list, and the source catalog are cached for two minutes.
- **Archive page and as-of JSON.** The archive page names documents ("LCD L33718"), drops internal ids, shows plain dates, and links the KB page. The as-of JSON's `kb_url` keeps the date (`?dos=`). Feed receipts name the source host, and history entries say "version", not "new version".
- **Revision notes for current policy versions.** LCD and Article versions captured from the Coverage API now carry CMS's own revision-history entry for that version (for example L33718 version 52, R11, 2024-01-01), read verbatim from CMS's revision-history endpoint. Before, the newest version of a policy showed no note while older versions did. 126 current versions gain a note. The quote carries its own receipt: the sha256 of the archived revision-history file it comes from, on the feed's receipt and as `note_sha256` on webhook events.
- **The honest-empty line says only what we know.** Where Verzi extracted the publisher's note and found none, an entry reads "CMS published no revision note for this change." Where no extraction runs for the source, it reads "No revision note is extracted for this source." Applies to the feed, calendar, digest, alerts, RSS, and the calendar export.
- **KB links that work.** Feed receipts and the as-of JSON link to `/kb/lcd/{n}` or `/kb/article/{n}`, and only when a KB page exists. The as-of notice names the KB page only when there is one.
- **No raw ids or internal numbers in headlines.** An entry with no title or label reads as its source's name plus the id's own tail. The gain line shows the publisher's version, or just "new version", never Verzi's internal version id.
- **Failing sources say so.** The status page reads each source's latest finished run. A failed, partial, or zero-capture run shows a "failing" pill with the run's own error line, as plainly as "fresh".
- **A complete source is not "failing".** Some sources skip files we already hold. When every file is held, the run finds nothing new and records that. The status page and the daily ledger no longer read that run as a moved page. CMS DME supplier utilization (44 of 44 files held) and Medicare provider enrollment (2 of 2) show their real state.
- Fixed: manual-chapter effective dates from transmittals now require publication agreement (migration 135); seven cross-publication false dates retracted to honest unknowns.
- **`/sources` understated our own freshness on 20 datasets we hold and serve.** The page reported "never loaded" for Part D Prescriber (102.9M rows), Physician Utilization (29.1M), Open Payments (27.7M), HCRIS, MA Star Ratings, HSAF, CDC PLACES, ClinicalTrials.gov, HRRP, IPPS Impact, the VBC/ACO layer, the CBSA crosswalk and population files, Dartmouth, AHRQ SDOH, Medicare Geographic Variation, CMS POS service lines, Medicaid SDUD, the SEC 8-K feed, and the employer typology crosswalk. Only the pipeline ever wrote `last_loaded_at`, and it writes it only for runs it wrapped. A loader run by hand never reached it, and neither did a yearly source that had already loaded its last published year: the Checker finds nothing new, so the loader never runs, so the page keeps saying never. Every registered loader now stamps the catalog itself when it succeeds, and a test fails the build when a new one does not.
- **`/sources` published per-run numbers as record counts.** The page claimed we hold 73 hospitals, 17 nursing homes, 6 hospices, 3 long-term care hospitals and 8 clinicians. Those were the counts of CSV files inside the last quarterly CMS ZIP. NPPES showed 73,243, the size of one weekly delta against a 9.7M-row registry. `last_record_count` now means what it says: the rows a source currently holds. The per-run number stays in the load history at `/sources/{source_id}/history`.
- **DAC stopped reporting its own refreshes.** The pipeline calls the source `dac`; the published catalog row is `dac_providers`. Every audit and freshness write went to a source_id no row has, and an `UPDATE` that matches nothing is not an error. So `/sources` froze DAC at its last manual load while the monthly pipeline kept reloading it, and `/sources/dac_providers/history` showed no runs at all. The two ids are mapped explicitly now, and a mismatch is a test failure rather than a silent no-op.
- **`GET /rx/demand` county and metro rows now cover rural counties and Connecticut.** A prescriber's county was taken from the ZIP-to-CBSA crosswalk, so prescribers in about 1,200 counties outside any metro or micro area had no county row, and Connecticut had no county or metro rows at all. The county is now the one that holds most of the practice ZIP's population, and the metro is that county's CBSA.
- **Paging through a list endpoint no longer repeats or skips rows.** Many list endpoints sorted on columns that tie, such as participant counts, dollar amounts, distance, or last name. Postgres returns tied rows in no fixed order, so `limit` and `offset` pages could return the same row twice and never return another. Every paged query now ends its sort on a unique key. This affects `/rx/employers`, `/rx/employers/metros`, `/rx/asp`, `/rx/biosimilars` and the other `/rx` lists, facility searches by radius, Part D and physician utilization, Open Payments, OIG exclusions, HPSA, DAC, PECOS, clinicians, HCRIS, community health, market opportunity, signals, and the VBC lists.
- **`/status` and `/sources` no longer flag healthy CMS sources as critical.** Eight CMS Archive sources declared a `weekly` update cadence. That is how often the pipeline checks them, not how often CMS publishes, and the freshness thresholds read that field. Every one of them turned critical between releases while working correctly, leaving `/status` red with 10 critical sources. Measured from the CMS archive API over the last 13 releases, the median gap is 91 days for hospitals, long-term care, inpatient rehab and hospice, 84 days for home health and dialysis, and about 30 days for nursing homes and doctors/clinicians. Cadences now match the publisher, so the two monthly sources stay on monthly thresholds rather than being lumped in as quarterly. `/status` is green with zero critical sources. How often the pipeline checks is unchanged.
- **The homepage no longer shows invented numbers.** It carried a hardcoded fallback row count, source count, facility count and clinician count, plus sample market-share and community-health panels that named real health systems beside fabricated figures. The fallbacks rendered whenever `/platform/stats` was unreachable, and the sample panels rendered always, so a visitor could not tell a real figure from a placeholder. The page now renders live figures, and omits a count entirely when the API does not answer.
- **Column lookups now fall back to a case-insensitive match.** CMS re-cases column names between files: the MSSP savings rate is `Sav_rate` in twelve files and `Sav_Rate` in PY2021, and the ambulance expenditure is `CapAnn_AmbPay` except in PY2014, where it is `CapAnn_ambpay`. The loader's fixed set of name variants missed both and produced silent NULLs for a whole year. Found by checking every field against every file header rather than by spot-checking.
- **ACO REACH participant records were dropped on load.** The table held a unique key on ACO, performance year, and participant name. CMS publishes one row per participation spell instead. A clinician whose capitation arrangement changes mid-year is several rows with several start dates. The key discarded 16% to 33% of each year's published rows. The key is removed. The loader now collapses only rows that are identical in every column. A REACH participant list is a set of dated spells, not a set of names.
- **Hospital affiliations were empty on DAC and dossier endpoints** — CMS removed the `hosp_afl_*` columns from the DAC national file in 2026; the affected endpoints read those columns and silently returned no affiliations. They now read the Facility Affiliation dataset (2.25M rows). `GET /dac/hospital/{ccn}/affiliates` matches providers through the same dataset.
- **Weekly CMS quality data resumed after a months-long silent stall** — CMS replaced its provider-data archive API in 2026. The old endpoint froze its index around Feb–April and later returned 503, so the weekly sync saw "no new data" and hospital, nursing home, hospice, and other CMS quality sources stopped updating (hospitals and hospice last refreshed 2026-02). All downloaders now use the new archive API, and the stalled quarters are backfilled.
- **`/status` and `/sources` no longer hide staleness on sources without load-tracking history** — a source with no recorded load timestamp used to classify as "never loaded" and was excluded from the health rollup, which is how the stall above went unflagged. A source that has never recorded a load is now judged by its age since registration on the same cadence thresholds, so it turns stale/critical like any other source.
- **`GET /hospitals/{ccn}` returned 500 for every hospital from ~2026-07-23 to 2026-08-13** — the hospital deep-dive code deployed on 07-23, but its database migrations were not applied to production, so every detail request failed on a missing table. Search, history, and other endpoints were unaffected. Fixed by applying migrations 045–059 and loading the six new data sources (HRRP, CMI/wage index, service lines, CBSA population, HCRIS deep worksheets, ClinicalTrials.gov) to production. Two guards now prevent recurrence: migrations run automatically before each deploy and abort it on failure, and a synthetic probe checks the public endpoints every 5 minutes and alarms on consecutive failures.
- **Hospice facility count corrected: 8,391, not 14,067** — the 2020-08 and 2020-11 CMS hospice files quote CCNs and ZIPs in Excel's text-forcing format (`="011500"`). The loader stored the wrapper literally, which created 5,676 duplicate facility rows under phantom IDs and ~823K quality/patient-experience rows that never joined to the real facility. The duplicate rows are removed, the historical measure rows are re-keyed to the correct CCNs (no history lost), and the loader now strips the wrapper so reloads cannot reintroduce it. If you had paged through `/hospice`, roughly 40% of rows were duplicates of other rows; per-CCN lookups for the affected facilities previously required the wrapped literal and now work with the plain CCN.
- **HCRIS `net_income` was silently wrong for every hospital** — the worksheet coordinate pointed at line 02500 ("Other Income"), not the actual Net Income line (02900). For a hospital with material non-operating income, this understated net income by orders of magnitude. Also fixed: `total_discharges` was reading a Medicare-days count instead of an actual discharge count, which meant `cost_per_discharge` was calculated against the wrong denominator for every loaded report. Both are corrected in the loader; existing rows are corrected on the next reload.
- **HCRIS "latest report" queries could nondeterministically return a stale, superseded report** — `/cost-reports/{ccn}` and related endpoints picked the most recent report by fiscal year end with no tiebreaker, so a hospital with two report submissions for the same fiscal year (e.g. an amended resubmission) could have either one served depending on unspecified row order. Now breaks ties by preferring the higher `rpt_rec_num` (more recently processed). Affects roughly 13% of loaded report-years.
- **HCRIS balance sheet quick ratio now uses the standard definition** (current assets minus inventory and prepaid expenses) rather than summing individual line items, which understated it.
- **HCRIS square footage bounded against known data-entry anomalies** — a small number of source submissions carry physically-impossible square footage values (one report showed 22 billion sq ft; a worksheet subtotal line was being read as if it were a single cost center, showing up to 200M sq ft). Both are now excluded/nulled rather than surfaced.
- **The DMEPOS fee lookup answers 2022 Q2 and Q3 dates from the January 2022 state table** (after its reversion run). CMS posted no DMEPOS fee file for April or July 2022, and transmittals R11292CP and R11451CP state there are no updates to the DMEPOS fee schedule file for those quarters. The answer's `as_of` says `rule: member_continued` with the transmittal and its exact sentence. For certain codes in Alaska, Hawaii, Puerto Rico and the U.S. Virgin Islands, CMS's revised 2022 file (DME22-R) changes these amounts and is not applied yet, so those 2022 amounts are not final.
- **The archive file text endpoint answers 503, not 500, when the stored rendering cannot be read.** `GET /v1/archive/files/{sha256}/text` (and `/archive/files/{sha256}/text`) answered 500 when the S3 read of the rendering failed or timed out. It now answers 503 with `Retry-After: 5`. The log keeps the error type and code only.

### Security
- `GET /archive/files/{sha256}/text` holds a rendering that quotes the ADA's CDT descriptor for D7880 in any form Verzi can detect: another letter case, a tag, a line break or an invisible character inside it, another dash, or an HTML entity. The response has `text_withheld: "licensed_text"` and no text. The rendering of three more CMS documents that quote the descriptor is held by document.
- The weekly digest emails, the alerts and the webhooks now apply the licensed-text check to revision notes, as the API feeds do. A note that quotes licensed code text CMS cites, or comes from a document that does, serves no text. The email says Verzi holds the note, and the webhook event carries `revision_note: null` and `revision_note_withheld: "licensed_text"`.
- The receipt viewer, the W3 passages and the KB pages now apply the same licensed-text check as `GET /archive/files/{sha256}/text`. They hold the text of the three CMS documents that quote the ADA's CDT descriptor for D7880. They also hold a text that still has the descriptor in another form after the exact blank. A held receipt serves no quote and no context.
- The KB code-list paragraphs, their asterisk notes and the revision notes now go through the licensed-text check. A held one shows a Verzi line and keeps its receipt.
- In `GET /archive/changes`, the upcoming calendar, the RSS and ICS feeds and the status pages, a revision note that quotes licensed code text serves no text. `revision_note_withheld` is `licensed_text`.
- `GET /archive/files/{sha256}` and `GET /archive/docs/{doc_id}/as-of` give no link to the original bytes of those three documents. `url_withheld` says why, and the sha256 and `resolved_url` still serve.
- **One rule decides which stored source text the API serves, and it fails closed (issue #498).** Before, `GET /warrant/receipt` served any span of any archived rendering to any key, including MCD and CPT-licensed files that `/archive/files/{sha256}/text` withholds. Now the receipt serves the whole rendering only for a public-domain source. For an MCD source it serves only spans inside a narrative section of the coverage document (not the code tables), with the context clipped to that section. For any other license, or a license not classified, it serves no text: `found` is false and `withheld` says why. A quote is at most 5,000 characters. A file that only a non-public source holds answers 404. An out-of-range span no longer reports the rendering's length. `/archive/files/{sha256}/text` now uses the same rule, so a license value not yet classified (NUCC, X12, HL7, payer CPT terms) is held there too, and so is the text of bytes that any such source also holds. The link to the original bytes (the archive file metadata, the as-of answer and the KB file page) is withheld for MCD and CPT copies only, as before. The receipt's MCD section list holds the Coverage API field `coding_guidelines` for now. The KB passages do not follow this rule: they serve every section as before (next line).
- **The KB passages serve every section again.** The license gate briefly withheld the quote and section text of approved labels outside the narrative sections of an MCD document (for example the Coding Information panel) on the KB code page and in the free sample. That is undone: nothing is removed from the KB. Scans for issue #498 found no CPT descriptor and no revenue code text in the served KB sections or in the newest stored renderings. The gate stays on `/warrant/receipt` and `/archive/files/{sha256}/text`.
- The physician utilization endpoints no longer return a CPT descriptor in `hcpcs_description`. They return it only for public HCPCS Level II codes; for any other code it is null.
- The API no longer takes the client address from a visitor-supplied `X-Forwarded-For` or `CF-Connecting-IP`. Rate limits, the signup caps and the OTP caps now use the address Railway appended. Cloudflare headers count only when `CF_EDGE=1` is set.
- **`/archive/files/{sha256}` and `/archive/docs/{doc_id}/as-of` no longer give a link to the original bytes of a source whose files carry CPT descriptors.** These are the sources under the AMA license (`cms_ama_cpt_terms`) and the MCD terms (`cms_mcd_terms`), the same ones whose `/text` rendering the API already withholds. Before, any API key got a one-hour link to those bytes. `url` is now `null` for them, with or without a key, and `url_withheld` says why. The sha256 and the publisher's `resolved_url` still identify the file. Other sources are unchanged.
- The server access log no longer records query strings. A warrant request can carry a date of service and a patient ZIP, which together can identify a patient. The log keeps the path only.
- The MCP tools `warrant`, `warrant_changes` and `warrant_appeal` now return signed answers, the same Ed25519 attestation the REST warrant routes carry. Verify them offline with the key at `/v1/rules/warrant/signing-key`.
- **Verzi leaves out the CDT text that CMS quotes in Article A52512.** CMS's Oral Appliances Policy Article quotes the ADA's CDT descriptor for D7880. Every surface that serves the Article's text now shows "[CDT text owned by the ADA, not shown]" in place of that descriptor: the KB's signed-in text, the KB sections API, the W3 passages (full text, section text, labeled quotes and the free sample), and the receipt viewer (`/warrant/receipt`). The code and CMS's own words stay. W3 and receipt JSON name each gap in `redactions` (`reason: ada_cdt`, `code: D7880`). Verzi holds any text that quotes the code in another form, or that cuts into the descriptor.

---

## [2026-07-08]

### Added
- **Per-source freshness monitor** — `/status` and `/sources` now show whether each registered source is refreshing within its declared cadence. New shared module `api/providers/_shared/freshness.py` classifies each `source_registry` row as `fresh` / `stale` / `critical` / `never` against per-cadence thresholds (daily = 2/8d, weekly = 10/21d, monthly = 45/100d, quarterly = 100/200d, annual = 400/550d). Customer-facing consequence: if we say a source is monthly and it's actually 4 months stale, `/sources` shows a red "stale · 121d" badge with an explanatory note instead of hiding the drift. The `/status` pipeline check switches from "look at the single most recent successful run" (which was silently masking slow-cadence outages) to a per-source rollup that fails when any source is critical. Three CloudWatch alarms in `infra/template.yaml` (`PipelineNoRunAlarm` 25h, `PipelineWeeklyAlarm` 8d, `PipelineFailureAlarm` any) implement the same principle server-side. Alarm on the absence of success, not just the presence of failure.
- **NPPES Authorized Official schema** — migration 052 adds 8 AO columns (last/first/middle name, title, credential, name prefix/suffix, phone) to `nppes_providers` plus a partial index on the AO name pair for Type 2 orgs. Loader updated to capture them from the NPPES bulk file. Fields will populate on the next NPPES full-refresh run (monthly cadence). Unblocks the future `authorized_official` attribution signal migration.
- **Tenet Healthcare decomposition** — 6 new `system_hierarchy` rows: TENET parent + TENET_USPI, TENET_HEALTHSYSTEM, TENET_CALIFORNIA, TENET_FLORIDA, TENET_HOSPITALS. Sourced from CMS Hospital All Owners disclosures (60 hospitals under TENET HEALTHCARE CORPORATION + 24 USPI + 18 regional). EIN 952557091 linked. `/systems/TENET` now returns a real rollup.
- **UHS decomposition** — 7 new rows: UHS parent + UHS_ACUTE + UHS_BEHAVIORAL + UHS_DELAWARE + UHS_HOLDING + UHS_GEORGIA + UHS_PENNSYLVANIA. Only public behavioral-health rollup at scale (Provo Canyon, Cambridge, Cygnet). 145 hospitals under UNIVERSAL HEALTH SERVICES INC. EIN 232077891 linked.
- **Surgery Partners decomposition** — 3 new rows: SGRY parent + SGRY_ASC + SGRY_STONEBRIDGE. ASC roll-up; 17 direct facilities. EIN 473620923 linked. Unique in the market — no other public API exposes ASC ownership at NPI level.
- **Amazon One Medical decomposition** — 1 new row: ONE_MEDICAL as standalone primary-care group. Identified via 4 PECOS org_pac_id matches (ONE MEDICAL GROUP INC, ONE MEDICAL OF NY PC, ONE MEDICAL SENIORS GROUP INC, ONE MEDICAL P.C.). Amazon acquired for $3.9B Feb 2023.
- **IRS 990 XML index for 2020 + 2021** — 989K more rows loaded after fixing the `sub_date VARCHAR(4)` schema constraint (migration 049 widened to TEXT to accept the legacy `1/2/2020 3:03:27 PM` format). Combined with 2022-2024, healthcare-EIN discovery graph now spans 5 tax years (~2.7M index rows).

### Fixed
- **6 remaining sec_10k EINs** — Acadia, American Well, LifeStance, Privia Health, Select Medical, Pennant Group all previously had CIK-in-EIN or SEC placeholder `000000000`. Backfilled real 9-digit EINs by parsing each company's most recent 10-K cover page for the `XX-XXXXXXX` pattern. All 30 `org_eins.sec_10k` rows now have valid EINs.

### Removed
- **QPP MIPS TIN loader** was killed as a plan item after investigation confirmed CMS redacts TIN across all public files (Doctors and Clinicians, PECOS Individual Enrollment, QPP annual). No public path exists; moved to DATA_GAPS Tier 2.1 with external-constraint status.

---

## [2026-07-07]

### Added
- **DOL Form 5500 healthcare backfill** — loaded 107K healthcare-NAICS filings across tax years 2020-2024 (peak years 2021-2024 each cover 24-27K filings representing ~26M W-2 employees per year). Data lives in `dol_5500_filings` (raw) and `financial_filings` (aggregated per EIN × tax period). Powers the `/systems/{id}/financials/workforce` endpoint for any org whose EIN is known. Confirmed correct at the top of the distribution — Trinity Health at 158K W-2 employees (2023), Kaiser 149K, matching each system's publicly-stated head-counts.
- **DOL 5500 employment attribution signals** — 222,138 new signals in `employment_attribution_signals` with `source='dol_5500'`, `signal_type='participant'`, `confidence=0.55`. Cross-cites the DOL W-2 employer floor for NPIs whose PECOS reassignment org matches a linked EIN. Top employers surfaced: NYU (5,724 NPIs), Mayo Clinic (4,899), Montefiore (2,797), Henry Ford (2,538). Ready to grow substantially once the org_eins fuzzy-match review lands.
- **IRS 990 XML index for 2022-2023** — 1.36M new `irs_990_index` rows spanning both years. Combined with the pre-existing 2024 index, the healthcare EIN discovery graph now covers three tax years. 2020-2021 blocked on a `tax_period VARCHAR(4)` constraint issue (older years use different format); tracked in DATA_GAPS.

### Fixed
- **`org_eins` CIK-in-EIN data-model bug** — 30 rows sourced from SEC 10-K filings were storing the SEC EDGAR CIK in the `ein` column (variable-width, not a real EIN). Migration 047 added a proper `cik` column and moved the misplaced values. `scripts/providers/sec_filings/backfill_eins.py` then backfilled 25 real 9-digit EINs via SEC EDGAR submissions API (e.g. Tenet now correctly resolves to EIN 952557091 instead of CIK 70318). Remaining 5 issuers (American Well, LifeStance, Privia Health, Select Medical, Pennant Group) return SEC's placeholder `000000000` and are documented as a follow-up gap.
- **`org_eins.org_pac_id` first population** — 51 rows auto-linked to PECOS `org_pac_id` via trigram fuzzy match (similarity ≥ 0.85) using `scripts/providers/sec_filings/link_org_eins_to_pac.py`. 198 ambiguous candidates written to `data/ein_pac_id_review.csv` for human triage.

---

## [2026-07-02]

### Added
- **`/my-usage` HTML dashboard** — the authenticated usage endpoint now serves a styled dashboard to browsers with a tier badge, color-coded usage progress bar (green under 50%, amber under 90%, red at 90%+), tile grid, and a contextual upgrade CTA. JSON shape unchanged for API clients; a new `billing_tier` key is additive.
- **JSON-LD structured data** on the six main marketing pages (`/`, `/pricing`, `/quickstart`, `/sources`, `/accuracy`, `/legal/data-disclosure`). Enables Google Rich Results eligibility — pricing tiles as native price cards, quickstart as a HowTo card, `/sources` as a Dataset result.
- **`/changelog`** — public user-visible change log (this file). Content-negotiated: browsers get styled HTML, `curl` gets raw markdown.
- **`/status`** — public traffic-light health page. Shows real-time state of the four dependencies (PostgreSQL, S3, SES, pipeline freshness).
- **`/health/deep`** — deep health check endpoint. Runs live dependency probes. `/health` remains shallow + fast for CI polling.
- **`/examples`** and **`/examples/{lang}`** — copy-paste ready code samples in Python, Node.js, curl, Go, and PHP. Browsers get a styled page; `curl` gets the raw source file with the correct MIME type.
- **OpenAPI/Swagger enrichment** — 12 endpoints across `hospitals`, `nursing-homes`, `exclusions`, `provider` (npi_profile), `licenses`, and `mips` routers now carry `summary`, `description`, path/query examples, and response payload examples. The Swagger UI at `/docs` "Try it" form now auto-fills with real CCNs, NPIs, and state codes.
- **Sitemap now includes `/resources/{slug}` and `/examples/{lang}` URLs** with `lastmod` pulled from each file's mtime — helps search engines prioritize recently-updated content.

### Changed
- `/pricing` "Start with Free" CTAs pre-wired to `/billing/checkout?tier=starter` and `?tier=pro`. Currently return `503` until Stripe env vars are set; will flip on live automatically the moment the keys land.
- `/quickstart` "What's next" section now leads with `/examples` as the primary CTA (was "Full API Reference").
- `/health` split into shallow (`/health`, unchanged, fast) vs deep (`/health/deep`, runs real dependency probes).

---

## [2026-06-28]

### Added
- **`/quickstart`** — 5-minute developer integration guide with tabbed curl / Python / JavaScript samples, endpoint gallery, rate-limit callouts.
- **Open Graph social preview cards** — 1200×630 branded PNGs on every marketing page. LinkedIn / Twitter / Slack previews now render with a proper card instead of a bare URL.
- **`/sources/{source_id}`** now returns a styled HTML detail page in browsers (was JSON-only). Completes the `/sources/*` HTML family (list, detail, history).
- **`/resources` catalog + `/resources/{slug}`** rendered documents — the methodology docs (API Style Guide, Accuracy Benchmark, License Intelligence, Financial Intelligence, Employment Reconciliation) are now browsable as styled HTML with the site's brand chrome. `curl` still gets raw markdown.
- **`robots.txt`** and **`sitemap.xml`** for search-engine discovery. Allows public marketing surfaces; blocks admin / billing internals.

### Fixed
- `/billing/plans` legacy URL 301s to the canonical `/pricing`. Old page said "17 data sources" and referenced the wrong tier limits.

---

## [2026-06-27]

### Added
- **`/sources/{source_id}/history`** HTML — per-source refresh audit timeline with status-colored markers, four stat tiles (session count, success rate, last run, last status), and a smart empty state.
- **`/accuracy`** HTML — the marquee proof page. Four summary tiles (median variance, mean variance, systems within ±10%, within ±25%), methodology callout, sorted results table with color-coded variance (green/amber/red), and a ground-truth submission invitation.
- **`/platform/stats`** HTML — live dashboard with pulse-animated "refreshed Xs ago" badge, four stat tiles (data rows, sources, facilities, clinicians), last-pipeline-run card with status pill.
- SEO + Open Graph meta tags across seven public pages (description, `og:title`, `og:description`, `og:type`, `og:site_name`, `og:url`, `twitter:card`).
- **`/pricing`** page with four tiers (Free / $49 Starter / $199 Pro / Enterprise), FAQ, and cross-sell to the executive dashboard.
- **Shared content-negotiation helper** (`api/providers/_shared/content_negotiation.py:prefers_html`) — same URL serves HTML to browsers and JSON to API clients based on `Accept` header + explicit `?format=` override.

### Changed
- **`BILLING_TIERS`** aligned with the new 4-tier pricing (Free 10K/day, Starter 50K/day, Pro 500K/day, Enterprise 10M/day). Was 3 tiers with stale limits.
- **`/billing/checkout`** accepts a `?tier=` query param (starter or pro). Tier flows through Stripe checkout metadata so the webhook upgrades to the right tier.

### Fixed
- `test_admin::test_get_organization` was failing because it referenced a `starter` tier that didn't exist in `BILLING_TIERS`. Now passes.

---

## [2026-06-26]

### Added
- **Live source catalog at `/sources`** — every dataset we ingest with license, refresh cadence, accuracy class, and last-loaded state. 38 sources across CMS (13), other federal (5 — OIG, HRSA, CDC, AHRQ, Census/OMB), academic (1 — Dartmouth), state open-data (1 — CA Medical Board), commercial (2 — Google Maps, FMP), and Verzi-original layers (13). Same URL serves JSON to API clients.
- **Per-source refresh history at `/sources/{source_id}/history`** — every load attempt, success or failure, with timing and row-count deltas.
- **`/platform/stats`** endpoint — live row counts, source counts, facility / clinician counts, last pipeline run status. Backs the marketing numbers without hardcoding them.
- **Data Use & Disclosure Statement** at `/legal/data-disclosure` — canonical, version-controlled statement of which datasets we ingest, under what license, and our HIPAA classification. Maintained outside the auto-generated Terms/Privacy so Termly regenerations can't wipe substantive disclosure.
- **LoadSession instrumentation** — every pipeline loader run now records a session row (`source_id`, `started_at`, `completed_at`, `status`, `rows_loaded`, `error_message`) to the `load_sessions` table.

### Changed
- Homepage stats now fetch from `/platform/stats` via Next.js ISR (1-hour revalidation) instead of hardcoded strings. Row counts stop drifting.
- Data source attribution rewritten across README, `PROJECT_OVERVIEW`, `terms.html`, `privacy.html`, and the homepage to reflect the full 30+ source catalog (CMS, HHS OIG, HRSA, CDC, AHRQ, IRS, DOL, SEC EDGAR, Census, Dartmouth, plus commercial APIs) instead of claiming CMS-only. Every `312M+ rows` / `27 sources` reference updated to live counts or durable phrasing.
- Signup form bot-prevention hardening: honeypot field, disposable email denylist, per-IP rate limit, Cloudflare Turnstile scaffolded and env-flag-gated.

### Fixed
- Migration tooling: `run_migrations.py --stamp` bootstrap flag, cleaner prod auth flow via AWS Secrets Manager (`cms-dev/postgres-credentials`), fixed URL-encoding issue with prod password.

---

## [2026-06-17]

### Added
- **Financial Intelligence** product layer — IRS Form 990 deep XML pipeline (Schedule R + Schedule J), DOL Form 5500 loader, SEC EDGAR 10-K via FMP for 30 curated public healthcare tickers.
- **`financial_filings`** table — cross-source EIN × filing_type × tax_period_end unified store. 15K+ rows.
- **`executive_compensation`** table — Schedule J + 10-K NEO records, 267 entries with matched NPIs for clinician-executives.
- **Financial Intelligence API endpoints**: `/systems/{id}/financials`, `/systems/{id}/financials/executives`, `/systems/{id}/financials/workforce`, `/companies/{ticker}`, `/companies/search`, `/ma-events/recent`, `/provider/{npi}/financial-affiliation`.

---

## [2026-06-16]

### Changed
- **API consolidation**: `/quality` nested under `/provider`, `/quality` router renamed to `/mips`, fixed `/systems` route collision.

---

## [2026-06-11]

### Added
- **License + Quality Intelligence v1**
  - `provider_licenses` (5M+ NPI-keyed licenses derived from NPPES taxonomy across all 50 states)
  - `license_disciplinary_actions` (8.5K OIG LEIE joined to NPI)
  - License Intelligence API: `/licenses/state/{state}`, `/licenses/{npi}`, `/licenses/{npi}/disciplinary`, `/licenses/alerts/recent`
  - Quality Intelligence API: `/quality/{npi}`, `/quality/leaderboards/{specialty}`, `/quality/systems/{system_id}`
  - Extended `/provider/{npi}` dossier with new `license_intelligence`, `quality_intelligence`, `employment_intelligence` blocks

---

## [2026-06-10]

### Added
- **Physician Employment Intelligence v2** — multi-source attribution + system decomposition
  - `org_eins`, `employment_attribution_signals`, `system_hierarchy` tables
  - Large national systems decomposed into operating divisions
  - Accuracy benchmark at `/accuracy` — per-system variance vs publicly-stated counts
- **Physician Employment Intelligence v1** — initial employer typology product
  - `physician_employer_systems`, `employer_facts` materialized view
  - 12 API endpoints at `/provider/*`, `/employers/*`, `/systems/*`

---

## Older

Prior to 2026-06-10, changes were tracked via git log only. The last major additions before that date were VBC/ACO analytics, HCAHPS benchmarking materialized views, market intelligence (HSAF), community health (CDC PLACES, Medicare Geographic Variation, AHRQ SDOH), and the initial 9 CMS provider-type ingestion.

---

## How to read this

- **Added** — new endpoints, tables, or user-visible features
- **Changed** — modifications to existing behavior (usually backward-compatible)
- **Fixed** — bugs or inconsistencies resolved
- **Deprecated** — features scheduled for removal (none yet)
- **Removed** — features removed (none yet)
- **Security** — security-relevant changes (none breaking; hardening only)

For the machine-readable API changelog, hit `curl https://api.verzi.health/changelog` — returns raw markdown.
