mdvin User Manual

User Manual

mdvin

The complete guide to the platform: what it is, how each role uses it, how the two modules connect, and the full API surface. Including the guards the server enforces no matter what any client sends.

Getting started#

mdvin is a US healthcare orchestration platform that runs Prior Authorization (PA) and Value-Based Care (VBC) on one decision spine. Instead of two disconnected products, a single patient record, workflow engine, versioned policy library and append-only audit trail sit underneath both modules, so an authorization is a step inside a care journey, a care gap knows when it is blocked on a payer decision, and every state change either side makes is visible to the other in real time.

The platform is API-first: every screen you see is a projection of server-side state, and every rule — who may act, which transitions are legal, what a denial requires. Is enforced on the server, never in the browser. That design has a practical consequence for you as a user: if the UI lets you press a button that the rules forbid, the server refuses and tells you why. Several parts of this manual invite you to try exactly that, because watching the platform refuse an illegal action is the fastest way to understand how it protects the people in it.

The demo environment#

Demo environment, all data is synthetic; no PHI. Every patient, member ID, payer, policy and clinical value in this environment is fictional and generated for demonstration. Nothing you do here touches real health information. Security controls (authentication, role gating, audit chaining, tenant isolation) run exactly as they would in production, they are being demonstrated, not simulated.

Signing in#

  1. Open /app. You will see the sign-in screen with the synthetic-data demo banner and the platform points (CMS-0057-F-aligned SLA clocks, Governed AI, Defensible by design).
    What you should seeAn email + password form, and a click-to-fill panel listing the five demo roles.
  2. Enter one of the credential pairs from the table below and press Sign in.
    What you should seeThe app shell loads with a left navigation tailored to your role — a Care Manager lands on My Work; a UM Nurse or Medical Director lands directly on the Review Queue.
  3. Sessions use short-lived JWT access tokens (15 minutes) with a refresh token behind them. If a request returns 401 after a long idle period, sign in again.
Account lockout is real. Five failed password attempts lock the account for 15 minutes and further sign-ins return 423 Account temporarily locked. The login response is deliberately generic (Invalid credentials) so the system never reveals whether an email exists.

Demo credentials#

Five seeded users cover the whole demo. Passwords are published intentionally, this is a synthetic environment.

RoleEmailPasswordWhat this role demonstrates
Care Manager
Jane Smith
jane@demo.acoCareManager!2026x My Work queue, interventions, engagement, launching PAs
VBC Manager
Alex Rivera
alex@demo.acoVbcManager!2026xx Dashboard aggregates, population views
UM Nurse (RN)
Sam Nurse
nurse@demo.payerUmNurse!2026xxxx PA review queue, AI advisory, pend/approve. Attempt a denial and the server will refuse it (an RN cannot deny)
Medical Director
Dr. A. Patel — MD, Ophthalmology
md@demo.payerMedDirector!2026 Adverse determinations with credential + specific reason
Medical Director 2
Dr. B. Rivera — MD, Ophthalmology
md2@demo.payerMedDirector2!26x Independent appeal reviews (the original denier cannot decide an appeal)
Auditor
Avery Auditor
audit@demo.acoAuditor!2026xxxx Read-only decision traces
Platform Admin (backend)
Pat Admin
admin@demo.acoPlatformAdmin!26 Backend administration at https://mdvin.com/app → Administration: runtime branding (rename the product), field registry (add/remove/rename/hide fields per entity, patient, care gap, intervention, prior auth, task). All changes audited and applied platform-wide instantly.

Your first session, by role#

  • Care Manager (jane@demo.aco), start in My Work: one Critical task is waiting (post-ED follow-up for John Doe). Open his 360, then follow the VBC guide end-to-end on Sarah Martinez's retinal-exam gap. It is the one that requires a PA, so you will see both modules cooperate.
  • VBC Manager (alex@demo.aco), start on the Dashboard: every KPI is computed live from the same rows its drill-down shows, so numbers always reconcile. Then browse Patients and open a 360.
  • UM Nurse (nurse@demo.payer), open the Review Queue. If it is empty, have the Care Manager submit a PA first (how). Run the AI advisory, approve or pend, then press Deny and read the server's refusal. That refusal is the product's compliance story in one screen.
  • Medical Director (md@demo.payer). Open the same queue and complete an adverse determination properly: 5-state criterion outcomes, a specific reason code and text, your MD credential recorded on the trace.
  • Auditor (audit@demo.aco), open Authorizations and view any PA's decision trace. Note what you cannot do: no review/decide buttons appear, and any write you attempt (say, Start Intervention on an open gap) is refused by the server with 403. Read-only is enforced server-side, not by the UI.

Core concepts#

Six ideas explain the whole platform. Everything in the role guides below is a combination of these.

Patient 360#

The Patient 360 is the single clinical workspace for one member: identity and coverage, the risk tier (HIGH / RISING / MEDIUM / LOW) with the evidence behind it, all open and closed care gaps, and every intervention with its full event timeline. Risk is never a bare score. Each contributing factor carries its source and date (e.g. HbA1c 9.4% · lab · 2026-06-14), and the risk model version (apex-risk v3.2 in the demo) is displayed with it, so the number is explainable and reproducible.

Care gaps & their states#

A care gap is a measurable difference between recommended and delivered care, tied to a versioned quality measure (e.g. CDC-HBA1C, measure package MY2026.1). Every gap answers two questions on its face: why is it open (the evidence that opened it) and what closes it (the clinical evidence the measure requires). A gap moves through these states:

StateMeaning
CANDIDATEMeasure engine has flagged the member; not yet confirmed into the work stream
OPENConfirmed gap, no active work, this is where Start Intervention appears
IN_PROGRESSAn intervention is actively working the gap
SCHEDULEDThe closing service has an appointment on the books
AWAITING_AUTHBlocked on a pending prior authorization, the cross-module wait state
SATISFIEDMeasure numerator met per the engine; pending confirmation into CLOSED
CLOSEDClosed on qualifying clinical evidence, the only way a gap closes
EXCLUDEDMember meets a measure exclusion; gap leaves the denominator
NOT_APPLICABLEMeasure does not apply to this member
REOPENEDEvidence lapsed or was invalidated; the gap is live again
PA approval never closes a gap. An approved authorization changes what is allowed, not what was done. Only clinical completion evidence that matches the measure (see auto-closure) moves a gap to CLOSED.

Intervention lifecycle#

An intervention is the unit of care-team work on one gap: who owns it, what has happened, what comes next. Its state machine is enforced server-side. Any transition not in this diagram returns 409 Illegal intervention transition.

Teal = the states involved in the cross-module handshake. Dark = terminal success. Red = exception outcomes.

Prior Authorization lifecycle#

A PA request has eleven states. In the demo environment intake validation passes synchronously, so a successful submission lands directly in IN_REVIEW with its SLA clock already running.

Rule & policy packages#

Coverage rules are data, not code. A rule package (e.g. UHC-MA-EYE-014) is versioned and effective-dated, keyed by payer + line of business + service codes, and carries the criteria themselves — each with an ID, plain-language text and the evidence type that satisfies it. The policy resolver picks the newest active version matching the member's coverage and the requested CPT. When a PA is created, the package key and version are snapshotted onto the request, so the decision is always judged. And can always be replayed. Against the exact criteria in force at submission, even after the package is updated.

The audit trail#

Every consequential action. Sign-ins and failures, every patient view, every transition, every decision — appends an event to a hash-chained, append-only audit log: each row carries the hash of the previous row, so any tampering breaks the chain, and an integrity self-check can recompute it end-to-end at any time. Events record the actor, role, object, patient, structured detail (criteria version, credential, denial code…) and a correlation ID that ties one user action to every row it produced. The current correlation ID is shown in the app's top bar. Quote it when reporting an issue and the operator can reconstruct your exact request.

Roles & permissions#

Ten roles are defined. A role is a fixed set of permissions checked on every API call, the UI merely hides what you cannot do; the server is what refuses. The five demo users cover the roles marked below; the others exist in the model for production deployments.

Permission matrix#

Role patient.readvbc.writevbc.admin task.readanalytics.read pa.createpa.readpa.review pa.pendpa.decide policy.readpolicy.write audit.readadmin.all
care_manager YesYesYesYesYes
vbc_manager YesYesYesYesYesYes
executiveYesYes
providerYesYesYesYes
pa_coordinatorYesYesYes
um_nurse YesYesYes
medical_director YesYesYesYes
policy_adminYesYes
platform_adminYesYes
auditor YesYesYes

= has a seeded demo user. admin.all satisfies any permission check — platform_admin effectively holds every permission. Note that medical_director is the only role holding pa.decide, and only care_manager, provider and pa_coordinator can create PAs.

The credential rule#

Adverse determinations require role + credential + reason. A denial only succeeds when all three hold: the actor's role grants pa.decide (medical_director only), the actor's stored credential is a physician credential (MD:…, the demo Medical Director is MD:Ophthalmology), and the request carries a specific denial reason code and text. Missing role or credential → 403; missing reason → 422. An AI advisory row can never produce a denial. The advisory endpoint is physically unable to change state. This guard is designed to align with California SB 1120 ("Physicians Make Decisions Act") and the physician-review norm reflected in the NAIC AI model bulletin now adopted in roughly 25 states.

