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:
parent
a732423f04
commit
483f185861
4 changed files with 143 additions and 8 deletions
|
|
@ -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";
|
||||
|
||||
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 practiceState = locationAddr?.state || null;
|
||||
|
||||
// 2) Companion data joins (best-effort; tables may be empty pre-load)
|
||||
const [revalRes, exclRes, optoutRes] = await Promise.all([
|
||||
// 2) Companion data joins. Revalidation comes LIVE from the public CMS
|
||||
// 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(
|
||||
`SELECT revalidation_due_date, adjusted_due_date, enrollment_type, specialty, enrollment_state
|
||||
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[] })),
|
||||
]);
|
||||
|
||||
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 optout = optoutRes.rows[0] || null;
|
||||
|
||||
|
|
@ -289,6 +348,16 @@ router.get("/api/v1/npi/lookup", async (req, res) => {
|
|||
enumeration_date: enumerationDate ? fmtDate(enumerationDate) : null,
|
||||
last_updated: lastUpdated ? fmtDate(lastUpdated) : null,
|
||||
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: {
|
||||
red: redCount,
|
||||
yellow: yellowCount,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue