The Problem
NAFDAC maintains a public registry of 8,727 registered drugs. Pharmacists can't use it at the counter — it's not built for lookups under pressure.
A pharmacist receiving a suspicious batch has two options: trust the packaging, or dig through a registry not designed for search. This tool closes that gap.
v1 → v2
v1 answered one question: is this drug name registered? v2 answers the one that matters more — is the specific pack in your hand genuine?
v1 shipped with 8,727 drugs, FTS5 full-text search, and a 5-path verification engine. VERIFIED in under 3 seconds. Claude only called on ambiguous results, never on the clean path.
But VERIFIED only confirmed the name existed in the registry. It said nothing about expiry, tampered packaging, or whether the manufacturer on the box matched the record. v2 closes that gap.
- Dual-mode platform — same backend, two audiences. Pharmacist mode: clinical language, technical Claude risk assessment, collapsible inspection checklist. Community mode: plain language, prominent warnings, checklist open by default, NAFDAC hotline on every card. Toggled in the header, persisted in localStorage.
- Expiry checker — inline on the VERIFIED card. Two inputs (MM/YY), client-side only, zero extra API calls. Returns VALID, EXPIRES SOON (within 30 days), or EXPIRED with mode-aware copy.
- Physical inspection checklist — 5 tap-targets (seal, print quality, lot number, colour/smell, heat damage) on every NOT FOUND card. Also on VERIFIED cards in Community mode — because registration doesn't guarantee the specific pack is genuine.
- Reporting pipeline — anonymous report form on NOT FOUND cards routes to NAFDAC's sf.alert inbox in WHO Rapid Alert System format. Reference numbers follow the SF-{YEAR}-{id} convention used in official WHO substandard/falsified classification. Email failure never blocks the user-facing response.
Verification Logic
Five paths. One right answer. Claude only enters where judgment is genuinely needed.
The system tries the fastest, most certain signal first. Only when that fails does it run FTS5. Claude is called only on paths where the database result alone is insufficient.
Exact match on drug name or reg number
Instant lookup — the first check returns a definitive record. Fuzzy search never runs.
FTS5 returns 0 results
Nothing in the database matches. Claude generates a counterfeit warning with guidance for the pharmacist.
FTS5 returns exactly 1 result
A single fuzzy match isn't confident enough to surface — returning it would risk a false positive. The system returns NOT FOUND instead.
FTS5 returns 2+ results — one dominant
Top result's rank score is at least 2× the second. Claude picks the best candidate and explains the distinction.
FTS5 returns 2+ results — evenly matched
No clear winner by score. Claude lists all candidates so the pharmacist can compare and decide.
Outcome
Real regulatory integration. Dual-audience design. Honest about what it can't guarantee.
- Same data layer, two UX modes — clinical for pharmacists, plain-language for the public. No backend changes, no separate app.
- Honest about limits — VERIFIED means registered, not that the pack is genuine. The inspection checklist appears on VERIFIED cards in Community mode for exactly this reason.
- Real regulatory pipeline — reports route to NAFDAC's actual SF alert inbox in WHO Rapid Alert System format. Not a mock endpoint.
- Review-driven quality — 8 issues caught through structured code review before a line hit production, including an XSS vector in the email template and a mode prop threading bug across card states.