Tenant isolation is equally absolute: every query filters by the tenant in your token. An object in another tenant returns 404, the platform never reveals whether it exists.

VBC guide, care manager & VBC manager walkthroughs#

Sign in as jane@demo.aco (Care Manager) or alex@demo.aco (VBC Manager) to follow along.

The Dashboard

  1. Open Dashboard in the left navigation.
    What you should seeKPI tiles — Patients, High Risk, Open Care Gaps, Active Interventions, Open Tasks. Computed live from the same rows their drill-downs show. Change anything anywhere in the app and the numbers move with it; they are never hardcoded.

My Panel

The care manager's landing view, and the answer to six questions in one screen: which members need me today, why them, what to do next, can the system do it, did it happen, and did it close the gap.

It ranks members, not tasks, and that distinction is the reason it exists. A queue can only rank work somebody already created. A member who is high risk with four open gaps and no task raised is invisible in a queue and top of this panel — the count of members here with no task is printed in the header, because that number is the queue's blind spot measured.

  1. Open My Panel.
    What you should seeMembers ordered by a priority score, each row carrying the score, the reasons that produced it, the recommended next action with its clinical reasoning, what is already in flight, and what has closed. Members with no task carry a NO TASK badge.
  2. Expand all factors on any row.
    What you should seeEvery component of the score with its own points and its own sentence — risk tier, gaps with nothing running, a discharge window closing, an authorization on the clock, adherence below threshold, outreach that stalled. The components sum to the score, and a test enforces that.
  3. Press Start on a row.
    What you should seeThe same thing that happens from Patient 360: the authorization requirement check runs first, then the intervention is created, and you are told whether a PA is required and under which policy version. The panel is a shortcut, not a side door.
Nothing here is stored. The score is computed at request time, so the order moves as the clocks do — a discharge window closing overnight reorders the panel by itself. There is no priority column in the database and there should not be one; a persisted priority is stale the moment a clock moves.

Did what I started land? Underneath the panel: interventions you own, how many reached the member, how many completed, and how many gaps closed. Reached means the member responded — a sent message is not a reached member. Interventions that went nowhere are counted as stalled rather than quietly dropped, because an outreach channel that is not working is the finding.

Read it as what happened to the work, not what the work caused. A gap closes on clinical evidence, which can arrive for reasons that have nothing to do with your outreach. The platform will not claim your call closed the gap.

The Dashboard

The dashboard answers "what requires my attention today?" It used to answer "what exists in my population" — six counts and a note — and the counts are still there, moved below the operational row into a Population panel. The role does want the denominator; it is just not the first question.

The first screenful is things with a clock on them: tasks due today, overdue tasks, high and rising-risk members, discharges inside their follow-up window, PA and eligibility issues blocking care, outreach that came back with nothing, active interventions, and gaps closed this month. Anything that needs acting on carries a needs attention chip.

Today's priorities fills the main area: member, the reason, the priority score, the recommended action, and the due date — with the action available on the row. It is the same ranking as My Panel, deliberately: one computation, two places to meet it. Two screens computing "who needs attention" by different rules is how a platform starts disagreeing with itself.

A due date says what it is a deadline FOR. Three clocks can run on one member — a task's own due date, the post-discharge follow-up window, and an authorization's decision SLA. Showing the soonest without saying which it is would leave you guessing whether missing it costs a phone call or a readmission.

Three strips underneath give the shape of the work: care gap progress (open → in progress → closed), the engagement funnel (outreach → reached → scheduled → completed), and risk distribution. In the funnel, reached means the member responded — a sent message is not a reached member.

Blocked care has two causes, and both are counted. An authorization still on its clock and coverage that cannot pay stop the same service — the second is the quieter of the two, because nothing is pending to look at. The eligibility half breaks down into no coverage on file, terminated, and lapsed, since each needs a different fix: a record problem, a member who left the plan, and dates that ran out.
"Gaps closed this month" needed a schema change to be honest. A care gap recorded what closed it and never when, so the count had no source on the row, and the audit trail covers only one of the two ways a gap can close. Gaps now carry a closure date. Any closed before that existed are reported separately as undated rather than counted as "not this month" — which would read as a measured zero.

The Patients list

  1. Open Patients.
    What you should seeThe tenant-scoped roster, in the demo, John Doe and Sarah Martinez, both HIGH risk, with member ID, PCP and program. A note reminds you that every view is audited with your identity and a correlation ID. That is literally true; check the audit trail as the Auditor later.

Patient 360 & risk evidence

  1. Click a patient row to open their 360.
    What you should seeThree panels: the header with DOB, member ID and risk badge; a "Risk, why?" box listing each factor with source and date (for John Doe: HbA1c 9.4% · lab, ED visit · adt, Missed follow-up · scheduling) plus the model version apex-risk v3.2; then Care Gaps and Interventions.
  2. Read a gap's row: measure key and version, status, priority, and the why-open narrative (Sarah: "No retinal exam in 24 months; HbA1c 10.1%."). Every gap justifies itself.

Starting an intervention (with automatic PA check)

  1. On an OPEN gap, click Start Intervention.
  2. Before creating anything, the platform silently runs a PA requirement check for the gap's service code against the member's active coverage (a CRD-style discovery call, see the PA guide).
    What you should seeFor Sarah's retinal exam (CPT 92250) the toast reads "Intervention created — PA REQUIRED under UHC-MA-EYE-014 v3". For a service with no matching policy the toast reads "no PA required (policy trace logged)". The check itself is audited either way.
  3. The intervention appears in the Interventions panel in state IDENTIFIED, its first timeline event reading "created, context inherited from care gap". The gap moves to IN_PROGRESS.
  4. Try clicking Start Intervention again for the same gap: the server answers 409 Active intervention exists. One gap, one active intervention, duplicates are blocked at the API.

Outreach → engaged → schedule

  1. On the IDENTIFIED intervention, click Begin outreach → state becomes OUTREACH.
  2. Click Patient respondedENGAGED. Every click appends a timestamped event to the timeline.
  3. Click ScheduleSCHEDULEDunless a linked PA is still pending. If you launched a PA that has not been decided, the server refuses with 409 Scheduling gated: PA-2026-xxxxx is IN_REVIEW.
    What you should seeWhile the PA is pending the intervention sits in AWAITING_AUTH with a stopwatch chip ("awaiting payer decision") and no schedule button at all. The moment the PA is approved, the intervention flips to AUTH_APPROVED automatically and the button returns as Schedule (auth on file).

Recording completion evidence & auto-closure

  1. On a SCHEDULED intervention, click Record completion evidence. This simulates the EHR/claims completion feed a production deployment receives automatically.
  2. Choose the evidence type — lab, encounter or procedure, and enter the CPT observed (e.g. 83036 for the HbA1c gap, 92250 for the retinal exam), then Send event.
  3. The closure rule is deterministic: the evidence type must be clinical and the CPT (or measure key) must match the gap's measure.
    What you should seeOn a match: "Care gap closed automatically; evidence recorded", the intervention passes through COMPLETED to GAP_CLOSED and the gap becomes CLOSED. On a mismatch: "Evidence insufficient, gap stays IN_PROGRESS (logged)" and an audit event records exactly what was offered and rejected.
  4. Try sending mismatched evidence on purpose (wrong CPT). The gap does not close, and notice that an approved PA never closed it either. Approval changes permission, evidence changes state.

My Work

  1. Open My Work.
    What you should seeA prioritized queue where every task answers two questions in its own columns: why am I seeing this (e.g. "Post-ED follow-up + HbA1c gap (ED 08/02, HbA1c 9.4)") and what should I do ("Start Intervention"), with priority and due date. An Open 360 button jumps straight to the patient.
  2. Switch between Mine and Team using the toggle, which carries both counts.
    What you should seeMine is work assigned to you and is where you act — Done, Dismiss, or Release it back to the team. Team is everyone's open work plus anything owned by nobody, marked UNASSIGNED, and its only action is Claim.

The queue's columns are Priority, Member, Reason, Recommended action, Due, Owner and the actions. Two of those are worth explaining.

Priority is derived, not typed. The order comes from clinical risk, care gap priority, time sensitivity, the post-discharge window, any authorization on its SLA clock, and engagement status. The Critical/High/Medium/Low somebody typed is one input among those — it still counts, because discarding a human's Critical would be its own failure, but it no longer decides the order. A Medium task on a member overdue four days can and does outrank a High due in three weeks.

Reason is the clinical sentence, not the label. Instead of "marked Critical" the row reads "overdue by 4 days + rising risk + marked medium" — the highest-scoring factors joined, with the task's own reason underneath. Hover the score to see every factor with its points.

Every task says where it came from. Created by hand, raised by a campaign, or raised by the transitions sweep — marked AUTO when it was not a person. A care manager treats "I raised this" and "a 3am sweep raised this" differently, and the second is worth a glance at the record before acting.

