diff --git a/api/src/routes/checkout.ts b/api/src/routes/checkout.ts index 165f79b..c669c68 100644 --- a/api/src/routes/checkout.ts +++ b/api/src/routes/checkout.ts @@ -2152,49 +2152,35 @@ export async function sendComplianceIntakeEmail(

` : ""; - // CMS filing-method section for PECOS / NPPES orders. We offer two service - // tiers and let the provider pick the one that's easiest for them: - // (1) Standard filing — they review + sign one certification, we submit to - // their MAC. Zero account setup. Default for reval/enrollment. - // (2) Expedited filing — faster/same-day-trackable via CMS I&A surrogate access. - // NPPES-only services (reactivation, update) are web-only, so surrogate access - // is the only path for those. We never ask for the provider's password. + // CMS filing for PECOS / NPPES orders. We file everything for the provider; + // the ONLY client-facing choice is the optional surrogate question, framed + // positively. We NEVER expose the mechanics (paper, CMS-855/10114, MAC, Fargo) + // and we NEVER tell them "the alternative is paper". If they grant I&A + // Surrogate access we file online same-day; if not, we file it for them via our + // Standard path silently. Same price either way — surrogate is just faster for + // us (fewer steps, lets us bulk-file). We never ask for their password. const npiConfirmUrl = `${SITE_DOMAIN}/order/success?action=ia_surrogacy&order_id=${orderId}`; - // Which ordered services support the standard (no-account) filing path - // vs. are NPPES-web-only (surrogate access required). - const STANDARD_FILING_SLUGS = new Set(["npi-revalidation", "medicare-enrollment", "provider-compliance-bundle"]); - const NPPES_ONLY_SLUGS = new Set(["npi-reactivation", "nppes-update"]); - const hasStandardFiling = npiAccessOrders.some(o => STANDARD_FILING_SLUGS.has(o.service_slug as string)); - const hasNppesOnly = npiAccessOrders.some(o => NPPES_ONLY_SLUGS.has(o.service_slug as string)); - - const standardFilingBlock = hasStandardFiling ? ` -

Standard filing — no account needed

-

- We complete the correct CMS-855 for you. You review and sign the certification - from a secure link we send (takes about a minute), and we submit it to your - Medicare Administrative Contractor (MAC) and track it to confirmation. Nothing - for you to set up. -

` : ""; - - const surrogacyHeading = hasStandardFiling - ? `Expedited filing — CMS I&A surrogate access` - : `Grant CMS I&A surrogate access`; const npiSection = hasNpiAccess ? `
-

Action Required: Choose How We File

- ${standardFilingBlock} -

${surrogacyHeading}

+

We're handling your filing

- ${hasNppesOnly ? "NPPES updates and reactivations are online-only, so this is required for those. " : ""}You add us as a - Surrogate in CMS Identity & Access (I&A) — you never share your password. - We then file in PECOS / NPPES under our own credentials and capture the tracking ID the same day. + You're all set — we prepare and submit your filing and track it to + confirmation. The only thing we may need from you is a quick signature on + a secure link we'll send. Nothing to set up. +

+

Optional: speed it up

+

+ If you can electronically grant us CMS I&A Surrogate access, + we can file faster — it cuts steps on our end and lets us process your + filing right away. You never share your password; you simply authorize us + as a Surrogate for your NPI.

    -
  1. Log in to CMS I&A (I&A System)
  2. +
  3. Log in to CMS Identity & Access (I&A)
  4. Go to My Connections → Add Surrogate
  5. Add surrogate organization: Performance West Inc. (email filings@performancewest.net)
  6. -
  7. Grant access to PECOS and NPPES for your NPI, then approve our request
  8. +
  9. Grant access for your NPI, then approve our request

@@ -2202,7 +2188,8 @@ export async function sendComplianceIntakeEmail(

- ${hasStandardFiling ? "Prefer standard filing? Just reply to this email and we'll send your CMS-855 to sign — no further action needed from you here." : "Clicking this notifies our team so we can begin your filing."} + Can't do this step? No problem — we'll handle your filing without it. + There's nothing else you need to do.

` : ""; diff --git a/docs/healthcare-filing-tiers-verified.md b/docs/healthcare-filing-tiers-verified.md new file mode 100644 index 0000000..30b7b22 --- /dev/null +++ b/docs/healthcare-filing-tiers-verified.md @@ -0,0 +1,99 @@ +# Healthcare filing: Standard (no-login) vs Expedited (surrogate) — VERIFIED matrix + +Internal capability map. Confirms how each healthcare service is fulfilled with +**no client login and no credential sharing**, and where the optional +**Expedited (CMS I&A Surrogate)** path applies. + +> **Client-facing rule (do not break):** never expose mechanics to the client. +> No "paper", "CMS-855", "CMS-10114", "MAC", "Fargo", or form numbers in any +> client copy. The only client-visible choice is the positive surrogate question +> ("electronically granting us I&A Surrogate access lets us bulk-file faster / +> fewer steps on our end"). Declining is silent; we just "file it for you." +> Same price either way — Expedited is NOT a paid tier. + +## Two service tiers (internal) +- **Standard (default):** we complete the official form, client signs ONE + certification (e-sign link), we submit + track. For CMS-855s the signed form is + mailed to the provider's MAC; for NPPES-only changes via the NPI Enumerator + paper path. Daily batched mailing (see §Daily batch). +- **Expedited (if client grants I&A Surrogate):** we file online in PECOS/NPPES + under our own login (surrogate), same-day tracking. Surrogacy is a delegation, + NOT a password handover. + +## VERIFIED from the official CMS form PDFs in `docs/` (primary source) + +### CMS-855I (05/23) — individual practitioner [CONFIRMED, quoted] +- Valid reasons include: **revalidation**, **reactivation** ("reactivate your + Medicare billing number to resume billing"), **changes to enrollment info**, + and enrolling with another MAC. → revalidation/reactivation/enrollment all + ride the 855I paper path. +- Paper submission: *"Send this completed application with **original signatures** + and all required documentation to your **designated MAC**. The MAC that services + your State is responsible for processing your enrollment application."* + → **no login required; routed by STATE.** +- Signature caveat: *"As an individual practitioner, you are the only person who + can sign this application. The authority to sign the application on your behalf + **may not be delegated** to any other person."* → client must personally sign + (pen/e-sign), but that is NOT a login. A surrogate **cannot** sign the 855 for + them — surrogacy speeds the *online filing*, the cert signature is still theirs. + +### CMS-855B (12/2025) — clinic/group/supplier [CONFIRMED, quoted] +- Same paper-to-MAC, original-signatures, routed-by-State rule. +- Org signer: a **delegated/authorized official** signs §15B/15D (not the + individual) — still a signature, no login. +- **Application FEE** required (paid via PECOS feePaymentWelcome) on initial + enrollment, new location, and revalidation BEFORE mailing. → org standard path + has a fee-payment step we must handle/route; individual 855I does not. + +### CMS-855O (09/23) — ordering/referring only [CONFIRMED, quoted] +- Paper-to-MAC, original signatures, routed by state. "Sign and date section 8 + using ink." + +### NPPES data update / NPI reactivation (CMS-10114 paper path) [PARTIAL] +- 855I confirms NPI is obtained/changed via NPPES and "you may apply **online** at + NPPES.cms.hhs.gov" — implies the online path; the paper CMS-10114 to the NPI + Enumerator (PO Box 6059, Fargo ND 58108-6059) is the documented paper + alternative per the existing `healthcare-no-login-value-add.md` (NPI Enumerator + 800-465-3203). +- **UNCERTAIN (verify before relying on it as the sole standard path):** whether + CMS still accepts paper CMS-10114 for *updates/reactivation* (vs initial + enumeration only) today. Could not reach the live CMS-10114 instruction from + this host (search engines blocked, direct URLs 404). +- **Decision:** for NPPES-only services, offer Expedited (surrogate → file in + NPPES online) as the primary fast path, and the CMS-10114 paper path as the + Standard fallback. Confirm the CMS-10114-for-changes acceptance + obtain the + current form PDF before building `cms10114_pdf_filler.py`. Until confirmed, the + honest Standard fallback for a declined-surrogate NPPES update may be "we + prepare it and guide the one-step online submission" — TBD on verification. + +## CMS I&A Surrogate (Expedited path) +- Provider adds Performance West as a **Surrogate** in CMS I&A; we then file in + PECOS/NPPES under our own credentials. No password shared. +- Scope nuance to verify in copy: confirm a single surrogacy grant covers the + PECOS and NPPES functions we need (the checkout copy currently asks for both). + +## Per-service tier table +| Service | Standard (no-login) | Expedited (surrogate) | Fee? | +|---|---|---|---| +| Medicare revalidation | 855I/B → MAC, client signs | file in PECOS | 855B yes | +| Medicare enrollment | 855I/B/O → MAC, client signs | file in PECOS | 855B yes | +| NPI reactivation | 855I → MAC (reactivation reason) | PECOS/NPPES | no | +| NPPES data update | CMS-10114 → Fargo *(verify)* | NPPES online | no | +| OIG/SAM screening | public DBs, **zero client action** | n/a (no portal) | no | +| Provider compliance bundle | spawns reval + screening + NPPES | mixed | per-piece | + +## Daily batch (Standard-path mailing) +Each **postal working day morning** (skip weekends + USPS/federal holidays — use +`scripts/workers/business_days.py` calendar): gather all signed+pending paper +filings, **group by destination agency** (each MAC; NPI Enumerator Fargo; each +state agency), merge each group into ONE print job + a cover sheet (PW sender, +destination, date, enclosed count, per-item order#/provider/NPI/form), one +**Priority Mail** envelope per agency. Mark each order mailed with batch date + +tracking. Phase 1: generate per-agency batched PDF + cover sheet + manifest for a +human to print & drop. Phase 2: wire a print-mail API (Lob/Click2Mail). + +## MAC routing +855s route by the provider's **State** (855I/B/O all say "the MAC that services +your State"). Mailing addresses: CMS.gov/Medicare/Provider-Enrollment-and- +Certification. Need a **state → MAC → mailing-address** table to address envelopes ++ to group the daily batch. `practice_state` intake field drives this. diff --git a/docs/plans/Plan.md b/docs/plans/Plan.md index 030ab29..0417587 100644 --- a/docs/plans/Plan.md +++ b/docs/plans/Plan.md @@ -1,252 +1,164 @@ # Plan -## Status: COMPLETE (all phases implemented + validated 2026-06-02) +Offer healthcare regulatory filings as a **two-tier model** (internal mechanics; +the "paper" alternative is NEVER surfaced to the client): -| Phase | What | Status | -|-------|------|--------| -| 1 | Hazmat/PHMSA handler + product (`hazmat-phmsa`, $149) | ✅ | -| 1.5 | Order-form bundle/mutual-exclusion enforcement (server-side) | ✅ | -| 2 | State-trucking intake form (slug-gated) + REQUIRED_FIELDS + admin-todo fields | ✅ | -| 2.5 | BOC-3 authority-aware (active/pending/revoked/none) + upsell-approve follow-ups | ✅ | -| 2.6 | Pipeline activation gating (`require_active` edges, FMCSA poll, waiting_on_activation) | ✅ | -| 3 | State emissions (non-CA) product `state-emissions` ($199) | ✅ | -| 4 | Order landing pages for all state/hazmat/emissions slugs (48 pages build) | ✅ | -| Adv | Prerequisite-aware DOT lookup + state recommendations | ✅ | -| 5 | Campaign builder deficiency segments + LP routing + --list-segments | ✅ | -| Val | Consistency checker (24/24) + campaign segment test (synthetic) | ✅ | +- **Standard service (default):** we handle the filing end-to-end. Client signs + ONE certification and we submit + track to confirmation. (Internally this is the + paper path — 855 to the MAC, CMS-10114 to the NPI Enumerator in Fargo — but we + never say "paper" to the client; it's just "we file it for you.") +- **Expedited (faster, framed positively at intake):** we ask if they can + **electronically grant us CMS I&A Surrogate access**. We position this as + *reducing the number of steps on our end so we can bulk-file faster* — never as + "the alternative is paper." If yes, we file online same-day. If no, we silently + fall back to Standard. Same price either way; surrogacy is a delegation, not + password sharing. -**Remaining ops (not code):** create the 6 Listmonk source-campaign templates and -set their `CAMPAIGN_*_ID` envs (`CAMPAIGN_FOR_HIRE_ID`, `CAMPAIGN_IRP_IFTA_ID`, -`CAMPAIGN_INTRASTATE_ID`, `CAMPAIGN_WEIGHT_TAX_ID`, `CAMPAIGN_EMISSIONS_ID`, -`CAMPAIGN_HAZMAT_ID`). Until set, those segments are reported by -`--list-segments` but skipped by the scheduled run. Optional follow-up: a -client-side incompatibility UX hint in the order form (server already enforces). +This pass verifies the no-login paper path per service, reconciles the doc/code +contradiction, and makes "standard = paper / expedited = surrogate" consistent +everywhere it's surfaced. ## Goal - -Make every trucker deficiency type we flag actually *fulfillable*: each flagged -deficiency must have (1) a service handler that can complete the work, (2) a -checkout/order path, and (3) an intake form that collects **all** information -that handler needs before the job runs. Only after fulfillment is complete and -verified do we extend the campaign builder to email those deficiency segments. +Every healthcare service (federal sold + state/adjacent to add) has a verified +**Standard (paper, no-login)** path and, where a CMS/portal exists, an +**Expedited (surrogate/delegated)** upsell. No surface should ever present +surrogate as *required* when a paper path exists; paper is always the default. ## Scope / affected areas +- `api/src/routes/checkout.ts` (~L2155-2207) — already has the two-tier copy, BUT + treats NPPES update/reactivation as **"online-only, surrogate required"** with + NO standard fallback (`NPPES_ONLY_SLUGS`, `hasNppesOnly`). The directive says: + give those a Standard paper path too (CMS-10114 to Fargo) and make surrogate the + *expedited* option, not the only option. +- `scripts/workers/services/npi_provider.py` — `access` strings call NPPES + update/reactivation "NPPES via CMS I&A surrogate access (online-only)". Must + become "Standard: CMS-10114 paper to NPI Enumerator (Fargo), client signs; + Expedited: NPPES via I&A surrogate." Also `_STANDARD_FILING_SLUGS` currently + excludes `nppes-update` (it has no 855) — needs a CMS-10114 paper path instead. +- `scripts/document_gen/templates/cms855_pdf_filler.py` — working 855 paper path + (fills 855I/B/O/A + cert-page signature anchor). **Gap:** no CMS-10114 filler + for the NPPES standard path → likely a small new `cms10114_pdf_filler.py`. +- `docs/healthcare-no-login-value-add.md` — already documents the CMS-10114 paper + path; promote it to the canonical "Standard vs Expedited" matrix. +- `site/src/pages/services/healthcare/{npi-revalidation,medicare-enrollment}.astro` + — already describe expedited/surrogate; npi-reactivation/nppes-update order + pages need the same two-tier framing. +- `site/src/components/intake/steps/NpiIntakeStep.astro` + intake manifest — add + the surrogate-access question framed positively: "Can you electronically grant + us CMS I&A Surrogate access? It lets us bulk-file faster and cuts steps on our + end." Optional; declining is fine and never mentions paper. The captured answer + routes fulfillment internally (surrogate vs our Standard path). +- `docs/state-healthcare-compliance-opportunities.md` + `new-sector-compliance- + targets.md` — extend the two-tier classification to state/adjacent services. -Deficiency flags in play (live counts): -for_hire (19,811), interstate_irp_ifta (19,761), intrastate_authority (14,081), -state_emissions (12,424), state_weight_tax (6,289), state_permit (3,418), -mcs150_overdue (4,539), hazmat (514), zero_fleet (134). - -Files / systems: -- `scripts/workers/services/__init__.py` — handler registry -- `scripts/workers/services/state_trucking.py` — IRP/IFTA/weight-tax/permit/intrastate handler (admin-todo only today) -- `scripts/workers/services/mcs150_update.py` — MCS-150 + reactivation (real FMCSA filing) -- `scripts/workers/services/boc3_filing.py` — BOC-3 (Playwright) -- **NEW** `scripts/workers/services/hazmat_phmsa.py` — only fully-missing fulfillment path -- `site/src/lib/intake_manifest.ts` — per-service intake steps + pricing/meta -- `site/src/components/intake/steps/DOTIntakeStep.astro` — unified DOT intake (no state-trucking sections today) -- **NEW** `site/src/components/intake/steps/StateTruckingIntakeStep.astro` — state filing fields -- `api/src/routes/compliance-orders.ts` — `COMPLIANCE_SERVICES` (products) + `REQUIRED_FIELDS` (validation; **none defined for any DOT/state-trucking slug today**) -- `api/src/routes/checkout.ts` — slug allowlist -- `api/src/routes/dot-lookup.ts` — recommended-services mapping -- **NEW** `site/src/pages/order/*.astro` — landing pages for state-trucking + hazmat (none exist) -- `scripts/build_trucking_campaigns.py` — campaign builder (extend last) - -## Key findings (grounding) - -1. **Fulfillment handlers already exist** for ~98% of flags. The single fully - missing path is **hazmat / PHMSA registration** (no handler, product, page). -2. **State-trucking intake collects nothing.** All 13 state slugs map to - `["review"]` in `intake_manifest.ts` with a comment "info collected at - checkout" — but checkout collects no per-filing fields. So IRP has no - vehicle/weight/jurisdiction data, IFTA has no fleet/base-state, NY HUT has no - vehicle list, intrastate-authority has no insurance/authority-type, etc. The - handler's admin todo is therefore incomplete and an admin must chase the - customer for data. -3. **`REQUIRED_FIELDS` has zero entries** for any DOT or state-trucking slug, so - the API performs no intake validation for these orders. -4. **No dedicated order landing pages** for IRP/IFTA/state-tax/permit/intrastate - or hazmat. Checkout works by slug, but campaign emails have no clean LP to - drive conversions. -5. **State emissions** flags (NY/CO/MD/NJ/MA/etc., 12,424) only map to a product - for CA (CARB via `ca-mcp-carb`). Non-CA emissions have no product — decide - whether to build or fold into existing state DOT/permit service. +Out of scope this pass: building the actual surrogate Playwright automation +(expedited fulfillment can stay human-in-PECOS for now), email-stream machinery, +pricing changes. ## Approach (concrete ordered steps) - -### Phase 1 — Close the hazmat fulfillment gap (only fully-missing path) -1. Add `HazmatPHMSAHandler` in `scripts/workers/services/hazmat_phmsa.py` - (admin-assisted, mirrors `state_trucking.py`: creates admin_todo with PHMSA - registration steps, sends status email). Slug `hazmat-phmsa`. -2. Register `"hazmat-phmsa": HazmatPHMSAHandler` in `services/__init__.py`. -3. Add product to `COMPLIANCE_SERVICES`, meta to `intake_manifest.ts`, slug to - checkout allowlist. - -### Phase 1.5 — Order-form incompatibility enforcement (bundles vs components, dupes) -Today the batch endpoint (`compliance-orders.ts` POST `/batch`) only dedupes and -hard-codes one case (drop standalone `fcc-499a` when `fcc-499a-499q` present). -There is no general rule preventing a customer from selecting a **bundle plus its -own components** (e.g. `dot-full-compliance` + `mcs150-update` + `boc3-filing`, or -`state-trucking-bundle` + `irp-registration`), or other incompatible combos. This -double-charges and creates duplicate filings. - -Build a single source of truth for service relationships and enforce it: -- Add `BUNDLE_COMPONENTS` map (bundle slug -> component slugs) covering - `dot-full-compliance`, `state-trucking-bundle`, `new-carrier-bundle`, - `fcc-full-compliance`, plus the DB `service_bundles` rows. -- Add `INCOMPATIBLE_PAIRS` / mutually-exclusive groups (e.g. `usdot-reactivation` - vs `carrier-closeout`; `fcc-499a` vs `fcc-499a-499q`; emergency-temp-authority - vs mc-authority where applicable). -- Server-side (authoritative) in `/batch`: when a bundle is present, **drop any of - its components** from the cart (keep the bundle), reject hard-incompatible pairs - with a clear error, and keep dedup. Generalize the existing 499a special-case - into this map. -- Client-side (order form / cart UI): disable/grey out a component when its parent - bundle is selected (and vice-versa: selecting all components suggests the bundle), - and prevent selecting mutually-exclusive options, with an inline explanation. - Mirror the server map so UX matches enforcement. - -### Phase 2 — Make state-trucking intake actually collect required data -4. Build `StateTruckingIntakeStep.astro` (one shared step, sections shown by - slug, mirroring `DOTIntakeStep.astro`'s section-gating pattern): - - Carrier identity: legal name, DOT#, MC#, base state, contact (prefill from DOT lookup). - - IRP/IFTA: power units w/ VIN+plate+gross-weight rows, operating jurisdictions, fuel type. - - Weight-distance (OR/NY/KY/NM/CT): vehicle list + gross weights + (OR) declared combined weight. - - CA MCP+CARB: fleet engine model-years for CARB Clean Truck Check, CA# if any. - - Intrastate authority: authority type, insurance carrier + policy#, cargo, BOC-3 on file? - - State DOT / OSOW: as needed. -5. Update `intake_manifest.ts`: replace `["review"]` with - `["state-trucking", "review"]` for the 13 slugs; wire the step into the Wizard. -6. Add `REQUIRED_FIELDS` entries in `compliance-orders.ts` for each state-trucking - slug + the DOT slugs (mcs150, ucr, boc3, dot-registration, mc-authority, etc.) - so intake is validated server-side. Mirror handler "Intake data needed" docstrings. -7. Update `state_trucking.py` handler to read + surface the new intake fields in - the admin todo (so admins get vehicle lists, jurisdictions, insurance, etc.). - -### Phase 2.5 — Make BOC-3 authority-aware (preexisting authority handling) -The BOC-3 attaches to a carrier's operating authority (MC/FF/MX docket). Today -`boc3_filing.py` only reads `commonAuthorityStatus`/`contractAuthorityStatus`/ -`brokerAuthorityStatus` to print a status string and otherwise always files a -fresh BOC-3. That can be wrong/wasteful depending on the preexisting authority. -Add branching off the live FMCSA authority state: -- **Active authority:** file/refresh BOC-3 only. (current behavior) -- **Pending authority:** file BOC-3 + flag that active insurance must be on file - for the authority to activate; create follow-up todo. -- **Revoked/inactive authority:** file BOC-3 **and** flag/upsell reinstatement - (OP-1 reinstatement + $80 gov fee, route via `usdot-reactivation`/`mc-authority` - reinstatement branch). BOC-3 alone does not reinstate. -- **No authority (USDOT only):** BOC-3 has nothing to attach to — flag that MC - authority (`mc-authority`) is likely needed first; do not silently file. -Implementation: have `process()/handle()` read full authority status (reuse -`_check_boc3_status`, expanded to return structured fields), select the branch, -adjust the admin-todo + customer email, and emit a `recommended_followups` list -the order timeline / upsell can surface. No automatic charge for the follow-up — -surface it for the customer/admin to approve. - -### Phase 2.6 — Prerequisite/activation gating (wait for FMCSA "active", not just "submitted") -There are real FMCSA dependency chains where a downstream filing must wait for an -upstream item to be **active at the agency**, not merely submitted by us. The -existing `pipeline_orchestrator.py` models ordering via `wait_for`, but a step is -treated as satisfied when `pipeline_step_status == "completed"` (= our handler -finished), which is NOT the same as FMCSA showing it active. - -True prerequisites to enforce: -- **MC Authority (OP-1)** requires an **active USDOT**. -- **Authority activation** requires **BOC-3 on file + insurance (BMC-91) on file**, - then a **mandatory ~21-day vetting/protest period** before it goes active. - BOC-3 + insurance CAN be filed while authority is pending (parallel OK). -- **IRP / IFTA / intrastate-authority / UCR** that depend on *active* authority - (or active USDOT) must wait for that activation, not just for the prior order to - be submitted. - -Implementation: -- Add an "activation gate" to the orchestrator: for dependency edges flagged - `require_active: true`, poll FMCSA (mobile QC API for USDOT status; L&I for - authority/BOC-3/insurance) and only mark the dependency satisfied when the - agency reports active. Until then, hold the downstream step in a - `waiting_on_activation` state with a next-poll timestamp. -- Encode the 21-day authority vetting window as an expected-activation estimate so - the timeline/customer comms set correct expectations. -- Expand `PIPELINES` edges with `require_active` flags (USDOT→MC, USDOT→IRP/IFTA/ - UCR/intrastate, authority-active→IRP-for-hire/intrastate). -- Standalone (non-bundle) orders: when a single service is ordered whose - prerequisite isn't active yet, surface a clear "blocked until X is active" - status + recommended prerequisite order rather than filing prematurely. - -### Phase 3 — Decide + handle state emissions (non-CA) -8. Either: (a) add a generic `state-emissions` service handler+product covering - NY/CO/MD/NJ/MA clean-truck/ACT programs, or (b) map those flags to - `state-dot-registration` / advisory-only. (Open question — see below.) - -### Phase 4 — Order landing pages -9. Create `site/src/pages/order/*.astro` for: irp-ifta (combined), state weight - taxes (one templated page per state or a single state-aware page), - ca-mcp-carb, intrastate-authority, hazmat-phmsa. Reuse existing order-page - layout (e.g. `boc3-filing.astro`, `ucr-registration.astro` as templates). - -### Phase 5 — Extend campaign builder (only after 1-4 verified) -10. Add new campaign segments to `build_trucking_campaigns.py` keyed off - `deficiency_flags`: for-hire/BOC-3+UCR, IRP/IFTA, intrastate-authority, - state weight-tax (per-state), CA MCP/CARB, hazmat. Each links to its LP. -11. Create the corresponding Listmonk source campaigns (templates) and wire IDs. +1. **Confirm the CMS-10114 paper path** for NPPES data updates AND NPI + reactivation (not just initial enumeration): paper CMS-10114 mailed to NPI + Enumerator (PO Box 6059, Fargo ND), client signature only, no I&A login. + Cite the official source. This is what makes NPPES services "Standard, no + login" instead of surrogate-required. (We currently have no CMS-10114 PDF.) +2. **Lock the two-tier matrix for the 6 federal services.** For each slug record: + Standard path (form, mail destination, client's only action = sign / nothing), + Expedited path (surrogate scope: PECOS and/or NPPES; what client grants), + and whether expedited is even applicable (screening = public, no portal, so + no expedited tier). Confirm the 855 wet-signature-cannot-be-delegated caveat + applies to Standard; surrogate covers Expedited. +3. **Reconcile checkout.ts.** Plan the edit so: + - NPPES update/reactivation no longer present surrogate as *required* — remove + the "online-only / required for those" language. + - The surrogate ask is framed as the **faster, fewer-steps** option, never + against a "paper" alternative. Audit/strip the existing copy that leaks + mechanics to the client (e.g. "we'll send your CMS-855 to sign", "submit it + to your MAC") — replace with neutral "we file it and track it for you." + - Declining surrogate silently routes to our Standard path; no client-facing + mention of paper/MAC/Fargo/form numbers. +4. **Reconcile npi_provider.py.** Update `access` strings + `_STANDARD_FILING_SLUGS` + so nppes-update/reactivation generate the CMS-10114 (or 855 reactivation) + paper + e-sign, and the admin todo reflects Standard-default / Expedited-if- + surrogate-granted. Mirror the existing 855 generate→upload→esign flow. +5. **MAC + Fargo routing rule.** Document which Standard filings go where + (855 → provider's MAC by state/jurisdiction; CMS-10114 → Fargo). Confirm + `practice_state` intake field drives MAC envelope addressing. +7. **Daily batched-mail fulfillment (Standard path).** Design the operational + flow for paper filings that are signed + pending submission: + - On each **postal working day morning** (skip weekends + federal/USPS + holidays), gather ALL signed-and-pending paper filings, **group by + destination agency/address** (each MAC, the NPI Enumerator in Fargo, each + state Medicaid/CLIA agency). + - For each destination, **merge all that day's documents into one print job** + plus a **cover sheet** (Performance West sender, destination agency, date, + enclosed-count, per-item list of order# / provider / NPI / form). One + **Priority Mail envelope per agency** → saves postage + handling. + - Mark each included order as "mailed" with the batch date + tracking #. + - Build this as a worker (a `daily_paper_batch` job + a cover-sheet generator + in document_gen), mirroring existing worker/cron patterns + (`infra/cron/*`, `business_days.py` for the working-day calendar). Decide: + fully-automated print-to-PDF batch that a human prints + drops, vs. + print-API (Lob/Click2Mail) auto-mail. **Recommend phase 1 = generate the + per-agency batched PDF + cover sheet + manifest for a human to print & mail; + phase 2 = wire a print-mail API.** +8. **Extend two-tier to state/adjacent services.** For each (State Medicaid + enroll/reval, CAQH re-attest, payer credentialing, DEA renewal, state CSR, + PDMP, CLIA, license renewal+CME) classify: + - **Standard (no-login):** paper/mail-in form + one client signature? + (e.g. CLIA = CMS-116 paper to state; Medicaid varies — some paper.) These + feed the same daily batched-mail flow, grouped by their state agency. + - **Expedited (delegated):** the lightest legitimate delegation that avoids a + client *login* — e.g. CAQH "authorized administrator/org" grant, Medicaid + "delegated official" on the app, payer EDI/CAQH attestation rights, an LOA. + Distinguish "client signs one authorization once" (acceptable, still + no-login) from "client must log in / share credentials" (never). + - Flag services that are genuinely portal-only with NO paper standard so + marketing never claims "no logins" for them. +9. **Sequence the rollout** by (Standard-feasibility first) × revenue, and write + the two doc updates + the small code edits (checkout.ts, npi_provider.py, + optional cms10114_pdf_filler.py, and the daily batch worker + cover sheet). + All gated on approval. ## Validation (how each part is verified) - -- **Handlers:** unit-invoke each handler with a synthetic `order_data` dict in a - throwaway script; assert an `admin_todos` row is created with the expected - fields and (dev mode) no live filing fires. Confirm `SERVICE_HANDLERS` resolves - every new slug. -- **Intake completeness (the core ask):** write a check that, for every slug, - cross-references `REQUIRED_FIELDS[slug]` against the fields the intake step - emits and against the keys the handler reads — fail if a handler-needed field - is never collected. This is the verifiable "we collect all needed info" metric. -- **Checkout/products:** assert every flagged slug exists in `COMPLIANCE_SERVICES`, - `SERVICE_META`, checkout allowlist, and `SERVICE_HANDLERS` (one consistency test). -- **Order pages:** build site (`astro build` / existing build script) and confirm - each new `/order/` route renders; smoke-load locally. -- **Campaign builder:** run `build_trucking_campaigns.py --dry-run` and assert - each new segment selects a nonzero, deduped audience pointing at a valid LP. -- **End-to-end:** place a test order per new slug through checkout in dev, verify - intake validation blocks missing fields, and the handler produces a complete - admin todo. +- **Source-grounded:** every Standard (paper) and Expedited (delegation) claim + cites the official instruction (CMS-855 instr, CMS-10114 instr, CMS-116 for + CLIA, state Medicaid enrollment page, CAQH/payer docs). No unsourced claim. +- **Consistency sweep:** after edits, `grep` for "online-only" / "required" / + "surrogate" / "no login" across checkout.ts, npi_provider.py, and the service + pages — confirm none present surrogate as *required* where a paper path exists, + and Standard is the default on every surface. +- **Standard path mechanism check:** `cms855_pdf_filler.fill_cms855("855i",...)` + still yields filled PDF + cert anchor (read-only smoke render). If CMS-10114 + filler is built, same smoke render proves a signable Fargo-bound PDF. +- **Expedited path check:** confirm the surrogate-grant CTA + `ia_surrogacy` + success action + admin todo together let a human file in PECOS/NPPES same-day; + surrogate scope wording matches what CMS I&A actually grants (PECOS vs NPPES). +- **Matrix completeness:** every service (federal + state) has Standard + (where + applicable) Expedited rows with a definite client-action column and source; + no "unknown" and no service that's portal-only-but-marketed-as-no-login. ## Open questions / decisions - -**RESOLVED (per user 2026-06-02):** -1. **BOC-3 follow-ups + prerequisite blockers → upsell-approve, advise pre-order - where possible.** Two layers: - - *Pre-order advisory (preferred):* extend the existing DOT Compliance Check - tool (`site/public/tools/dot-compliance-check/`) + `dot-lookup` recommended - services to be **prerequisite-aware**. When a recommended service needs an - active prerequisite (e.g. IRP needs active authority), show the dependency, - pre-select the prerequisite, order the cart correctly, and state the ~21-day - authority activation expectation — all before payment. - - *Post-order upsell-approve:* when a handler discovers a blocker mid-fulfillment - (e.g. BOC-3 ordered but authority revoked → needs reinstatement), write a - `recommended_followups` entry on the order and render a one-click, pre-filled - "Add this service" card on the order timeline/portal. Customer confirms + pays - via the existing checkout. **No auto-charge.** - - *Standalone checkout guard:* if a service is bought directly and its prereq - isn't active at FMCSA, warn + offer to add the prereq rather than filing - something unfulfillable. -2. **State emissions (non-CA): build a real product.** Add a `state-emissions` - service (handler + product + intake + page) covering NY/CO/MD/NJ/MA clean-truck - / Advanced Clean Trucks programs (CA stays on `ca-mcp-carb`). -3. **Pre-order advisory: yes.** Covered by 1 above — reuse the compliance-check - tool as the advisory surface. - -**STILL OPEN:** -4. Pricing for `hazmat-phmsa` and `state-emissions`. - **DEFAULT:** `hazmat-phmsa` = $149 (admin-assisted PHMSA registration; gov fee - $25 placardable-hazmat reg billed at cost). `state-emissions` = $199 - (NY/CO/MD/NJ/MA clean-truck / ACT advisory + registration assist). -5. Vehicle-list intake fidelity. - **DEFAULT:** lightweight up front — collect fleet count + base/operating states - + fuel type + gross-weight bracket at intake; collect full per-vehicle - VIN/plate/weight rows via a post-order follow-up form only when the specific - filing requires it (IRP, weight-distance taxes). Keeps conversion high. -6. Standalone-order prereq guard. - **DEFAULT:** warn + offer to add the prerequisite (pre-selected), allow override - ("file anyway"). Never a hard block. -7. Long-term home for the reviewable plan (`docs/plans/` used here; no - `side_panel` tool in this harness). +1. **NPPES update/reactivation Standard path:** is CMS-10114 paper-to-Fargo + accepted for *changes/reactivation* today (RESOLVE in step 1)? If yes, both + become Standard-default + surrogate-expedited. If CMS has gone online-only, + they stay surrogate-required and we say so honestly. (Directive assumes paper + works as Standard; step 1 verifies.) +2. **Build `cms10114_pdf_filler.py` this pass?** Needed for a real NPPES Standard + path. Recommend: yes if step 1 confirms paper, mirroring the 855 filler; + otherwise defer. +3. **State Medicaid scope:** classify the mechanism generically + verify top ~5 + states by our lead density now; template the rest. (50-state research is a + separate effort.) +4. **CAQH / payer credentialing tier:** almost certainly Expedited = "client + signs one authorization, no login" (category C). Confirm we market that as + "no logins for you" with precise wording. +5. **Expedited pricing/positioning:** RESOLVED — expedited is **not** a paid + tier and we never expose "paper" as the alternative. It's an intake question + framed positively ("electronically granting surrogate access lets us bulk-file + faster / fewer steps on our end"). If yes → online; if no → silent Standard + fallback. Same price. No price delta, no separate product, no mention of paper. +6. **Client-facing messaging rule:** never surface mechanics to the client — + no "paper", "CMS-855", "CMS-10114", "MAC", or "Fargo" in any client copy + (cold email, order page, checkout, intake). The only client-visible choice is + the positive surrogate question; everything else is "we file it for you." This + applies to the checkout copy audit in step 3. diff --git a/scripts/workers/mac_routing.py b/scripts/workers/mac_routing.py new file mode 100644 index 0000000..1dc48f5 --- /dev/null +++ b/scripts/workers/mac_routing.py @@ -0,0 +1,118 @@ +"""State -> Medicare Administrative Contractor (MAC) routing for CMS-855 mailing. + +CMS-855 paper applications are mailed to the provider's *designated MAC*, which is +determined by the provider's STATE/jurisdiction (per the official 855I/855B/855O +instructions: "The MAC that services your State is responsible for processing your +enrollment application"). + +This module maps each US state/territory -> its A/B MAC name + provider-enrollment +mailing address. It is used to: + 1. Address the outgoing envelope for the Standard (paper) filing path. + 2. Group the daily batched mailing by destination agency (one Priority Mail + envelope per MAC address). + +IMPORTANT — verify before first live mail run: MAC jurisdiction assignments and +(especially) the provider-enrollment PO Box addresses change when CMS re-competes +a jurisdiction. Confirm each MAC's current enrollment mailing address against the +MAC's website / CMS.gov/Medicare/Provider-Enrollment-and-Certification right +before the first batch, then keep this table the single source of truth. + +Jurisdiction assignments below reflect the long-standing A/B MAC map. Addresses +are marked TODO where they must be filled from the MAC's current enrollment page. +""" +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class MAC: + key: str # short stable id (used for batch grouping) + name: str # MAC contractor + jurisdiction + # Provider-enrollment mailing address lines. VERIFY before live use. + address_lines: tuple[str, ...] + + +# A/B MAC jurisdictions (long-standing assignments). Addresses TODO-verify. +NORIDIAN_JE = MAC("noridian_je", "Noridian Healthcare Solutions — Jurisdiction E", + ("Noridian Healthcare Solutions", "Provider Enrollment (JE)", + "P.O. Box — VERIFY", "Fargo, ND — VERIFY")) +NORIDIAN_JF = MAC("noridian_jf", "Noridian Healthcare Solutions — Jurisdiction F", + ("Noridian Healthcare Solutions", "Provider Enrollment (JF)", + "P.O. Box — VERIFY", "Fargo, ND — VERIFY")) +NOVITAS_JH = MAC("novitas_jh", "Novitas Solutions — Jurisdiction H", + ("Novitas Solutions", "Provider Enrollment (JH)", + "P.O. Box — VERIFY", "Mechanicsburg, PA — VERIFY")) +NOVITAS_JL = MAC("novitas_jl", "Novitas Solutions — Jurisdiction L", + ("Novitas Solutions", "Provider Enrollment (JL)", + "P.O. Box — VERIFY", "Mechanicsburg, PA — VERIFY")) +FCSO_JN = MAC("fcso_jn", "First Coast Service Options — Jurisdiction N", + ("First Coast Service Options", "Provider Enrollment (JN)", + "P.O. Box — VERIFY", "Jacksonville, FL — VERIFY")) +PALMETTO_JJ = MAC("palmetto_jj", "Palmetto GBA — Jurisdiction J", + ("Palmetto GBA", "Provider Enrollment (JJ)", + "P.O. Box — VERIFY", "Columbia, SC — VERIFY")) +PALMETTO_JM = MAC("palmetto_jm", "Palmetto GBA — Jurisdiction M", + ("Palmetto GBA", "Provider Enrollment (JM)", + "P.O. Box — VERIFY", "Columbia, SC — VERIFY")) +CGS_J15 = MAC("cgs_j15", "CGS Administrators — Jurisdiction 15", + ("CGS Administrators", "Provider Enrollment (J15)", + "P.O. Box — VERIFY", "Nashville, TN — VERIFY")) +WPS_J5 = MAC("wps_j5", "WPS Government Health Administrators — Jurisdiction 5", + ("WPS GHA", "Provider Enrollment (J5)", + "P.O. Box — VERIFY", "Madison, WI — VERIFY")) +WPS_J8 = MAC("wps_j8", "WPS Government Health Administrators — Jurisdiction 8", + ("WPS GHA", "Provider Enrollment (J8)", + "P.O. Box — VERIFY", "Madison, WI — VERIFY")) +NGS_J6 = MAC("ngs_j6", "National Government Services — Jurisdiction 6", + ("National Government Services", "Provider Enrollment (J6)", + "P.O. Box — VERIFY", "VERIFY")) +NGS_JK = MAC("ngs_jk", "National Government Services — Jurisdiction K", + ("National Government Services", "Provider Enrollment (JK)", + "P.O. Box — VERIFY", "VERIFY")) + +# State (USPS) -> MAC. Based on the established A/B MAC jurisdiction map. +STATE_TO_MAC: dict[str, MAC] = { + # Noridian JE + "CA": NORIDIAN_JE, "HI": NORIDIAN_JE, "NV": NORIDIAN_JE, + "AS": NORIDIAN_JE, "GU": NORIDIAN_JE, "MP": NORIDIAN_JE, + # Noridian JF + "AK": NORIDIAN_JF, "AZ": NORIDIAN_JF, "ID": NORIDIAN_JF, "MT": NORIDIAN_JF, + "ND": NORIDIAN_JF, "OR": NORIDIAN_JF, "SD": NORIDIAN_JF, "UT": NORIDIAN_JF, + "WA": NORIDIAN_JF, "WY": NORIDIAN_JF, + # Novitas JH + "AR": NOVITAS_JH, "CO": NOVITAS_JH, "LA": NOVITAS_JH, "MS": NOVITAS_JH, + "NM": NOVITAS_JH, "OK": NOVITAS_JH, "TX": NOVITAS_JH, + # Novitas JL + "DE": NOVITAS_JL, "DC": NOVITAS_JL, "MD": NOVITAS_JL, "NJ": NOVITAS_JL, + "PA": NOVITAS_JL, + # First Coast JN + "FL": FCSO_JN, "PR": FCSO_JN, "VI": FCSO_JN, + # Palmetto JJ + "AL": PALMETTO_JJ, "GA": PALMETTO_JJ, "TN": PALMETTO_JJ, + # Palmetto JM + "NC": PALMETTO_JM, "SC": PALMETTO_JM, "VA": PALMETTO_JM, "WV": PALMETTO_JM, + # CGS J15 + "KY": CGS_J15, "OH": CGS_J15, + # WPS J5 + "IA": WPS_J5, "KS": WPS_J5, "MO": WPS_J5, "NE": WPS_J5, + # WPS J8 + "IN": WPS_J8, "MI": WPS_J8, + # NGS J6 + "IL": NGS_J6, "MN": NGS_J6, "WI": NGS_J6, + # NGS JK + "CT": NGS_JK, "ME": NGS_JK, "MA": NGS_JK, "NH": NGS_JK, + "NY": NGS_JK, "RI": NGS_JK, "VT": NGS_JK, +} + +# NPI Enumerator paper address (NPPES / CMS-10114 paper path) — not a MAC, but a +# destination the daily batch groups by, same as a MAC. +NPI_ENUMERATOR = MAC( + "npi_enumerator", "NPI Enumerator (NPPES / CMS-10114 paper)", + ("NPI Enumerator", "P.O. Box 6059", "Fargo, ND 58108-6059"), +) + + +def mac_for_state(state: str) -> MAC | None: + """Return the designated MAC for a USPS state code, or None if unknown.""" + return STATE_TO_MAC.get((state or "").strip().upper()) diff --git a/scripts/workers/services/npi_provider.py b/scripts/workers/services/npi_provider.py index 2aa6ab4..2bb645b 100644 --- a/scripts/workers/services/npi_provider.py +++ b/scripts/workers/services/npi_provider.py @@ -46,7 +46,8 @@ _SLUG_META = { "enrollment record, and submit. Capture the PECOS tracking ID." ), "access": ( - "Standard: prepare CMS-855I/B/R, provider signs cert, submit to MAC. Expedited: file in PECOS via CMS I&A surrogate access." + "Standard (default): prepare CMS-855I/B, provider e-signs cert, mail to MAC (daily batch). " + "Expedited (if surrogate granted at intake): file in PECOS via CMS I&A surrogate access, same-day tracking." ), "priority": "high", }, @@ -54,11 +55,12 @@ _SLUG_META = { "name": "NPI Reactivation", "portal": "https://nppes.cms.hhs.gov/", "action": ( - "Reactivate the deactivated NPI in NPPES. Verify the deactivation " + "Reactivate the deactivated NPI. Verify the deactivation " "reason, correct any stale data, and re-certify the record." ), "access": ( - "NPPES via CMS I&A surrogate access (online-only)." + "Standard (default): prepare CMS-855I (reactivation reason), provider e-signs, mail to MAC (daily batch). " + "Expedited (if surrogate granted): reactivate online in PECOS/NPPES via I&A surrogate access." ), "priority": "high", }, @@ -71,7 +73,8 @@ _SLUG_META = { "changes and certify." ), "access": ( - "NPPES via CMS I&A surrogate access (online-only)." + "Standard (default): prepare CMS-10114 NPPES update, provider e-signs, mail to NPI Enumerator (Fargo, daily batch). " + "Expedited (if surrogate granted): update online in NPPES via I&A surrogate access." ), "priority": "normal", }, @@ -83,7 +86,9 @@ _SLUG_META = { "Confirm taxonomy, practice location, and authorized official." ), "access": ( - "Standard: prepare CMS-855, provider signs cert, submit to MAC. Expedited: file in PECOS via CMS I&A surrogate access." + "Standard (default): prepare CMS-855, provider e-signs cert, mail to MAC (daily batch). " + "Expedited (if surrogate granted): file in PECOS via CMS I&A surrogate access, same-day tracking. " + "NOTE: org (855B) enrollment/revalidation requires the CMS application fee paid via PECOS before submission." ), "priority": "high", }, @@ -109,16 +114,22 @@ _SLUG_META = { "record. Set the next revalidation reminder." ), "access": ( - "Standard CMS-855 filing for the enrollment/revalidation piece; NPPES + PECOS via CMS I&A surrogate access; screening is public." + "Standard (default): CMS-855 paper filing for the enrollment/revalidation piece, mailed to MAC (daily batch); screening is public (no client action). " + "Expedited (if surrogate granted): NPPES + PECOS pieces filed online via CMS I&A surrogate access." ), "priority": "high", }, } # Slugs whose fulfilment includes a CMS-855 (auto-filled official form, signed -# via the secure e-sign link, then submitted to the provider's MAC). The -# bundle's revalidation piece is handled by the dedicated revalidation order it -# spawns, so it is not listed here. +# via the secure e-sign link, then submitted to the provider's MAC via the daily +# batched mailing). The bundle's revalidation piece is handled by the dedicated +# revalidation order it spawns, so it is not listed here. +# +# nppes-update is intentionally NOT here: its Standard path is the CMS-10114 (NPPES +# update mailed to the NPI Enumerator in Fargo), handled by a separate filler when +# built. Until then, nppes-update falls to the admin todo (Expedited if surrogate +# granted, otherwise manual CMS-10114 prep). _STANDARD_FILING_SLUGS = { "npi-revalidation", "npi-reactivation", diff --git a/site/src/pages/services/healthcare/medicare-enrollment.astro b/site/src/pages/services/healthcare/medicare-enrollment.astro index 12dea9c..9622ad2 100644 --- a/site/src/pages/services/healthcare/medicare-enrollment.astro +++ b/site/src/pages/services/healthcare/medicare-enrollment.astro @@ -51,10 +51,10 @@ const description = "New to Medicare or adding a practice location? We assemble
  • We submit it to your Medicare Administrative Contractor (MAC) and track it through to approval.
  • - Need it filed faster? Choose expedited at - checkout and add us as a Surrogate in CMS Identity & - Access. We then file directly in PECOS under our own credentials and - capture the tracking ID the same day. You never share your login. + Want it filed faster? If you can electronically grant us + Surrogate access in CMS Identity & Access, we can file + even faster — it cuts steps on our end. You never share your login, + and it's never required; we'll file it for you either way.
    diff --git a/site/src/pages/services/healthcare/npi-revalidation.astro b/site/src/pages/services/healthcare/npi-revalidation.astro index cbff0ab..d19838c 100644 --- a/site/src/pages/services/healthcare/npi-revalidation.astro +++ b/site/src/pages/services/healthcare/npi-revalidation.astro @@ -69,12 +69,13 @@ const description = "CMS requires every enrolled provider and supplier to revali

    How we file for you

    - You pick whichever path is easiest. You never share your CMS - password. + We handle the whole filing and track it to confirmation. The only thing + we may need from you is a one-minute signature on a secure link. + You never share your CMS password.

      -
    • Standard filing (nothing to set up). We complete the correct CMS-855, you approve and sign the certification from a secure link in about a minute, and we submit it to your Medicare Administrative Contractor (MAC) and track it to confirmation.
    • -
    • Expedited filing (needs a CMS account). You add us as a Surrogate in the CMS Identity & Access (I&A) system; we then file directly in PECOS under our own credentials and capture the tracking ID the same day.
    • +
    • We do the work. We complete the correct CMS-855 (855I for individuals, 855B for groups), you approve and e-sign the certification from a secure link, and we submit and track it to confirmation.
    • +
    • Optional: speed it up. If you can electronically grant us Surrogate access in the CMS Identity & Access (I&A) system, we can file even faster — it cuts steps on our end. You never share your password, and it's never required.