Mirrors the DDMA flow: persistent Chrome profile + credential-change logout,
PingOne login with email MFA selection and OTP from app or browser,
Eligibility & Plan Details search (masked Subscriber ID/SSN), family member
picked by DOB and re-verified on the plan details page, PDF saved to the
patient's Eligibility folder and status set ACTIVE/INACTIVE.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Claim form: AI claim/preauth prefill adds rows beyond the initial 10
instead of dropping codes 11+
- CDT lookup: "D6010 #5, 9, 11, 12" expands to one line per tooth, and
the chat graph rebuilds such lists from the message if the LLM drops items
- Eligibility: match an existing patient by name+DOB when the insurance ID
changed (e.g. MassHealth → CCA) instead of creating a duplicate patient
- CCA PreAuth: services grid scrolls to reach rows 11+
- CCA and Tufts SCO PreAuth: tentative service date = today + 3 days
- CCA (eligibility/claim/preauth) and DentalHub (claim/preauth): keep Chrome
open, blank and minimized, after a job so the next job reuses it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Preauth column: BCBS appointments fill the 2024 ADA form with box 1
"Request for Predetermination/Preauthorization" (claim saved as PREAUTH),
saved to Documents > PreAuth as ADA_PreAuth_*.pdf, then back to schedule
- Claim form PreAuth tab: "BCBS PreAuth (ADA Form)" button
- Preauth forms list procedure dates 6 days after the preauth date
- PREAUTH claims create no payment; saved BCBS preauths count as preauthorized
- Schedule: appointments with a completed preauth show dark gray
- "Download Filled Claim/Preauth Form" also zips the BCBS preauth forms
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Schedule: last column is the Preauth column; "Claim for Column with AI" is now
"Claim/Preauth for Column with AI" — that column's appointments go to PreAuth
(MH, CCA, Tufts SCO, United/DentalHub; other insurances are skipped), the rest
to Claim. Preauthorized appointments (hasPreauth) aren't resubmitted
- PreAuth payloads carry appointmentId; appointment X-rays/attachments are sent
with MH/CCA/UnitedDH/Tufts preauths (shared appendAppointmentFiles helper)
- Patient form: policy holder is searchable from patients in the DB, plus
"Relationship to Policy Holder/Subscriber" (Self, Spouse, Dependent Child,
Other). New Patient columns policyHolderPatientId, policyHolderRelationship;
browser-side schema updated so the fields aren't stripped on save
- ADA form: box 12-14 filled from the linked policy holder's record; box 18
ticks the relationship (Self when the patient is the holder)
- AI notes: a bare "2PA" is an image count, not tooth #2 — restored before CDT
lookup so it maps to D0220 #8 / D0230 #24
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- "Download Claim PDF for Column" -> "Download Filled Claim Form": ZIP of the
ADA forms the app filled (e.g. BCBS) for appointments on the selected
schedule date, all columns; Selenium-submitted claims are not included
(GET /api/claims/filled-forms replaces /batch-pdf)
- After a BCBS claim from "Claim for column with AI", go back to the
schedule even when the queue is finished
- Schedule remembers the selected date for the tab, so returning from the
claims page keeps the working date instead of jumping to today
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Claim for column with AI: BCBS patients auto-generate the 2024 ADA form
(no tab) and save it to the patient's Documents > Claims folder; the
manual BCBS button saves there too (new POST /api/claims/:id/ada-form)
- Saved BCBS form sets the claim to REVIEW; appointments with a claim
number or REVIEW/APPROVED status show as claimed (dark gray)
- CDT lookup: "comp on #18 O, 20 O, 21 O" applies composite to every tooth
- BCBS fee schedule (procedureCodesBCBS.json, copied from DDMA prices)
- Office Contact: Legal Name field; "Dental Office Name (DBA)" label
- ADA form: 48 = legal name, DBA, address; 56 = address only; 53 date =
claim date; 23 and 26 left blank; 43 = No unless appointment notes say
a prosthesis is replaced (then Yes, with 44 prior date when given)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pontic previously fell through to keyword search and always hit D6241.
Now an explicit material on the pontic wins; otherwise it follows the
crowns in the same request, defaulting to D6245. Also drop 'not for
united' from the D6245 description.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Backend insurance ID check now accepts letters + digits (BCBS IDs have
a letter prefix), matching the frontend schema; create route validates
the normalized body
- Patient.state column (default "MA") with State select on the patient
form; ADA claim form uses the patient's state instead of the office's
- Rebuild stale packages/db compiled .js so the runtime schemas include
new fields (state, office billing NPI fields, Telnyx settings)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New GET /api/claims/:id/ada-form fills the 2024 ADA Dental Claim Form
(J43024) with payer address, patient/subscriber, CDT lines, fees,
missing teeth, billing (office) NPI and treating NPI; signatures blank
- "BCBS Claim (ADA Form)" button saves the claim and opens the PDF;
Claim All routes BCBS patients to it; "Print ADA Form" in claim view
- Settings > NPI Providers: "Add Office NPI" (billing name, NPI, license,
TIN) and optional per-provider license number
- Migration: office_contact billing fields, NpiProvider.licenseNumber
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Patient's actual age now decides adult vs child prophylaxis instead of
trusting literal "adult prophy"/"child prophy" wording in notes. Applies
bidirectionally (corrects both D1110-on-a-child and D1120-on-an-adult) and
also resolves ambiguous bare "prophy"/"cleaning" input purely from age.
Wired into all single- and batch-claim chat flows.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The WebDAV server was narrowed to serve only backups/ (to stop exposing
.env), but the receiver still pulls /backups, /chat-history and /uploads,
so every sync failed with "directory not found". Serve the app root
filtered with --include to just those three folders; everything else
(.env, source, config/key files) returns 404.
Sync Now now saves the typed source IP/port before pulling, since the
pull reads the saved config.
stop-app.sh also frees port 8080: kill -9 on the backend orphans the
rclone child, which then keeps the port with stale settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CCA step1 was treating the harmless "password set to expire" banner as a
fatal error, closing the browser before the eligibility popup/report
could be captured. Now it short-circuits to success once the patient
popup renders and ignores password-expiration notices.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes the transaction-type radio button (was matching nothing; now
targets the real data-testid), and adds a Deltains fee schedule
(seeded from DDMA's prices) so Deltains claims stop falling back to
MassHealth pricing.
Adds a full "Deltains Claim" button next to Delta MA Claim, mirroring
the DDMA claim flow across frontend, backend queue/routes, and a new
Selenium worker. Reuses the existing Deltains eligibility login/OTP
code and was iteratively wired against the live portal (patient roster
search, per-service-line fields, tooth number/surface selects, review
and submit). Also fixes chatbot/insurance-alias carrier recognition so
"deltains" resolves correctly instead of falling back to MassHealth.
Remaining benefit amount didn't show until a manual page reload
because invalidateQueries was placed after an early return (chatbot
claim flow) for MH checks, and was missing entirely from the 7
other-provider eligibility buttons.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- AI-column auto-submit and manual "Claim All" dispatchers in claim-form.tsx
had no United Ucard branch, so United Ucard claims silently fell through
to the MassHealth (MH) submit handler instead of opening the United Ucard
site.
- Added a United Ucard fee schedule (copied from United SCO as a starting
point) and wired it into the frontend price-mapping/mismatch-check code
and the backend fee-schedule update route, which previously had no
schedule for United Ucard and fell back to MassHealth's prices.
- Selenium claim worker: when the member search returns multiple coverage
records, it now tries the last row first (existing default) and falls
back to the first row only if the "Member Details" link never appears
(i.e. the last row was an inactive record), instead of only ever trying
one or the other.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a "United Ucard Claim" button (claims/preauth insurance form) that
submits through a full Selenium pipeline mirroring the Tufts SCO claim
worker: frontend handler -> backend route/queue processor -> Python
agent, reusing the persistent login/OTP session from the existing
United Ucard eligibility check. Implements the UHC Dental claim wizard
end-to-end (Provider Information, Patient Information search/select,
Claim Information procedures + questions, Upload Documents, Review and
Sign, Submit); the final confirmation-page PDF/claim-number capture is
still a TODO stub pending that page's DOM.
Also wires United Ucard into the AI chatbot's eligibility flow
(insuranceAliases.json, siteKeyToAutoCheck, clarification options), and
fixes a bug where the AI-column batch claim feature silently defaulted
United Ucard patients to MassHealth because batchColumnDeriveSiteKey
had no matching branch and no job-dispatch case for UNITED_UCARD.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a full login/OTP/search/capture flow for United Ucard (OneHealthcareID
-> UHC Dental provider portal), mirroring the United SCO/DDMA pattern:
persistent Chrome profile, OTP polling, member eligibility search, and PDF
capture saved to the patient's Eligibility folder.
Also fixes a validation bug in the browser-safe patient schema where
email/address/city/zipCode/gender/phone were optional but not nullable,
rejecting patient records with null values on those fields during edits
(e.g. "Expected string, received null").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Auto-fill service line price when a recognized CDT code is typed
(e.g. D2740), instead of requiring a separate "Map Price" click
- Fix chatbot preauth prefill using an empty DOB for price lookup,
causing age-tiered codes (D2740) to price as $0
- Re-apply fee-schedule pricing with the loaded patient DOB before
MassHealth preauth auto-submit
- Add a "Recement Crown" (D2920) combo button distinct from "Crown"
(D2740) to avoid picking the wrong code/price
- Fix AI note parsing to prioritize "recement" over "crown" regardless
of word order or tooth-number format (e.g. "#7 crown recement")
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
closeClaim() was clearing chatbotAutoSubmitSiteKey synchronously right
after the selenium job kicked off, before the job's async completion
handler could check it. This was masked for all but the last claim in
the batch because closeClaim() also navigated away (unmounting the
page) whenever more claims were queued. On the last claim the page
stayed mounted, so the completion handler read a stale cleared value
and let the PDF preview popup through. Track the site key in a ref
that survives closeClaim() and is cleared only after the job's own
completion handler consumes it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 2PA with no explicit tooth numbers now defaults to D0220 #8 / D0230 #24
- Claims submitted via the AI chat column no longer auto-open the PDF preview modal
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Frontend uses the Web Speech API for speech-to-text and text-to-speech
(no key needed). Adds POST /api/translate backed by Google Cloud
Translation API v2, gated on GOOGLE_TRANSLATE_API_KEY (placeholder
added to .env/.env.example, unset by default).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Google Translate-style side-by-side layout with per-side language
pickers on the patient connection page. UI shell only for now, no
translation provider wired up yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Twilio now handles only the in-browser dial pad (Access Token + browser-Device
outbound TwiML); Telnyx takes over SMS, inbound voicemail, the AI voice
conversation loop, and staff-triggered outbound calls via TeXML. Adds
TelnyxSettings model, Communication.provider discriminator, new comms/routes
modules, and a real Telnyx Settings page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add remainingBenefit and thirdPartyInsurance columns to Patient; extract both
from the MassHealth accumulator/member-detail pages during the eligibility
check and surface them in the eligibility table
- Add an updated-date display and a toggle to suppress the post-check PDF
viewer popup (default off) on the insurance eligibility page
- Rename CMSP-prefixed PDF filenames and UI copy to MH, since CMSP and MH now
share the same worker
- Fold CMSP into MassHealth/MH across patient/claim insurance-type dropdowns
and the chatbot's eligibility routing, since they're the same insurance
- Remove auto-download of saved eligibility PDFs; downloading is now manual
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Only block appointment creation on explicit INACTIVE status, since
scrapes for some plan types (e.g. MassHealth Child Medicaid) can land
on UNKNOWN without actually finding an ineligible record.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Reset conversation stage to a fresh start on every new voice call
(recall and reminder alike), and on every hangup/timeout/goodbye,
so an abandoned or completed call never leaves a patient stuck
mid-flow for their next call or text.
- Add a staleness check to the SMS flow: after a long idle gap,
check whether the patient's reply still reads as a continuation
before resuming the stored stage, falling back to a friendly
"it's been a while" re-greeting when unsure.
- Fix parseTime() to handle Twilio's speech-to-text output, which
renders "pm"/"am" with periods and often drops the colon in a
spoken time (e.g. "100 p.m." -> 1:00 PM) — this was silently
failing and causing the AI to loop asking for a time it had
already been given.
- Extend the voice Gather timeout so the AI doesn't hang up on a
patient who just needs a few seconds to respond.
- Reword the recall-call opening line and rework the post-"yes"
prompt to ask for day and time together.
- Fix rclone WebDAV server to serve only the backups folder instead
of the entire backend app directory (was exposing .env and source).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
packages/db/usedSchemas/browser.ts (the browser-resolved copy of the Patient
zod schema, kept separate from the Node copy since it can't import
Prisma-generated code) was missing several fields added to the real schema
over time — preferredLanguage, gender, insuranceProvider, insuranceId,
groupNumber, policyHolder, and the PLAN_NOT_ACCEPTED status. Since the schema
isn't strict, those fields were silently stripped from every patient
create/update in the browser with no validation error, so edits to them
never reached the server.
Also fixes the AI call greeting always playing in English regardless of the
patient's preferred language — the greeting text was a single hardcoded
string, unlike every later turn in the conversation which already branched
by language.
Adds a new recall conversation flow (recall-graph.ts) triggered from the
Patient Connection "AI Call" button: offers an existing patient a routine
exam & cleaning, classifies their yes/no reply, then books directly onto
the schedule (Column A, 30-minute slots, real conflict checking against
office hours and existing appointments) instead of only capturing a
preference for staff to follow up on.
Also adds a "Voice call" label in the message thread to distinguish call
transcripts from SMS, and updates the Call Templates settings UI with an
editable recall greeting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AI phone calls now route through the same routePatientTurn() rule engine
as SMS (office-hours checks, slot-conflict checks, MassHealth eligibility,
staff escalation) instead of a freeform LLM chat, and speak in the
patient's preferred language (English/Spanish). Adds a separate "Call
Templates" settings section so spoken wording can be customized
independently from SMS wording.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an "AI Call" option alongside Call/SMS/Chat that places an outbound
call where Lisa converses live with the patient via Twilio speech
gather + TTS, using the same conversational AI as chat. Includes a
separate, user-editable Call Template (Settings > AI Chat/Call Settings)
so the call greeting can be customized independently of SMS templates.
Also fixes the outbound-call webhook URL to always use the fixed public
Twilio hostname (CLOUDFLARE_HOST) instead of deriving it from the
triggering request, since staff-browser requests arrive over the
LAN-only hostname, which Twilio can never reach.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces fragile whole-screen text matching with a screenshot-diff step that
finds each dialog's actual pixel bounds, then restricts every subsequent AI
locate/click to that cropped region — eliminating false matches from text
elsewhere on screen (title bars, side panels). Adds column-boundary and
row-height detection so the patient row and Exam procedure click positions
are computed geometrically instead of relying on repeated fuzzy AI guesses
for visually similar neighbors. Also adds per-run screenshot/debug-crop
backups and a structured run.log for diagnosing failed runs, plus a
cmd:move primitive on the Windows agent for pre-click confirmation crops.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a Windows-side agent (screenshot/click/type over HTTP) plus backend
services to locate UI elements via vision and drive an existing-patient
appointment flow in Open Dental, wired into the Copy/Type Agent page and
socket progress updates.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- ccaPreAuthProcessor: keep claim status as PREAUTH after successful
submission instead of jumping straight to APPROVED, matching the
Tufts SCO/UnitedDH/MassHealth preauth flows; also tag PNG fallback
screenshots with image/png instead of the default application/pdf
so they render in the Documents preview modal.
- claims-recent-table: add a PREAUTH status case so it displays as
"Submitted" instead of falling through to the raw enum value.
skipAppointmentAttachmentMerge was blanket-set for any autoSubmit path
(chatbot, Claim with AI batch automation, insurance-status auto-check),
so screenshots/files attached directly on the appointment were silently
dropped from the MassHealth Selenium upload whenever a claim was
auto-submitted. Now the merge is only skipped if the automated flow
actually supplied its own files.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Chatbot: submitting with only a screenshot attached (no text) now triggers the same
"AI Detect & Check Eligibility" flow as the Copy Agent page
- detect-eligibility-info now also extracts the visible insurance payer name and picks
Tufts SCO auto-check when both Sun Life and DentaQuest are detected, else MassHealth
- Chatbot-attached files are now handed off to both claim and preauth forms unconditionally,
not just claims
- TuftsSCO preauth Selenium worker: verify-and-retry the Tooth field (typing was silently
getting reset by duplicate-procedure-code warning banners), add a final re-verification
pass across all rows, fix acknowledgement-checkbox targeting/verification, and verify
the "Next step" click actually advances the wizard instead of trusting a blind click
- selenium-claim route no longer re-appends previously saved appointment
attachments when the chatbot's autoSubmit flow already sent every intended
file, preventing an unwanted extra upload (e.g. attach 2 -> Selenium sees 3).
- Add a "Take screenshot" button to the AI chatbot's attachment bar, reusing
the same capture/crop flow as the appointment editor, so claims and
preauths can be submitted from the chatbot with a live screenshot attached.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add "surg ext" alias mapping to D7210 in the AI CDT lookup.
- Add GET/DELETE endpoints for appointment attachments, and load existing
attachments in the appointment edit modal (they were uploaded correctly
but never re-fetched, so they appeared to vanish on reopen).
- Fix claim-form.tsx so previously-saved appointment attachments are always
loaded (even when a claim already exists) instead of only when creating a
brand-new claim, preventing them from being silently wiped on save.
- Merge existing appointment attachments into every insurance submit handler
(MassHealth, CCA, DDMA, UnitedDH, TuftsSCO) so Selenium submission always
includes files saved earlier via the Schedule editor, not just files
uploaded in the current claim-form session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an optional Provider (NPI) select to the Edit Appointment form,
sourced from Settings > NPI Providers. The AI "Claim for Column" batch
flow now prefers the appointment's own provider when resolving which
provider Selenium should select on the insurance site (e.g. MassHealth
rendering provider dropdown), falling back to the existing per-procedure
selection, active claim, or first configured provider.
Also resyncs packages/db's committed compiled schema/client artifacts
with their .ts sources — they had drifted out of sync (some untouched
since July), which was silently stripping the new npiProviderId field
via stale .strict() zod schemas.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Member ID and DOB in the AI chat "patient found" confirmation card are now
editable inputs, and edits are persisted back to the patient record before
proceeding so corrections don't revert to stale values.
- Fix auto-lookup-by-member-ID effect on the insurance status page clobbering
a hand-edited DOB with the stale DB value during chatbot prefill.
- CMSP (under-14) eligibility flow was missing the appointment-creation hook
that the adult MassHealth flow already had, so under-14 patients never got
scheduled after a chatbot-initiated "eligibility & appointment" request.
- For already-known patients, the chatbot was creating the appointment before
running the eligibility check instead of after; now eligibility is always
checked first and the appointment created only afterward, and only when the
resulting patient status is ACTIVE.
MassHealth eligibility lookups via the AI chatbot only matched existing
patients on memberId+DOB, so patients imported from PDF extraction (which
have a member ID but no stored birthday) were treated as new/unknown even
though they already exist in the system.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets staff attach files to an appointment directly from the edit form via
local disk upload, picking a previously-saved scanned attachment, or an
inline screenshot with crop-before-save (matching the Copy Agent page's
crop UX), instead of detouring through the Copy Agent page. Also fixes a
latent data-loss bug in claim-form's save-for-appointment call, which
replaced all AppointmentFile rows instead of merging with existing ones.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- AI claim queue now uses previously saved AppointmentProcedure data (with a derived insurance site key) when an appointment has no notes but has saved procedures, instead of skipping with "No notes on this appointment"
- selenium-claim route now reads saved AppointmentFile attachments from disk when they weren't re-uploaded in the browser, so images/PDFs already attached via "Select Procedures" reach the MassHealth submission
Adds From/To date pickers and CCA PA / United PA / Tufts PA / All recent PA
buttons above the Upload claim documents section; button logic is stubbed
for now.
Selecting a patient from search results only stored the ID, not the
patient object. Closing the dropdown reset the search term and reverted
the list to "recent 50" patients, which often didn't include the just
selected patient, leaving nothing to render in the trigger. Also removed
a redundant prefillPatient reset that nulled the fallback display before
the list swap even happened.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets a user override the default MassHealth check on the chatbot's
eligibility-id-ready confirm card (e.g. switch to United SCO, CMSP,
DDMA, Delta Dental, Tufts SCO, or CCA) before running the check.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>