The Owner column reassigns: pick a colleague to hand the task over, or clear it to put the task back in the team queue. Either way it lands in the audit trail. Overdue tasks carry a red badge.

The two views have deliberately different jobs. Team is for seeing what exists and picking it up; Mine is for working it. You cannot close a task you have not claimed — a queue where anyone can tick off anyone's work stops meaning anything, and the delivery metrics that hang off ownership stop meaning anything with it.

Unassigned work is visible on purpose. A task owned by nobody sits in no one's queue and in no one's numbers, which is exactly how it goes unworked. It appears in the Team view with a Claim button, and it is counted separately on the VBC Manager's care-team delivery view.

Before 2026-08-18 this screen returned every open task in the tenant regardless of owner, so one care manager could not tell their queue from anyone else's.

Prior Authorization guide#

Submission side: sign in as jane@demo.aco. Review side: nurse@demo.payer, then md@demo.payer.

The requirement check

Before any request is drafted, POST /pa/requirement-check answers the question providers ask first: does this service need prior authorization under this member's plan? The resolver matches the member's active coverage (payer + line of business) and the CPT code against the versioned policy library and returns a three-valued answer: yes, no, or unknown when no policy matches — unknown is never collapsed into "no". The response names the governing package and version (UHC-MA-EYE-014 v3 for Sarah's retinal exam) and lists the documentation types its criteria demand, and the whole lookup is written to the audit trail. In the UI this runs automatically when you start an intervention; the pattern mirrors Da Vinci CRD (Coverage Requirements Discovery).

Launching a PA from an intervention

  1. On an intervention in IDENTIFIED, click Launch PA.
    What you should seeA form with the member line read-only and the service CPT pre-filled from the care gap — context is inherited, not re-typed. You supply the ICD-10 diagnosis and choose urgency: Standard — 7-day SLA or Expedited — 72h SLA.
  2. Click Submit PA. The request is created with an idempotency key, so a double-click or a network retry returns the same PA instead of creating a duplicate.
    What you should seeA toast with the new reference and its deadline: "PA-2026-xxxxx submitted — SLA due …". The linked intervention parks in AWAITING_AUTH and the gap shows AWAITING_AUTH too.
  3. The policy package and version in force are snapshotted onto the request, and the submission passes intake validation into IN_REVIEW with the decision clock running.

The review queue & SLA clocks

Sign in as the UM Nurse. The Review Queue lists every PA in the tenant, newest first: reference, patient, CPT/ICD-10, status, the SLA due timestamp for anything IN_REVIEW, the policy snapshot and the auth number once issued. Decision deadlines are stamped at submission — 72 hours for expedited requests, 7 calendar days for standard, matching the CMS-0057-F timeframes in force since January 1, 2026. The clock is data on the request itself, so it survives into reporting and the audit trail.

AI advisory, what it does and cannot do

  1. Click Review on an IN_REVIEW PA, then Run criteria analysis in the AI advisory box.
    What you should seeAn advisory panel labeled with the model and version (apex-um v1.8) and the tag "cannot decide", e.g. "Criteria package evaluated: 3/3 MET on available evidence. Approval probability high. Advisory only." The toast confirms: "AI advisory recorded, no state change".
  2. What happened server-side: an advisory row was appended to the decision trace carrying the model version, criteria version and per-criterion outcomes. What cannot happen: the advisory endpoint has no code path that touches PA state. The AI can read, evaluate and recommend. Approving, pending and denying are exclusively human actions, and denial is exclusively a credentialed physician's action.

Approve, pend, and the deny guard

  1. In the review dialog, record the criterion outcomes. Outcomes are 5-stateMET, NOT_MET, UNKNOWN, NOT_APPLICABLE, NEEDS_HUMAN_REVIEW, never collapsed to yes/no.
  2. As the UM Nurse: Pend (needs more information; the PA can re-enter review later) and Approve both succeed, approval issues an auth number valid for 180 days.
  3. Still as the nurse, press Deny.
    What you should seeThe dialog itself warns you: "You are not a physician: the server will refuse a denial regardless of this UI." And it does — 403 Adverse determinations require a medical director. The button exists so you can confirm the server enforces the rule.
  4. Sign in as the Medical Director and deny properly: enter a specific denial reason code and text (e.g. MED-NEC-001 + the clinical rationale). Leave the reason empty and the server answers 422 Denial requires a specific reason code and text. Generic denials are rejected by design, matching the CMS-0057-F specific-denial-reason requirement.
    What you should seeOn a valid denial: PA → DENIED with the reason stored on the request, your MD:Ophthalmology credential recorded on the decision row, and the linked intervention logging a pa.denied event flagging that an appeal or alternative plan is required.

Reading the decision trace

Open any PA's Detail view. Below the service, policy snapshot and auth/denial fields sits the decision trace: one row per review action, in order. Each row shows its kind — ai_advisory (with model version) or human (with the reviewer's credential), the disposition, and the criteria version it was judged against. Together with the hash-chained audit events behind it, the trace makes every decision reproducible: inputs, rule version, evidence outcomes, actor and credential, timestamps. This is the artifact an auditor, an accreditor or opposing counsel would read. And the platform is built so it reads well.

Assistance & readiness#

Two features carry the word "AI" on the screen, and it is worth being precise about what each one is, because the distinction is the reason they are safe to use on a live request.

Neither of them decides anything. The documentation readiness check and Ask mdvin both read your own records, match them against the plan's own criteria, and report what they found. Every statement is computed, and every statement names the record it came from. A language model may be switched on to rephrase a finished sentence, and even then it is handed the conclusion, not the chart.

Documentation readiness#

Roughly a third of pended authorizations are not clinical disagreements. The documentation exists, and nobody attached it. The request goes out incomplete, the payer pends it, and a coordinator spends days chasing a file that was in the chart the whole time.

The readiness check runs before you submit. It resolves the active policy for the service you are requesting and looks for evidence of each criterion in the member's own record. Every criterion lands in one of three states:

StateWhat it meansWhat to do
Evidenced Found in the record, and the panel cites the item, its source and its date, for example HbA1c 10.1% · Lab result · 2026-07-01 Nothing
In the chart, not attached The document exists on the member but is not on this request. This is the case the feature exists to catch Attach it, see below. One click
Not evidenced Nothing in the record satisfies this criterion Obtain the documentation. It is not sitting in the chart
See it in the demo. Open Sarah Martinez, start the Diabetic Retinal Exam intervention and launch the PA. The panel shows three criteria evidenced and one, the ophthalmology referral, sitting unattached in her chart since 2026-07-20.

Attaching what it finds#

Finding the gap is only half the value. What you do next depends on where you are:

  • Before submission there is no request to attach to yet, so the panel offers "Attach this document to the request when I submit", ticked by default. The document is bound as soon as the request is created.
  • On an existing request, in the review screen or through Ask mdvin, each unattached document carries an Attach button that binds it immediately and re-runs the check.

Attaching is recorded as pa.attach_document in the audit trail with the file name and its checksum, because adding evidence to a request is an evidentiary act and the decision trace has to be able to explain it.

The payer sees the same panel on the same request. A UM nurse looking at an incomplete submission sees the documentation sitting in the chart rather than pending it back, which is the point: the pend is avoided instead of issued.

One document, several requests#

A document is not consumed by the request it is attached to. One ophthalmology referral or one HbA1c result routinely supports several requests over a year, and the Documents panel lists every request a file currently evidences. Attaching the same document twice to the same request is refused; attaching it to a second request is normal and expected.

Ask mdvin#

A question box that answers from this tenant's records and shows where each answer came from. It runs as you: it holds no wider view than your role does, and it refuses exactly where the API refuses, naming the permission you are missing rather than pretending the data does not exist.

Every figure it quotes is read from the same service the corresponding screen reads, so the assistant and the interface cannot disagree about a number.

Ask it aboutExampleNeeds
A request's statusWhat is the status of PA-2026-0001?pa.read
What is blocking a requestWhat is blocking my authorizations?pa.read
DeadlinesWhich requests are close to their SLA?pa.read
Why authorization is neededWhy does Sarah Martinez need a prior auth for 92250?pa.read
Open care gapsWhich care gaps are open for John Doe?patient.read
Your queueWhat should I work on next?patient.read
Risk scoringWhat is driving John Doe's risk score?patient.read
RecommendationsWhat do you recommend I do next?patient.read
DischargesWho was discharged recently?patient.read
PopulationHow many members are attributed?patient.read
Quality ratesWhat are our measure rates?analytics.read
CostWhat is our PMPM?analytics.read
Contract positionHow are we tracking against the benchmark?analytics.read
OutcomesWhich outcomes are we actually measuring?analytics.read

Answers arrive with the supporting facts and a deep link to the screen behind them, so nothing has to be taken on trust. Where a claim cannot be substantiated — two of the six outcome claims, for instance — it says so rather than producing a number.

What it will not do#

