Canonical source: STATUS_STEDI.md. This page is generated by docs/scripts/sync-handbook.mjs. Edit the source file in the repo; changes appear here on the next build.
Stedi Insurance / RCM — Worktree Status & Handoff
Worktree: /Users/G/wt-stedi · Branch: wt/stedi · Base commit: 6da8638b2 (wip checkpoint)
Last updated: 2026-07-10
Worktree rules (from George): Work and commit here only. Do NOT touch
/Users/G/basisor any other worktree. When work is ready, ask George to mergewt/stedi→master. Standing guardrails still apply: nofirebase deploy/ no platform publish without explicit confirmation; never paste the Stedi API key into chat; confirm before any edit that could touch payments/Stripe; PHI/codes-only to any LLM (no PHI in logs or prompts).
Business model (why this feature exists)
Model A enablement — charge a % of ERA-confirmed net collections (metered on the 835 paid
amount) via Stripe, no fund custody. Public brand "Ready Practice"; AI assistant "Atlas".
Clearinghouse = Stedi (JSON). Base host https://healthcare.us.stedi.com; auth = raw key in
Authorization header (NOT Bearer). Test payer id STEDI (needs a PROD key; test key → 403).
Authoritative deep-dive: docs/claude/insurance-rcm.md.
What's DONE and committed on this branch
The full pipeline #1–#31 is built (see the task list / docs/claude/insurance-rcm.md). Highlights:
- Eligibility 270/271 (
stedi_check_eligibility) + UI; readiness validator (#20); card scan (#19). - 837P mapper + PCN store; scrubber (#21); manual claim entry (#29); review+submit UI (#30).
- Lifecycle:
stedi_webhook→ 999/277CA/835 state machine; ERA reconcile by PCN (#13) — verified end-to-end (submitted→accepted→paid). - Merged Billing dashboard
/billing(#15): insurance + cash-pay, side sheets, comma separators. - #31 Claim correction & resubmission —
resubmit_ofinstedi_submit_claim+ "Correct & resubmit" panel inClaimDetailDrawer(InsuranceClaimsSection.tsx).
Just landed this session — #43 Client coverage & benefits tracking (L1 + L2)
Client request: "mark what each client is covered for + their allowance so far per covered period." Industry pattern = benefits & accumulator tracking (Jane App / Tebra / Headway style).
- L1 — coverage record (team-facing note):
PatientBenefitCoveragemodel embedded onPatientInsurance.coverage(model_clinic.py): covered CPTs, copay, coinsurance, deductible met/total, OOP met/max, visit allowance + used, unit, period, notes,source('manual' | 'eligibility').- Coverage & Benefits card on each policy in the client profile Insurance section
(
ClientDetailPage.tsx): visits-used progress bar (amber when ≤2 left), copay/coinsurance/ deductible/OOP facts, period, note, "From eligibility / Manual" badge. CoverageEditorModal— staff enter/adjust everything by hand. Saves viaupdateDoctoinsurance.coverage(dotted path), stampssource:'manual'.
- L2 — auto-fill + auto-decrement:
parse_271_summary()(stedi_client.py) now parses HSD visit/unit limits →visitLimit/visitsRemaining/visitUnitonEligibilityCheck.stedi_check_eligibilityseeds/refreshescoveragefrom the 271 (derives deductible-met, OOP-met, visits-used from the payer's "remaining" figures). Never overwrites asource:'manual'record; sparse 271s never wipe prior fields.stedi_submit_claimauto-incrementscoverage.visitsUsed(firestore.Increment(1)) when the payer accepts a new claim (skips resubmits; failure never blocks a claim).
Verification done: backend py_compile clean on the 3 files; basisflow-web tsc --noEmit clean.
Safety checked: the addition is pure-additive; default_model_config uses extra='ignore', and the
Stripe-webhook ClinicUserDetails.model_validate sites in functions_payment.py only read fields
(don't re-persist the whole doc), so coverage won't crash payments or get stripped. See memory
project_clinic_user_webhook_model_coupling.
✅ DEPLOY STATE — #43 shipped 2026-07-10 via ship.sh
Both functions deployed from master (George-approved greenlight) and verified live.
| What | Deployed? | Verified |
|---|---|---|
| #43 L1 (manual coverage card) | N/A — pure client read/write | Card + editor live on localhost |
#43 L2 seeding (stedi_check_eligibility w/ coverage seed + HSD parse) | ✅ 2026-07-10 | End-to-end: real 271 on Jane Doe → coverage seeded source:'eligibility' (copay 25, ded 500, OOP 7000). Tool: tools/test_stedi_coverage_seed.js |
#43 L2 decrement (stedi_submit_claim w/ visitsUsed increment) | ✅ 2026-07-10 | End-to-end (issue #174): sandbox claim accepted → visitsUsed 0→1, other fields untouched; rejected claim did NOT count. Tool: tools/test_stedi_visit_decrement.js (service date must be ≤ Stedi's UTC transaction date) |
#31 resubmit (stedi_submit_claim resubmit_of) | ✅ (refreshed by this deploy) | prior session |
Post-deploy checks done: both functions ACTIVE, all 7 stedi_* functions intact (no silent
deletions), allUsers run.invoker survived on both. Ships now go through
.github/scripts/ship.sh --approved-by-george <branch> <fns> — never raw firebase deploy.
Note: roles/iam.serviceAccountTokenCreator was granted to george@basishealth.io on
basis-hybrid@appspot.gserviceaccount.com (2026-07-10) so test tools can mint custom tokens.
OPEN DECISIONS for George
Deploy the two functions— DONE 2026-07-10, verified (see deploy state above).Exercise the L2 auto-decrement— DONE 2026-07-10 (issue #174), verified both paths.- #43 L3 go/no-go — patient-responsibility calc + charge prep at booking (map membership
ServiceBillingCPTs ↔coverage, compute copay vs coinsurance-on-allowed vs deductible-not-met, reconcile vs 835). Deferred — touches the payment/booking path, needs explicit go.
Roadmap — top remaining (full list in the task tracker)
- #12 ERA enrollment — DEPLOYED + verified 2026-07-11 (issue #175, closed). George's live draft test exercised the full chain; found+fixed the provider-orphan bug (providerId now persists immediately + already-exists recovery). Hard finding: demo clinic can NEVER complete ERA enrollment — its fake NPI 1234567893 is ERA-enrolled in another Stedi account (ERA is exclusive per NPI). Real-NPI E2E lands with #16 (BAA) / #18. Platform hosting publish of the ERA tab still pending George's explicit go. Cosmetic: unauth'd stedi callables return 500 (not 401) — module-wide pattern.
- #14 Collections metering → Stripe (2.75% fee on 835
paid) — scheduled with George (session of 2026-07-11+). - Autonomous batch 2026-07-11 (BUILT, awaiting one batched ship): #36 CMS-1500 PDF (issue #196;
verified live — 763KB PDF via
claimReference.correlationId, NOT the stored transactionId which is the X12 controlNumber) · #24/25 claim status 276/277 (issue #200; contract OK, Test Payer doesn't support 276 + test mode 403s → happy path needs a real payer) · #23 Atlas pre-submission review (issue #201; prompt contract verified live, codes-only/no-PHI) · #22 prior-auth chip (issue #202) · #27 AAA rejection explainer (issue #204; auto-retry half deliberately skipped — no silent identity mutation). Ship command:ship.sh --approved-by-george wt/stedi stedi_claim_pdf,stedi_claim_status,stedi_claim_ai_review,stedi_check_eligibility(submit path untouched by this batch). - #16/#17 RESOLVED (George, 2026-07-25): %-of-collections fee is cleared and there is NO separate Stedi BAA to sign — do not re-flag as metering/go-live blockers. #18 (external) start real ERA enrollments.
- AI tier that justifies the fee: #23 pre-submission review · #27 eligibility retry + denial explainer · #41 learned denial prediction · #35 Stedi MCP → Atlas.
- Depth: #22 prior-auth (271-derived today; CMS-0057 PARDD FHIR API ~2027 is the clean future path — no compliance work owed by us now, it's a payer-side rule) · #24/#25 status · #32 discovery · #33 COB · #36 CMS-1500 PDF · #38 attachments (275) · #39 underpayment · #40 GFE · #42 payer contracts.
Path to production (real claims → real payment → transparency) — assessed 2026-07-11
Software is largely ready; the gaps are enrollment execution, per-clinic go-live data, and controlled first claims.
- ERA enrollments executed per clinic × payer (#18) — tooling shipped (#175); real NPIs required. Gap: payer follow-up tasks/documents (signed forms) have API endpoints but NO UI — use the Stedi portal for those until built.
- Clinic go-live checklist — billing NPI+TaxID+address+contact email, rendering NPIs on staff, payer IDs linked, codes+rates set. Mostly enforced piecemeal at submit-time; no single readiness screen (small build).
- Controlled first-claims path — see testing ladder in the session notes: sandbox (done) → validate-mode 'T' against REAL payers (built: Validate button; run per pilot payer) → real eligibility on real staff policies (safe) → supervised pilot clinic, 1 payer × few claims, watch 999→277CA→835. NEVER submit claims for services not rendered (fraud). Adjudication+payment can only be proven on a real claim.
- Hardening before volume — stedi_webhook has NO auth (Stedi documents none; eventId dedupe only): add a shared-secret Authorization header on the Stedi webhook destination + verify it. Add stale-claim monitor (no 277CA/835 after N days → surface on /billing). Secondary/COB claims (#33) not built — exclude secondary-insurance claims from pilot scope.
- Not blocking clinics getting paid: #14 metering (that's OUR revenue), L3 patient-responsibility charging (deferred; 835 patient-owes amounts are already visible in the drawer).
2026-07-12 late session — pipeline proven on REAL Stedi infrastructure
#14 metering shipped (review-hardened: idempotency key, rate clamp, dedupe-on-processed, 3-month catch-up, distinct-claim counting). Webhook destination CONFIGURED in Stedi portal (Basic credential; secret in SM as STEDI_WEBHOOK_SECRET; guard enforces via compare_digest). Root causes of "Test Payer silence" fixed: sandbox claims now ISA15='T' (P is never adjudicated); Test Payer 835 enrollment executed LIVE via #12 tooling (demo NPI 1903240517 / TaxID 963723717 — unique fake ids required); real webhook events are metadata+transactionId → content fetched via reports v2 (835 shape has patientControlNumber/claimPaymentAmount; 277 uses patientAccountNumber + WQ=Accept). Full E2E verified: submit → adjudicate → deliver → fetch → match → paid + remittance + ledger (sandbox- flagged). Pending next ship: terminal-status downgrade guard. Stripe invoicing dormant until a clinic is enabled (settings/rcmBilling.enabled).
Suggested next step when you reopen here
Confirm with George: deploy L2 now, or keep iterating on L1 via localhost first? Then, if greenlit, pick up #12 (ERA enrollment) and #14 (collections metering → Stripe) — that's the actual revenue mechanism and the highest-leverage remaining work.