feat(healthcare): prove revalidation is real via official CMS data + self-verify

Skepticism ("is this even real?") is the top objection. The data IS accurate
(verified our subscribers' NPIs match the official CMS Revalidation Due Date List
exactly), so this is a credibility-presentation fix:

1. Email: replace the plain detail row with an "Official record - CMS Medicare
   Revalidation Due Date List" card (NPI, legal name, due date, days overdue)
   plus a "Verify on CMS.gov" button. Clearly labeled as our presentation of
   public CMS data, not a CMS screenshot (no impersonation).
2. API: npi/lookup now pulls the revalidation due date LIVE from the public CMS
   dataset (data.cms.gov) instead of the empty local table, and returns a
   revalidation{ due_date, source, cms_legal_name, verify_url } proof object.
3. Tool: /tools/npi-compliance-check shows a live "official record" card with a
   self-verify link when CMS returns a due date.

Builder now stores reval_due_date/days_overdue as separate attribs for the card
(existing 194 subscribers backfilled from their detail string).
This commit is contained in:
justin 2026-06-07 23:54:01 -05:00
parent a732423f04
commit 483f185861
4 changed files with 143 additions and 8 deletions

View file

@ -35,6 +35,56 @@ async function nppesFetch(params: Record<string, string>): Promise<any> {
} }
} }
// Live, authoritative CMS "Revalidation Due Date List" lookup by NPI. This is
// the same public .gov dataset the provider can check themselves at
// data.cms.gov/tools/medicare-revalidation-list, so it is the strongest proof
// the due date is real (not from our database). Returns null on miss/error so
// callers fall back to the local companion table.
const CMS_REVAL_API =
"https://data.cms.gov/data-api/v1/dataset/3746498e-874d-45d8-9c69-68603cafea60/data";
interface CmsRevalRecord {
revalidation_due_date: string | null;
adjusted_due_date: string | null;
enrollment_type: string | null;
specialty: string | null;
enrollment_state: string | null;
legal_name: string | null;
source: "cms_live";
}
async function cmsRevalFetch(npi: string): Promise<CmsRevalRecord | null> {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 6000);
try {
const qs = `filter[National Provider Identifier]=${encodeURIComponent(npi)}`;
const resp = await fetch(`${CMS_REVAL_API}?${qs}`, {
signal: controller.signal,
headers: { Accept: "application/json" },
});
clearTimeout(timer);
if (!resp.ok) return null;
const rows = (await resp.json()) as any[];
if (!Array.isArray(rows) || rows.length === 0) return null;
// Prefer the row with a concrete (non-TBD) due date.
const norm = (v: any) => (typeof v === "string" && v && v.toUpperCase() !== "TBD" ? v : null);
const row =
rows.find((r) => norm(r["Adjusted Due Date"]) || norm(r["Revalidation Due Date"])) || rows[0];
return {
revalidation_due_date: norm(row["Revalidation Due Date"]),
adjusted_due_date: norm(row["Adjusted Due Date"]),
enrollment_type: row["Enrollment Type"] || null,
specialty: row["Enrollment Specialty"] || null,
enrollment_state: row["Enrollment State Code"] || null,
legal_name: row["Organization Name"] || [row["First Name"], row["Last Name"]].filter(Boolean).join(" ") || null,
source: "cms_live",
};
} catch {
clearTimeout(timer);
return null;
}
}
type CheckStatus = "green" | "yellow" | "red" | "unknown"; type CheckStatus = "green" | "yellow" | "red" | "unknown";
interface ComplianceCheck { interface ComplianceCheck {
@ -98,8 +148,11 @@ router.get("/api/v1/npi/lookup", async (req, res) => {
const locationAddr = (result.addresses || []).find((a: any) => a.address_purpose === "LOCATION") || (result.addresses || [])[0] || null; const locationAddr = (result.addresses || []).find((a: any) => a.address_purpose === "LOCATION") || (result.addresses || [])[0] || null;
const practiceState = locationAddr?.state || null; const practiceState = locationAddr?.state || null;
// 2) Companion data joins (best-effort; tables may be empty pre-load) // 2) Companion data joins. Revalidation comes LIVE from the public CMS
const [revalRes, exclRes, optoutRes] = await Promise.all([ // Revalidation Due Date List (authoritative + provider-verifiable); the
// local table is a fallback only (it may be empty pre-load).
const [cmsReval, revalRes, exclRes, optoutRes] = await Promise.all([
cmsRevalFetch(rawNpi),
pool.query( pool.query(
`SELECT revalidation_due_date, adjusted_due_date, enrollment_type, specialty, enrollment_state `SELECT revalidation_due_date, adjusted_due_date, enrollment_type, specialty, enrollment_state
FROM npi_revalidation_due WHERE npi = $1 ORDER BY id LIMIT 1`, FROM npi_revalidation_due WHERE npi = $1 ORDER BY id LIMIT 1`,
@ -119,7 +172,13 @@ router.get("/api/v1/npi/lookup", async (req, res) => {
).catch(() => ({ rows: [] as any[] })), ).catch(() => ({ rows: [] as any[] })),
]); ]);
const reval = revalRes.rows[0] || null; // Prefer live CMS data; fall back to the local companion table.
const reval = cmsReval || revalRes.rows[0] || null;
const revalSource: "cms_live" | "local" | null = cmsReval
? "cms_live"
: revalRes.rows[0]
? "local"
: null;
const excl = exclRes.rows[0] || null; const excl = exclRes.rows[0] || null;
const optout = optoutRes.rows[0] || null; const optout = optoutRes.rows[0] || null;
@ -289,6 +348,16 @@ router.get("/api/v1/npi/lookup", async (req, res) => {
enumeration_date: enumerationDate ? fmtDate(enumerationDate) : null, enumeration_date: enumerationDate ? fmtDate(enumerationDate) : null,
last_updated: lastUpdated ? fmtDate(lastUpdated) : null, last_updated: lastUpdated ? fmtDate(lastUpdated) : null,
checks, checks,
// Verification proof: the revalidation due date (when present) is from the
// live public CMS dataset the provider can check themselves.
revalidation: reval
? {
due_date: (reval.adjusted_due_date || reval.revalidation_due_date) ?? null,
source: revalSource,
cms_legal_name: (reval as any).legal_name ?? null,
verify_url: "https://data.cms.gov/tools/medicare-revalidation-list",
}
: null,
summary: { summary: {
red: redCount, red: redCount,
yellow: yellowCount, yellow: yellowCount,

View file

@ -21,11 +21,38 @@
<div style="font-size:13px;color:#065f46;line-height:1.7;">If you do not revalidate, CMS will <strong>deactivate your Medicare billing privileges</strong> &mdash; claims stop paying and you must re-enroll from scratch, losing your effective date and any retroactive billing.</div> <div style="font-size:13px;color:#065f46;line-height:1.7;">If you do not revalidate, CMS will <strong>deactivate your Medicare billing privileges</strong> &mdash; claims stop paying and you must re-enroll from scratch, losing your effective date and any retroactive billing.</div>
</td></tr></table> </td></tr></table>
<!-- Detail row --> <!-- Official CMS record card: the data is straight from the CMS Revalidation
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:18px 0;font-size:13px;"> Due Date List (verified to match by NPI), presented as a clearly-labeled
<tr style="border-bottom:1px solid #e5e7eb;"><td style="padding:10px 0;color:#6b7280;">NPI</td><td style="padding:10px 0;font-weight:600;text-align:right;">{{ .Subscriber.Attribs.npi }}</td></tr> data readout. NOT a CMS screenshot / not impersonating CMS. -->
<tr style="border-bottom:1px solid #e5e7eb;"><td style="padding:10px 0;color:#6b7280;">Revalidation due</td><td style="padding:10px 0;font-weight:600;text-align:right;">{{ .Subscriber.Attribs.detail }}</td></tr> <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:22px 0;">
<tr><td style="padding:10px 0;color:#6b7280;">Our service fee</td><td style="padding:10px 0;font-weight:700;text-align:right;color:#047857;">$599</td></tr> <tr><td style="border:1px solid #cbd5e1;border-radius:10px;overflow:hidden;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
<tr><td style="background:#1e293b;padding:12px 16px;">
<p style="margin:0;font-size:11px;letter-spacing:.4px;text-transform:uppercase;color:#94a3b8;font-weight:700;">Official record &middot; CMS Medicare Revalidation Due Date List</p>
</td></tr>
<tr><td style="background:#f8fafc;padding:6px 16px 14px;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="font-size:13px;">
<tr style="border-bottom:1px solid #e5e7eb;"><td style="padding:9px 0;color:#64748b;">Provider / NPI</td><td style="padding:9px 0;font-weight:700;text-align:right;color:#0f172a;">{{ .Subscriber.Attribs.npi }}</td></tr>
<tr style="border-bottom:1px solid #e5e7eb;"><td style="padding:9px 0;color:#64748b;">Enrolled as</td><td style="padding:9px 0;font-weight:600;text-align:right;color:#0f172a;">{{ .Subscriber.Attribs.practice }}</td></tr>
<tr style="border-bottom:1px solid #e5e7eb;"><td style="padding:9px 0;color:#64748b;">Revalidation due date</td><td style="padding:9px 0;font-weight:700;text-align:right;color:#b91c1c;">{{ .Subscriber.Attribs.reval_due_date }}</td></tr>
<tr><td style="padding:9px 0;color:#64748b;">Status</td><td style="padding:9px 0;font-weight:700;text-align:right;color:#b91c1c;">PAST DUE &middot; {{ .Subscriber.Attribs.days_overdue }} days overdue</td></tr>
</table>
<p style="margin:10px 0 0;font-size:11px;color:#94a3b8;line-height:1.5;">Source: CMS Revalidation Due Date List (data.cms.gov), refreshed monthly. Performance West is an independent compliance firm, not affiliated with CMS or Medicare.</p>
</td></tr>
</table>
</td></tr>
</table>
<!-- Verify-it-yourself: nothing is more convincing than the government's own site -->
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:14px 0 22px;"><tr><td style="background:#eff6ff;border:1px solid #bfdbfe;border-radius:10px;padding:16px;">
<p style="margin:0 0 6px;font-size:13px;color:#1e3a8a;font-weight:700;">Don&rsquo;t take our word for it &mdash; check the official CMS record.</p>
<p style="margin:0 0 12px;font-size:13px;color:#1e40af;line-height:1.6;">Look up your NPI <strong>{{ .Subscriber.Attribs.npi }}</strong> on the U.S. government&rsquo;s public Medicare Revalidation List and you&rsquo;ll see the same due date above.</p>
<a href="https://data.cms.gov/tools/medicare-revalidation-list" style="display:inline-block;padding:10px 22px;background:#fff;border:1px solid #1d4ed8;color:#1d4ed8;font-weight:700;border-radius:8px;text-decoration:none;font-size:13px;">Verify on CMS.gov &#8599;</a>
</td></tr></table>
<!-- Service fee -->
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 8px;font-size:13px;">
<tr><td style="padding:8px 0;color:#6b7280;">Our service fee to file it for you</td><td style="padding:8px 0;font-weight:700;text-align:right;color:#047857;">$599</td></tr>
</table> </table>
<!-- CTA --> <!-- CTA -->

View file

@ -205,6 +205,11 @@ def main():
"practice": r.get("name", ""), "practice": r.get("name", ""),
"specialty": r.get("specialty", ""), "specialty": r.get("specialty", ""),
"state": r.get("state", ""), "state": r.get("state", ""),
# Separate fields so the email's "official CMS record" card can render
# the due date and overdue count cleanly (these mirror the authoritative
# CMS Revalidation Due Date List, verified to match by NPI).
"reval_due_date": r.get("reval_due_date", ""),
"days_overdue": str(r.get("days_overdue", "")),
"detail": (f"{r.get('reval_due_date','')} ({r.get('days_overdue','')} days overdue)" "detail": (f"{r.get('reval_due_date','')} ({r.get('days_overdue','')} days overdue)"
if r.get("reval_status") == "overdue" else r.get("reval_due_date", "")), if r.get("reval_status") == "overdue" else r.get("reval_due_date", "")),
} }

View file

@ -88,6 +88,9 @@ import Base from "../../layouts/Base.astro";
<div id="checks-container" class="space-y-4"></div> <div id="checks-container" class="space-y-4"></div>
<!-- CMS verification proof (shown when a revalidation due date is found) -->
<div id="cms-proof" class="hidden"></div>
<!-- CTA --> <!-- CTA -->
<div id="cta-section" class="bg-white border border-gray-200 rounded-xl p-6 shadow-sm"> <div id="cta-section" class="bg-white border border-gray-200 rounded-xl p-6 shadow-sm">
<div id="cta-content"></div> <div id="cta-content"></div>
@ -267,6 +270,37 @@ import Base from "../../layouts/Base.astro";
} }
renderCta(data); renderCta(data);
// CMS verification proof: when the revalidation due date came from the
// live public CMS dataset, show it as an "official record" the provider
// can independently verify on CMS.gov. This is what converts skeptics.
const proof = document.getElementById("cms-proof");
const rv = data.revalidation;
if (rv && rv.due_date && rv.source === "cms_live") {
proof.innerHTML = `
<div class="border border-slate-300 rounded-xl overflow-hidden shadow-sm">
<div class="bg-slate-800 px-4 py-2">
<p class="text-[11px] font-bold tracking-wide uppercase text-slate-300">Official record &middot; CMS Medicare Revalidation Due Date List</p>
</div>
<div class="bg-slate-50 px-4 py-4">
<dl class="text-sm divide-y divide-gray-200">
<div class="flex justify-between py-2"><dt class="text-gray-500">Provider / NPI</dt><dd class="font-semibold text-gray-900">${data.npi}</dd></div>
${rv.cms_legal_name ? `<div class="flex justify-between py-2"><dt class="text-gray-500">Enrolled as</dt><dd class="font-medium text-gray-900 text-right">${rv.cms_legal_name}</dd></div>` : ""}
<div class="flex justify-between py-2"><dt class="text-gray-500">Revalidation due date</dt><dd class="font-bold text-red-700">${rv.due_date}</dd></div>
</dl>
<p class="mt-3 text-xs text-gray-400 leading-relaxed">Pulled live from the U.S. government&rsquo;s public CMS dataset just now &mdash; this is not our data. Performance West is an independent compliance firm, not affiliated with CMS or Medicare.</p>
<a href="${rv.verify_url}" target="_blank" rel="noopener" class="inline-flex items-center gap-1 mt-3 px-4 py-2 bg-white border border-blue-600 text-blue-700 text-sm font-semibold rounded-lg hover:bg-blue-50 transition">
Verify this yourself on CMS.gov
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
</a>
</div>
</div>`;
proof.classList.remove("hidden");
} else {
proof.classList.add("hidden");
proof.innerHTML = "";
}
document.getElementById("checked-at").textContent = "Checked at " + new Date().toLocaleString(); document.getElementById("checked-at").textContent = "Checked at " + new Date().toLocaleString();
resultsEl.classList.remove("hidden"); resultsEl.classList.remove("hidden");
document.getElementById("results").scrollIntoView({ behavior: "smooth", block: "start" }); document.getElementById("results").scrollIntoView({ behavior: "smooth", block: "start" });