ControlBehaviour
It cannot widen your permissions The assistant runs as you. Every query filters to your tenant and every question is gated on your own permissions. A UM nurse asking about care gaps is refused, and told which permission is missing, exactly as the API would refuse it
It cannot decide coverage Criteria that are not evidenced are reported UNKNOWN, never not met. Absence of evidence on a request is not evidence that a criterion fails. Adverse determinations remain a licensed physician's act
It cannot invent a clinical fact The matching is deterministic. It reports what it found and where. There is no step in which anything is generated from nothing
Language-model phrasing is optional and off When enabled, a model may only rephrase a finished sentence. It never sees the record, it is refused entirely unless the deployment is marked synthetic-only, and every number in its output must already appear in its input or the rephrasing is discarded and the computed sentence kept. Answers that were rephrased are labelled phrased
Every question is recorded assistant.ask in the audit trail, with the intent and the records read

The golden path, end to end#

This is the sequence that shows both modules cooperating on one journey. Run it with Sarah Martinez's Diabetic Retinal Exam gap. The seeded scenario where the service (CPT 92250) requires prior authorization under UHC-MA-EYE-014 v3.

#ActorActionSystem response
1Care Manager (jane)Patients → open Sarah Martinez's 360 HIGH risk with evidence (HbA1c 10.1% · lab); gap Diabetic Retinal Exam is OPEN
2Care ManagerStart Intervention on the gap Requirement check runs first: PA REQUIRED under UHC-MA-EYE-014 v3. Intervention created IDENTIFIED; gap → IN_PROGRESS; duplicate attempts → 409
3Care ManagerLaunch PA from the intervention; submit (standard urgency) Context inherited (member, CPT 92250); PA created with idempotency key → IN_REVIEW; SLA due stamped (+7 days); intervention parks AWAITING_AUTH; gap AWAITING_AUTH
4Care ManagerAttempt to schedule anyway 409 Scheduling gated: PA-2026-xxxxx is IN_REVIEW, the appointment cannot outrun the authorization
5UM Nurse (nurse)Open Review Queue → Review the PA SLA clock, policy snapshot v3 and criteria context displayed
6UM NurseRun AI criteria analysis Advisory row recorded (apex-um v1.8) — no state change
7UM NursePress Deny 403 Adverse determinations require a medical director, refusal is itself audited
8Medical Director (md)Review → outcomes ALL MET → Approve PA → APPROVED, auth number issued (180-day validity); intervention auto-flips AWAITING_AUTH → AUTH_APPROVED; gap → IN_PROGRESS
9Care ManagerSchedule (auth on file) Gate passes, intervention → SCHEDULED. Note: the gap is still open. Approval ≠ care delivered
10Care ManagerRecord completion evidence: procedure / CPT 92250 Deterministic match → intervention COMPLETEDGAP_CLOSED; gap → CLOSED; toast "Care gap closed automatically; evidence recorded"
11Auditor (audit)Open the PA detail → decision trace Full trace: ai_advisory (model + version) then human approve with MD:Ophthalmology and criteria v3 — every step above also sits in the hash-chained audit log under one correlation ID per action
The alternative ending. At step 8 the Medical Director can instead deny with a specific reason code + text. The PA → DENIED, the intervention logs a pa.denied event calling for an appeal or alternative plan, and the gap stays open, because the patient still hasn't received the care.

The audit trail screen#

Sign in as the Auditor. Audit Trail is the first item in the navigation. Every material action in the platform writes a row here, and the rows are append-only: there is no edit and no delete path anywhere in the application.

Reading the trail, and verifying it#

Filter by action, by object (a PA reference works), or by date range. The action list offers only the actions actually present, so you are choosing from what happened rather than guessing. Each row shows the sequence number, who acted and in what role, the object, the member, the recorded detail, and the row's own hash.

Verify chain recomputes every hash from the beginning. Each row carries the hash of the row before it, so a removed or altered event breaks the sequence and the check reports the exact point where it broke. A clean result reads Chain intact, N events verified. It is a separate button rather than something that runs on every page, because verification rehashes the whole table.

Export writes the current view to CSV for an evidence pack.

Reads are recorded too#

Opening this screen writes an audit.read event recording the filter used and how many rows were returned. Someone trawling a member's history leaves a mark. A log that protects everyone except the people reading it is not an audit log.

Try this. Filter on a PA reference to see that request's whole life in order: submission, the AI advisory with its model version, any document attachments, and the physician's decision with their credential.

Population & intelligence#

Care gaps and risk tiers are computed, not authored. Earlier builds stored a sentence about a clinical fact — "HbA1c 9.4%" — which a reviewer cannot check. The platform now holds the coded, valued, dated fact underneath, and the engines read from it. If a number on screen looks wrong, the fact that produced it is one click away.

The clinical record#

Patient 360 carries labs and vitals (with units, reference ranges and an abnormal flag), the problem list, social drivers of health, medications with adherence, procedures and encounters. Every row names its source and date. Social drivers are stored as observations with Z-codes rather than in a separate store, because they are observations about a member like any other.

Risk scoring#

The risk panel names the engine that produced the tier — currently apex-risk-rules v2 — and lists every contributing factor with its point weight and provenance. It is a deterministic rule engine, not a machine-learning model, and that is a deliberate choice: a tier influences what outreach a member receives, and CA SB 1120, the NAIC AI bulletin and the nH Predict litigation all point the same way — a score that affects care must be explainable and defensible, not merely accurate.

Scores are written to a history table, so the question "which model, which version, on what inputs, when" has an answer for any past decision. The version is bumped whenever a weight changes, so an old score still means what it meant when it was produced.

If no engine has scored a member, the panel says so in those words. It will never display a model version for a model that did not run.

Quality measures#

A measure is versioned, effective-dated data with a denominator, a numerator and exclusions — never logic hidden in code. Evaluation produces one of three outcomes per member: in the denominator and compliant, in the denominator and not (which opens a gap), or excluded. Excluded is not compliant. Confusing those two is the ordinary way a quality rate gets inflated.

Evaluation is idempotent, so it can run after every clinical event without duplicating gaps: a member who becomes compliant has their gap closed with the evidence that closed it.

Measure packages here are synthetic. Real HEDIS and Stars specifications are licensed content, as are CPT value sets. The platform models the shape of a measure — denominator, numerator, exclusions, version — behind an adapter, which is both the BRD's rule and the licensing requirement.

Next best actions#

Ranked recommendations across the population: which member, which action, why them, why now. Ranking combines gap priority, patient risk and whether the action can actually be taken today.

Recommendations are advisory only. Nothing is created, transitioned or sent until a person acts. The engine also will not recommend what the workflow itself would refuse — it never suggests scheduling a service whose authorization is still pending, because the server would reject it.

Attribution, cohorts and enrollment#

Attribution answers "who was ours, for this period" — the denominator under every quality and financial number. Each attributed member records the method used, because when a payer disputes a rate, the method is the argument. Attribution is effective from the start of the performance period, not from the day the roster was loaded.

Cohorts are saved, named population definitions built from a fixed vocabulary — risk tier, condition, programme, open gaps, measure, PCP — and are deliberately not free-form queries.

Programme enrollment is a lifecycle with dates, consent and reasons: eligible, offered, enrolled, declined, disenrolled. A member can be in several programmes at once.

Transitions of care#

The one workflow driven by an external event rather than a standing measure: a member is discharged and a clock starts — contact within 48 hours, a follow-up visit within 7 days.

Both clocks run from the discharge date, never from when the record reached us. A discharge learned about late has less window remaining, not a fresh one. Anchoring to arrival is how organisations end up reporting a follow-up they did not achieve.

The sweep that raises follow-up tasks is idempotent, so it can run on every inbound message without stacking duplicates onto a care manager's queue. Readmissions are reported as observed history, not as a prediction.

VBC Manager#

The VBC Manager works one level above the care manager. A care manager asks which member needs me, and what should I do. A VBC Manager asks how is the contract performing, what is driving that, and what should my team focus on. These screens are built around that loop:

contract performance → variance → drivers → population → action → measured result

Every number on these screens opens. If a figure cannot be drilled to the members inside it, it is a dashboard tile — and a wall of tiles is what this persona was deliberately designed not to be. Each metric carries either a drill-down or an action.

Reading these screens requires analytics.read, which the VBC Manager and executive hold and a care manager does not. Generating work from them requires vbc.admin, which only the VBC Manager holds. That boundary is enforced on the server, not by hiding a menu item.

Contract health#

One executive view: actual PMPM against benchmark, gross savings, quality score against the gate, estimated settlement, open care gaps and the high/rising-risk count. Each row carries a status and the comparison that produced it, so a status you disagree with can be checked rather than argued with.

The headline sentence is composed from the values, never asserted. When cost is favourable and the quality gate is missed, it says quality is the binding constraint — and it stops saying so the moment the numbers stop saying it.

Every figure here is the same computation the Financial and Quality screens run, so the summary cannot drift from the screens beneath it.

The quality gate and the path to it#

Savings are earned only if the quality score clears the contract's minimum. The gate panel shows the current score, the required score, the shortfall in percentage points, and the specific gaps that close the distance — measure by measure, with the resulting rate.

The arithmetic is stated rather than hidden: closing one gap in a measure raises the composite by 100 ÷ that measure's denominator ÷ the number of rated measures. The plan takes the highest-yield gaps first.

Read the caveat that travels with the plan. The composite is an unweighted mean, so measures with small denominators move it fastest per gap closed. That is a property of the contract's scoring method, and it is surfaced deliberately: a plan that chases points rather than the members who most need care would be following the maths, not the medicine.

Quality opportunity#

Every measure, ranked by how much of the quality score is sitting inside it. Opportunity is defined, not intuited: it is the number of composite points the score would gain if every open gap in that measure closed. A measure twenty points below target with two eligible members matters less than one five points below target with thirty.

Targets come from the versioned measure package, not from the report — a target is a contract or programme term. A measure with no agreed target shows its rate and its gaps and says nothing about a shortfall, because comparing against a target nobody set would manufacture one.

Each row drills to the members behind it: who they are, their risk tier, the barrier, whether an intervention is already running, and the recommended next action — the same recommendation the member's own care manager sees.

Modelling a scenario#

Enter how many additional gaps you expect to close in each measure and the platform recomputes the composite, whether it clears the gate, and what that does to the estimated settlement. Closures are capped at the gaps actually open, the assumptions are listed with the answer, and nothing is stored or changed.

A projection, not a forecast. It says what the arithmetic gives if those gaps close and nothing else moves. It does not predict that they will close.

Cost drivers & utilization#

PMPM broken down by cost category — inpatient, emergency, outpatient, professional, pharmacy — each against the benchmark the contract sets for it, with the month-over-month move and a drill to the members driving it. Categories divide by the same member-months as the total, which is what makes them sum to it.

Where a contract carries no category benchmark, the view reports the largest driver and says so, rather than presenting a share of spend as an opportunity. Allocating the total benchmark across categories using the period's own mix would make every category land exactly on target — arithmetic dressed as insight.

Utilization covers admissions and ED visits per 1,000 per year, the 30-day readmission rate, average length of stay and post-discharge follow-up. Two rows deliberately report no value at all:

  • Avoidable ED utilization — avoidability is a classification judgement over the visit diagnosis, and no such value set is loaded. A number here would be a clinical claim with nothing behind it.
  • Specialist visits — encounters carry no provider specialty and claims carry a provider name rather than a taxonomy code, so a visit cannot be classified as primary or specialty care.

Each says why. A zero that means "we cannot measure this" is the most confident lie a dashboard can tell.

Today's priorities#

A management queue, not an alert feed. Rows are ranked by contract exposure — the gate that blocks settlement first, then cost variance and clinical windows already running, then measure shortfalls. Every row states why it is there, the recommended action, the owner, the due date, its status, and what escalates if it is left.

Highest-opportunity members#

Contract averages hide where the opportunity actually is. This ranks members by spend weighted by what can be done about it: open gaps with no intervention running, scaled by risk tier.

Cost alone does not rank a member highly. An expensive member whose every gap is already being worked has nothing for a care manager to do, and ranks below an actionable rising-risk member. Each row states why it sits where it does.

Campaigns: turning insight into work#

Launching a campaign from a measure creates prioritised tasks for the care team, and nothing else. It does not send a message, start an intervention, transition anything, or close a gap. Each task states why the member is on the list — the gap, the barrier and the risk tier — and lands in the normal work queue for a person to pick up.

That restraint is the rule the AI advisory rows follow, and population scale raises the stakes on it rather than relaxing them: a mechanism that could enrol two hundred members into outreach without a human deciding is exactly the automation the state machine exists to refuse.

One live campaign per target. Re-running a push tops up the existing campaign — newly eligible members get tasks, members who already have open work do not get a second one, and no rival campaign row appears. Two clicks of Launch cannot double a care manager's queue.

A campaign can target a quality measure (from the Quality screen) or a saved cohort (from Population). A measure campaign targets only the members with a live gap on it, not the whole denominator — generating work for compliant members is how a care team learns to ignore a queue. A cohort campaign targets everyone in the saved definition as it stands at launch. Either way you can name the care manager who receives the work, or leave it in the team queue.

The funnel, and what it will not claim#

Eight stages, from the eligible population through work generated, intervention created, outreach attempted, member reached, appointment scheduled and service completed, to gap closed. Each stage shows its share of the stage above it, because the drop is the finding — identification is rarely the problem; appointment conversion often is. The largest single drop is named.

Observed progression, not attributed effect. Members close gaps for reasons unconnected to any campaign — they see their doctor, another programme reaches them, a lab result finally arrives. With no comparison group the funnel cannot say the campaign caused the closures, and it does not.

Care-team delivery#

Operational management metrics per person: work assigned, open and overdue; median hours to first touch and to close; outreach response; appointments kept. Outcome columns come first and activity columns are grouped under a header that says what they are, because the brief was to measure progress toward outcomes rather than calls made.

There is deliberately no productivity score per person. A single ranked number over a handful of staff is a statistic about nothing, and in a real deployment it is the number that gets misused. Choose the metric you care about and own that choice.

Three things about how these figures are built:

  • Durations come from the audit trail, not from a mutable column. The chain is append-only, so a duration here is the same one a review would reconstruct. Medians, not means — one item left over a weekend drags a mean into uselessness.
  • Rates with no denominator are blank, not zero. "No contacts made" and "0% replied" mean different things, and only one of them is true when nobody has been contacted.
  • Gap closure is credited to whoever owned the intervention for that member. The measure engine closes a gap on clinical evidence, which may arrive for reasons that have nothing to do with the outreach. Read the column as delivery flow, not as individual performance.

Unassigned work is reported separately. A task owned by nobody sits in no one's queue and in no one's numbers, which is exactly how it goes unworked. When launching a campaign you can name the care manager who receives the work, or leave it in the team queue — and the delivery view will tell you which you chose.

Referral completion is listed as not available, with the reason: there is no referral in the domain model. Measuring it needs a referral entity with an ordered date, a recipient and a completion event. Proxying it from document types would report paperwork as care.

Every measure rate, the quality composite, open gaps, risk mix, attribution and PMPM are captured as dated snapshots, so the platform can answer the second half of the question: not just where are we, but are we improving or deteriorating. Improving is judged against each metric's own direction — open gaps falling is good, quality rising is good, and a screen that colours both the same way teaches people to stop reading it.

A point is frozen at capture time, never recomputed for history. So a rate cannot silently change after the fact when a measure package is updated, and last month stays what last month actually said.

The demo's history is seeded, and every seeded point says so. This tenant resets nightly, which would erase the history each morning, so a synthetic backfill sits behind the real data. Each point is labelled SEEDED or MEASURED: the most recent point in every series is the real one, and it equals what the Quality, Financial and Contract screens show. A trend whose newest point disagreed with the live number would be quietly lying.

Projection is offered only where the series earns it. Open any metric and the platform either projects to the end of the performance period or explains why it will not:

  • Fewer than 3 points, or a span under 14 days — no projection. Two points define a line through anything.
  • A poor straight-line fit (R² below 0.25) — no projection, with the R² stated. If the series is not behaving like a line, extrapolating one is not cautious, it is wrong.
  • No confidence interval, ever. On a series this short an interval would be false precision, and false precision is the part people remember.
  • Bounded metrics are clamped to their own units and say when they were.

What you get is the rate of change per week measured from the record, where that rate lands at period end if nothing else moves, and how well the line actually fits. Everything else is your judgement, which is where it belongs. A projection is arithmetic; a forecast is a claim, and this produces the first.

In a real deployment the capture runs daily on a schedule. It is exposed as an authenticated endpoint rather than a script so that writing a data point lands in the audit trail like every other write.

Asking mdvin the analysis#

The assistant answers the VBC Manager's analytical questions, not just record lookups:

  • Why is our quality score below target?
  • Which measures have the largest opportunity?
  • What is driving PMPM this month?
  • Which members are driving inpatient cost?
  • Which provider has the most open care gaps?
  • Which interventions have the lowest completion rate?
  • What should my team prioritise this week?
  • What happens to projected performance if retinal-exam gaps improve?

Each answer is assembled from the same services the screens read, so the assistant cannot describe a contract position that is not the one on screen — when you check one against the other they agree, because they are the same computation. Naming a cost category drills to the members inside it; naming a measure models closing its gaps rather than extrapolating a trend.

It runs as you. These answers require analytics.read, so a care manager asking what is driving PMPM gets the same refusal the API would give, naming the permission. And no answer offers to do the thing it suggests — the most any action does is open the screen where a person decides. A chat surface that launches campaigns is a chat surface that launches campaigns by accident.

Financial performance#

Cost and utilization for the attributed population. PMPM divides allowed dollars by member months, not by member count — a member attributed for four months of a twelve-month period contributes four. Dividing by a head count is the commonest way a PMPM comes out wrong, and it flatters every number downstream.

Utilization is expressed per 1,000 members per year because that is the unit payer benchmarks are published in; raw counts against a small population would be honest and useless.

Contract performance#

A value-based agreement carries its own terms: benchmark PMPM, risk model, shared-savings rate, quality withhold and the minimum quality score that gates earning anything. Settlement is computed as savings against benchmark, gated on quality, then reduced by the withhold.

Every settlement figure is an estimate and is labelled as one. Real settlement is a reconciliation the payer performs months later against run-out claims. A platform that prints it as fact is making a promise it cannot keep.

If savings are generated but the quality gate is not met, the screen says so and shows zero earned rather than quietly paying out.

Outcomes, measured and not#

Four outcome claims are computed from live data: composite quality rate, care-gap closure, avoidable utilization and total cost of care. Each states the method used.

Two are returned as NOT MEASURED, deliberately. Member experience needs survey instrumentation this platform does not collect. Success in value-based contracts cannot be claimed before a payer reconciles settlement. A plausible number would be worse than a blank.

Measured outcomes can be snapshotted and trended, which is what turns "performance improves over time" from an assertion into something checkable.

Master patient index#

When a second source arrives, the same person appears twice under different identifiers and every downstream number double-counts. The index scores candidate matches on identifier, date of birth and name, weighted so that no single field can carry a match alone.

The engine proposes; a person disposes. Matches stay candidates until someone with administrative permission confirms them. A false-positive merge joins two people's charts — a patient-safety event, not a data-quality one — so merges consolidate identity only and remain reversible.

Outreach and templates#

Outreach uses versioned, approved, multilingual templates rather than text typed at send time. An approved template is also what makes an automated send defensible: a person approved the words, the system chose the moment.

Consent is enforced at the moment of sending, against the member's channel opt-in and quiet hours — not at the workflow transition. Inbound replies are handled too: an opt-out keyword is honoured before anything else and then enforced on every subsequent send, and a confirmation advances the intervention from a real member response rather than a staff click.

This deployment cannot deliver a message externally. Sending requires both a configured provider and synthetic-only mode switched off. Being unable to message a real person by accident is the intended property of a demo environment.

Patient outcomes#

Quality rates and cost describe a population. These describe a member. Patient 360 shows, for each tracked value, the earliest and latest reading, the change between them, the direction of travel, and whether the member is at target. The Financial screen aggregates the same thing across the population and adds the 30-day readmission rate.

Observed change, not attributed effect. That an HbA1c fell is a measurement. That an intervention lowered it is a causal claim, and with no comparison group there is nothing behind it. Every response on this screen carries that distinction in writing, and it is deliberate: a number that looks more certain than it is causes more damage than a missing one.

A member with one reading has no trajectory. They are reported as not evaluable, never as unchanged — inventing a baseline from a population average would manufacture an improvement that did not happen.

Readmission is a rate, not a flag. The denominator counts only discharges whose 30-day window has fully elapsed. Including a discharge from yesterday would depress the rate by counting a member who has not yet had the opportunity to be readmitted.

Integrations & scheduling#

Data arrives as files and messages: member rosters, laboratory results, admission and discharge notifications, clinical documents. This is where it lands, and where you check what happened to it.

Reading a batch#

Inbound data is staged before it is promoted. Every row keeps its raw payload and its own outcome, and only then becomes a clinical fact. Three things follow from that, and they are the reason the design is worth the extra step:

  • Provenance survives. "Where did this value come from" resolves to a line in a named file, not to a source label.
  • Partial failure is legible. A malformed file becomes some promoted rows and some rejected ones with reasons attached, instead of a half-write nobody can reconstruct.
  • Reprocessing is safe. Batches are keyed by file digest, so the same file submitted twice is recognised rather than loaded twice. Feeds redeliver constantly.

Each row ends as PROMOTED, REJECTED (with the reason) or UNMATCHED.

An unmatched row is never attached to a best guess. If a result cannot be tied to a member with confidence it waits for a person. Filing a lab result in the wrong chart is a patient-safety event, not a data-quality one.

HL7 v2 and C-CDA#

FHIR is the roadmap and the certification pressure, but in practice ADT, lab results and scheduling still travel as HL7 v2, and documents as C-CDA. The parser is tolerant on structure and strict on meaning: it copes with the variant separators and missing optional fields real messages carry, and it refuses to guess a clinical value. A non-numeric result in a numeric field is rejected with the raw segment attached, never coerced into a number.

This is a parser, not an interface engine. There is no MLLP listener, no acknowledgement negotiation and no retry queue — those belong to a deployment and need a customer's environment.

Authorizations over X12 278#

An authorization can be rendered as a 278 request and its response parsed back, with both envelopes recorded against the request so a payer dispute can be answered with the segments that were actually exchanged rather than a summary of them. The channel is then stamped on the authorization, so "how did this reach the payer" has an answer.

An inbound segment cannot approve or deny. A parsed response is recorded; the determination still travels the normal review path. This is the same rule that keeps the AI advisory out of the state machine.

An unrecognised action code is reported as UNKNOWN rather than mapped onto a decision. Guessing what a payer meant is worse than saying you do not know.

Appointments#

Open slots, booking, and a lifecycle through confirmed, arrived, completed, cancelled and no-show. Cancelling releases the slot back to the pool; a no-show can be rebooked.

Two rules are inherited rather than reinvented. Booking is refused while a linked authorization is undecided — the same refusal the intervention workflow makes, enforced again here because booking is a second door into the same decision. And completing an appointment does not close a care gap: attendance is not evidence that the service was performed and resulted.

Slots are held internally. A production deployment federates slot search to the EHR; the booking rules stay here either way, because the authorization gate and the care-gap link are ours.

Voice (simulated)#

No call is placed. There is no telephony connected to this deployment: no carrier, no phone number, no audio, no speech recognition. This screen exists so the conversational flow can be shown and discussed, not to suggest the platform dials anyone today.

What is simulated is the channel. The controls in front of it are the real ones — the same consent record, the same quiet hours, the same audit trail that govern a text message.

Running a call in a demo#

Choose a member and an approved script, then start the call. The agent's side comes from the script; you choose what the member says at each turn. The conversation renders as it goes and ends with an outcome — appointment requested, callback, declined, escalated, opted out, voicemail or no answer.

Two scripts ship: care gap outreach and post-discharge follow-up. Both are versioned data, not text typed at dial time, for the same reason message templates are: an approved script is what makes an automated interaction defensible — a person approved the words, the system chose the moment.

The transcript is written to the member's contact history, so a simulated call appears in the member portal and the audit trail alongside every other contact.

What the agent will not do#

  • It does not assess, advise or triage. Tell it you feel unwell or have run out of medication and it says so plainly and hands over to a nurse. Recognising that it is out of scope is the behaviour, not a fallback.
  • It cannot start against an opted-out member, or inside their quiet hours.
  • It honours an opt-out spoken aloud exactly as it honours one sent by text — and that opt-out then blocks the next call. A member should not have to remember which channel they used to say stop.
  • It makes no coverage or clinical determination, on a call or anywhere else.

Member portal#

The one place in the platform a person outside the organisation can sign in. A member sees their own record in their own terms: what care is due and why, their appointments, their authorizations, their messages, and the contact preferences they control.

How they are kept apart#

This is a security boundary, not a screen, and it is enforced in three places that are not the interface:

  • A member login is bound to exactly one patient record, and the binding travels in the session token. Scoping never depends on a value the caller supplies.
  • The member role holds none of the staff permissions. It cannot reach a staff screen at all, so a forgotten check fails closed rather than leaking.
  • Every member endpoint re-checks ownership. A login not linked to a record is refused outright — a misconfiguration must not degrade into seeing everything.

Deliberately withheld: the operational risk tier (a stratification for care teams — telling someone they are "high risk" with no clinician present is an alarm, not care), reviewer notes, criteria evaluation and the AI advisory. The specific denial reason is shown, because CMS-0057-F entitles the member to it.

The member turns each channel on or off themselves rather than asking staff to do it, and a reply of STOP typed in the portal travels the same path as one texted — honoured immediately and enforced on every later send.

API reference#

All endpoints are JSON over HTTPS. Authenticate with JWT bearer tokens: sign in to receive an access token (15-minute lifetime) and a refresh token, then send Authorization: Bearer <access_token> on every call and exchange the refresh token for a new pair as needed. The permission column names the permission the server checks, see the matrix for which roles hold it.

MethodPathPermissionPurpose
POST/auth/loginpublic Sign in; returns access + refresh tokens, role and name. Throttled; lockout after 5 failures (423)
POST/auth/refreshvalid refresh token Exchange a refresh token for a fresh token pair
GET/auth/meany authenticated Current identity: user ID, tenant, role, credential
GET/population/patients/{id}/trajectorypatient.read First vs latest value per tracked measure, with direction. One reading = not evaluable
GET/population/outcomes/clinicalanalytics.read Population observed change plus the 30-day readmission rate
GET/ingest/batchespatient.read Inbound files with row counts and status
GET/ingest/batches/{id}/rowspatient.read Row-level outcome, reason and raw payload
POST/ingest/uploadvbc.write Stage and promote a roster, lab, ADT, condition or C-CDA file. Duplicate files recognised by digest
POST/edi/278/{ref}/buildpa.create Render an authorization as an X12 278 request and record the envelope
POST/edi/278/{ref}/responsepa.read Parse and record a 278 response. Recording only — it cannot approve or deny
GET/scheduling/slotspatient.read Open appointment slots by service or specialty
POST/scheduling/appointmentsvbc.write Book a slot. Refused (409) while a linked authorization is undecided
POST/scheduling/appointments/{id}/transitionvbc.write Confirm, arrive, complete, cancel or no-show. Cancelling releases the slot
GET/mpi/candidatespatient.read Probable duplicate records with score and basis
POST/mpi/candidates/{id}/mergevbc.admin Confirm a match. Consolidates identity only, and stays reversible
GET/voice/scriptspatient.read Approved call scripts. Simulated — no telephony is connected
POST/voice/callsvbc.write Start a simulated call. Refused on opt-out or quiet hours
POST/voice/calls/{id}/respondvbc.write Advance a call with the member's reply. Off-script responses refused
GET/me/profilemember.self Member's own record. Bound to one patient; risk tier deliberately excluded
GET/me/caremember.self What care is due, in member terms
GET/me/authorizationsmember.self Status, dates and the specific denial reason. No review detail
PUT/me/preferencesmember.self The member sets their own contact consent
POST/me/replymember.self Member reply. Opt-out keywords honoured first
GET/population/patients/{id}/clinicalpatient.read Longitudinal record: labs, problem list, SDOH, medications, procedures, encounters
GET/population/patients/{id}/riskpatient.read Current score, contributing factors with weights, and scoring history
POST/population/risk/recomputevbc.write Rescore the tenant; writes a new history row per member
GET/population/measurespatient.read Active measure definitions with version and steward
GET/population/qualityanalytics.read Measure rates with denominator, numerator, exclusions and open gaps
POST/population/quality/evaluatevbc.write Re-run evaluation and reconcile care gaps. Idempotent
GET/population/next-best-actionspatient.read Ranked recommendations with reasoning. Advisory only; creates nothing
GET/population/attributionpatient.read Attributed members for the open period, each with its method and rationale
POST/population/attribution/runvbc.admin Attribute the population to the open period. Idempotent
GET/population/cohortspatient.read Saved cohort definitions with live member counts
POST/population/cohortsvbc.write Save a cohort. Unknown filter keys are refused (422), never ignored
GET/population/patients/{id}/enrollmentspatient.read Programme enrollment history with status, dates and consent
POST/population/patients/{id}/enrollmentsvbc.write Record an enrollment transition. Updates the existing row rather than stacking duplicates
GET/finance/costanalytics.read Allowed, paid, member liability, PMPM over member-months, utilization per 1,000/year
GET/finance/claimsanalytics.read Claim headers with service lines
GET/finance/contractsanalytics.read Benchmark vs actual PMPM, quality gate, estimated settlement. Always labelled an estimate
GET/finance/outcomesanalytics.read Outcome metrics with method and trend; unmeasurable claims returned as NOT MEASURED
POST/finance/outcomes/snapshotvbc.admin Freeze today's measured outcomes so they can be trended
GET/finance/contract-healthanalytics.read Executive summary: PMPM, savings, quality score, settlement, gaps, risk mix — each with its status and a drill-down
GET/finance/cost-driversanalytics.read PMPM by cost category against the contract's category benchmark, with month-over-month move
GET/finance/cost-drivers/{category}/membersanalytics.read The members behind one cost category
GET/finance/utilizationanalytics.read Admissions, ED, readmissions, length of stay, follow-up. Metrics that cannot be computed honestly are returned unavailable, with the reason
GET/finance/utilization/{key}/membersanalytics.read The members behind one utilization measure
GET/finance/pmpm-trendanalytics.read PMPM per calendar month; the current month is flagged partial
GET/population/quality-opportunityanalytics.read Measures ranked by the composite points sitting inside them, against their configured target
GET/population/quality-opportunity/{measure}/membersanalytics.read Members in one measure's gap, with barrier, in-flight work and recommended action
GET/population/gate-pathanalytics.read Current score, the gate, and the specific gaps that close the shortfall
POST/population/scenarioanalytics.read Model additional gap closures against the gate and settlement. Stores nothing; closures capped at gaps open
GET/population/actionsanalytics.read Population-level next best actions. Advisory — creates nothing
GET/population/prioritiesanalytics.read Today's priorities with why, action, owner, due, status and escalation
GET/population/concentrationanalytics.read Highest-opportunity members: spend weighted by unworked open gaps and risk
GET/population/providersanalytics.read Provider-level panel: attributed members, risk mix, open gaps, gap closure, PMPM
GET/population/attribution-summaryanalytics.read Aggregate attribution by provider and method, with additions, removals and unattributed members
POST/population/campaignsvbc.admin Turn a measure or cohort into care-team tasks. Creates tasks only; tops up the live campaign for that target rather than creating a second
GET/population/campaignsanalytics.read Programme performance across campaigns
GET/population/campaigns/{id}analytics.read One campaign's eight-stage funnel and its largest drop
POST/population/campaigns/{id}/closevbc.admin Close a campaign. Tasks already created stay
GET/population/assignableanalytics.read Users who can act on care work, for assigning a campaign. Not /admin/users — assigning work is not administering accounts
GET/population/productivityanalytics.read Care-team delivery per person, outcomes first. No composite score, by design. Unmeasurable metrics declared with their reason
GET/population/productivity/{user}/workanalytics.read Every task and intervention behind one person's figures, with audit-derived timings
GET/vbc/summarypatient.read Dashboard: operational counts (due today, overdue, discharges to follow up, authorizations blocking care, stalled outreach, gaps closed this month) plus gap progress, engagement funnel, risk mix and the population totals
GET/population/panelpatient.read Members ranked by need, each row carrying its score components, the next best action, what is in flight and what has closed. Computed at request time, stored nowhere
GET/population/panel/follow-throughpatient.read Of the interventions you own: started, reached, completed, gaps closed, and the stalled ones
GET/population/trendsanalytics.read Every trendable metric with its movement, judged against that metric's own direction
GET/population/trends/{metric}analytics.read One metric's captured series plus a projection to period end — or the reason it will not project
POST/population/trends/capturevbc.admin Freeze today's values as trend points. Idempotent per day: a second run replaces the day rather than double-weighting it
GET/mpi/candidatespatient.read Probable duplicate records with match score and the basis for it
POST/mpi/scanvbc.admin Scan the tenant for duplicates, writing candidates
POST/mpi/candidates/{id}/mergevbc.admin Confirm a match. Consolidates identity only, and stays reversible
GET/transitionspatient.read Recent discharges with contact and visit clocks measured from the discharge date
POST/transitions/sweepvbc.write Raise follow-up tasks for open windows. Idempotent
GET/engagement/templatespatient.read Approved outreach templates by channel and locale
POST/engagement/sendvbc.write Send an approved template. Refused on opt-out or quiet hours (409). Cannot deliver externally in a synthetic deployment
POST/engagement/inboundvbc.write Record a member reply. Opt-out keywords honoured first; a confirmation advances the intervention
GET/vbc/patientspatient.read Tenant-scoped patient roster with risk tier, factors and model version
GET/vbc/patients/{id}patient.read Patient 360: demographics, risk evidence, care gaps, interventions with timelines
POST/vbc/interventionsvbc.write Create an intervention from a care gap; blocks duplicates (409); gap → IN_PROGRESS
POST/vbc/interventions/{id}/transitionvbc.write Move an intervention through its state machine; illegal moves and PA-gated scheduling → 409
POST/vbc/events/completionvbc.write Completion-evidence feed (simulates EHR/claims); deterministic gap auto-closure on match
GET/vbc/tasks?scope=mine|teamtask.read Work queue, ranked. mine (default) is work assigned to you; team adds everyone else's and the unassigned pile. Both counts ship with either response
PATCH/vbc/tasks/{id}vbc.write Close, dismiss, reopen or assign. owner_id claims it; "" releases it back to the team queue
GET/vbc/summarypatient.read Dashboard aggregates computed live from rows, so KPIs always reconcile to drill-downs
POST/pa/requirement-checkpa.read CRD-style discovery: is PA required for this member + CPT? Returns yes / no / unknown + policy + docs
POST/papa.create Create + submit a PA (idempotency key honored); snapshots policy version; stamps the SLA deadline
GET/papa.read PA work queue, tenant-scoped, newest first, with SLA due and auth numbers
GET/pa/{ref}pa.read PA detail including the full decision trace (reviews with kind, credential, model, outcomes), the authorization window and, on a denial, the specific reason text as well as its code
GET/pa/readinesspa.read Documentation readiness before a request exists. Takes patient_id and cpt; returns each criterion as evidenced, in-the-chart-unattached, or not evidenced
GET/pa/{ref}/readinesspa.read The same check against an existing request. Both sides of the desk see the same picture
POST/pa/{ref}/attach-documentpa.create Bind an existing member document to this request. Audited with file name and checksum. A repeat on the same request → 409; another member's document → 409
POST/assistant/askany authenticated Ask mdvin. Returns the answer, the facts behind it, the records it read, and the actions offered. Gated on the caller's own permissions, per question
GET/assistant/suggestionsany authenticated Opening questions appropriate to the caller's role, and whether phrasing is enabled
GET/auditaudit.read The audit trail. Filter by action, object, member, actor, correlation ID or date range; keyset paging on before_seq. The read is itself audited
GET/audit/actionsaudit.read The action vocabulary actually present, with counts
GET/audit/verifyaudit.read Recompute the hash chain; returns ok, how many were checked, and the first break
POST/pa/{ref}/ai-advisorypa.review Record an AI advisory row (model + version logged) — cannot change PA state
POST/pa/{ref}/decisionpa.review (deny: + pa.decide + MD credential) Human decision: approve / pend / deny. Deny additionally requires a specific reason code + text
GET/brandingpublic Brand config (name, tagline, company), the SPA reads this at boot
GET/healthpublic Liveness probe

Operations extras: GET /ready (database readiness probe) and GET /ops/audit-verify (recomputes the audit hash chain end-to-end and reports integrity). Every response carries an X-Correlation-ID header matching the audit rows the request produced.

Troubleshooting#

Most "errors" in this platform are guards doing their job. Here is what each status means and what to do.

StatusYou will see it when…What it means & what to do
401Any call after ~15 minutes idle; or a bad password at sign-in Session expired (access tokens live 15 minutes) or credentials wrong, sign in again. Login failures are deliberately generic ("Invalid credentials"): the system never confirms whether an email exists.
403An RN presses Deny; an Auditor tries to write; any role calls an endpoint its permissions don't cover By design. Your role lacks the permission (or, for denials, the physician credential). Switch to a user that holds it, see the matrix. The refused attempt is itself audited.
404Opening an object by ID that doesn't exist, or belongs to another tenant The platform never distinguishes "doesn't exist" from "not yours" (no existence leakage). Check the ID.
409(a) an illegal state transition, e.g. Illegal intervention transition OUTREACH -> COMPLETED; (b) Scheduling gated: PA-2026-xxxxx is IN_REVIEW; (c) Active intervention exists on a second Start Intervention The state machine refused. (a) follow the legal path in the diagrams; (b) wait for, or obtain, the PA decision, scheduling unlocks automatically; (c) use the existing intervention, one gap carries one active intervention.
422Denying without a reason: Denial requires a specific reason code and text; creating a PA for a member with no active coverage; an urgency other than standard/expedited The request was understood but violates a validation rule. For denials, supply a specific reason code and text. Generic denials are rejected in line with CMS-0057-F.
423Account temporarily locked at sign-in Five failed password attempts locked the account for 15 minutes. Wait it out, then sign in with the correct password from the credentials table.
When reporting an issue, include the correlation ID from the app's top bar (also returned on every response as X-Correlation-ID). It links your action to every audit row and log line it produced.

Glossary#

Prior Authorization (PA)
A payer's advance approval that a service is medically necessary and covered before it is delivered. The unit of work in the PA module.
Utilization Management (UM)
The payer discipline of reviewing care requests against clinical criteria, the review queue, nurses and medical directors in this platform.
Adverse determination
A denial (or partial approval) of requested care. Here it requires a physician credential and a specific reason, never automation.
SLA / TAT
Service-level agreement / turnaround time, the decision deadline on a PA. Stamped at submission: 72 hours expedited, 7 calendar days standard, per CMS-0057-F.
CMS-0057-F
The CMS Interoperability and Prior Authorization final rule. Its decision timeframes and specific-denial-reason requirements have been in force since January 1, 2026; its API requirements (Prior Authorization, Patient Access, Provider Access, Payer-to-Payer) take effect January 1, 2027.
Documentation readiness
The pre-submission check that matches a plan's criteria against the member's own record and reports each one as evidenced, present in the chart but not attached, or not evidenced. Deterministic: it cites evidence and never asserts a clinical fact.
Unattached evidence
A document that exists on the member but is not bound to the request being built. The most common avoidable cause of a pended authorization, and the case the readiness check exists to find.
Care gap
A measurable difference between recommended and delivered care for one member, tied to a versioned quality measure and closable only by clinical evidence.
Observed change
The difference between a member's earliest and latest reading of a tracked value. A measurement only — no claim is made about which intervention produced it.
Not evaluable
A member with a single reading, who therefore has no trajectory. Reported as such rather than as unchanged, because inventing a baseline would manufacture an improvement.
Index discharge
A discharge that counts in the readmission denominator. Only those whose 30-day window has fully elapsed qualify; a recent discharge is excluded, not counted as a success.
Staging (ingestion)
Landing inbound data as rows with their raw payload and outcome before promoting it into the clinical record, so provenance survives and partial failure stays legible.
Unmatched row
An inbound record that cannot be tied to a member with confidence. Held for a person rather than attached to a best guess.
X12 278
The EDI transaction set for a health care services review — an authorization request and its response. Still how most payers transact authorization today.
HL7 v2 / C-CDA
The message and document formats most hospital systems actually send: ADT for admissions and discharges, ORU for results, C-CDA for documents.
Simulated call
A scripted conversation used to demonstrate the engagement flow. No telephony is connected and no call is placed; the consent and audit controls around it are real.
Member login
The only non-employee role. Bound to exactly one patient record, holds no staff permission, and sees nothing about the business or any other member.
PMPM
Per member per month. Allowed dollars divided by member-months, not by member count, which is what makes it comparable to a payer benchmark.
Member months
The sum of months each attributed member was covered in a period. A member attributed for four months of a twelve-month period contributes four.
Attribution
The record of which members belong to an organisation for a performance period, and by what method. The denominator under every quality and financial number.
Benchmark / shared savings / withhold
Contract terms. The benchmark is the target PMPM; savings below it are shared at the contracted rate, gated on a minimum quality score, then reduced by the quality withhold.
Risk tier
HIGH, RISING, MEDIUM or LOW, produced by a named, versioned rule engine from the member's own clinical record. Every contributing factor carries its weight and source.
Denominator / numerator / exclusion
A measure's eligible population, the compliant subset of it, and members removed from consideration entirely. An excluded member is never counted as compliant.
Next best action
A ranked, advisory recommendation of what to do for which member and why. It creates nothing and never suggests an action the workflow would refuse.
Transitions of care
The post-discharge follow-up window. Both the 48-hour contact clock and the 7-day visit clock run from the discharge date, not from when the record arrived.
Master patient index (MPI)
Reconciliation of the same person appearing under different identifiers from different sources. Matches are proposed by score and confirmed by a person.
SDOH
Social drivers of health — housing, food, transport, isolation — recorded as Z-coded observations alongside clinical ones, because they are observations about a member like any other.
HEDIS
A widely used set of healthcare quality measures. The demo's measure keys (e.g. CDC-HBA1C, EED-RETINAL) follow this style of gap logic with explicit measure versions.
FHIR
Fast Healthcare Interoperability Resources, the modern HL7 standard for healthcare data exchange. The platform's integration surface is designed around FHIR R4 patterns.
CRD / DTR / PAS
The Da Vinci implementation guides for electronic PA: Coverage Requirements Discovery (is PA needed?), Documentation Templates & Rules (collect the evidence), Prior Authorization Support (submit and track). The requirement-check → launch → review flow mirrors this pattern, which the HTI-4 rule is building into certified EHRs.
X12 278
The legacy EDI transaction for PA requests/responses. Channel provenance on each PA records whether it arrived via FHIR, X12 278 or portal.
NCD / LCD
National / Local Coverage Determinations — Medicare's published coverage policies; the kind of source content a production policy library encodes as rule packages.
Rule package
A versioned, effective-dated bundle of coverage criteria keyed by payer, line of business and service codes. PAs snapshot the version they were judged under.
Gold carding
State-law programs exempting consistently-approved providers from PA for certain services. One reason PA applicability must be configuration, not code.
Idempotency key
A client-supplied token on PA creation guaranteeing that retries return the original request instead of creating duplicates.
Decision trace
The ordered record of every review action on a PA, kind (machine / human / AI advisory), criteria version, 5-state outcomes, credential, model version, sufficient to reproduce the decision later.
Correlation ID
A per-request identifier stamped on responses, logs and audit rows, tying one user action to everything it caused.
ACO
Accountable Care Organization, a provider group accountable for the total cost and quality of a population. The demo tenant is an ACO.
Medicare Advantage (MA)
Medicare benefits administered by private plans, the line of business on the demo members' coverage, and a primary target of CMS-0057-F.
TCOC / PMPM
Total cost of care / per-member-per-month, the financial lenses of value-based contracts that population dashboards ultimately roll up to.
Risk stratification
Ranking members by predicted need (HIGH / RISING / MEDIUM / LOW here), with each factor carrying provenance and the scoring model versioned.