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#
Signing in#
- 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.
- 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.
- Sessions use short-lived JWT access tokens (15 minutes) with a refresh token behind them.
If a request returns
401after a long idle period, sign in again.
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.
| Role | Password | What this role demonstrates | |
|---|---|---|---|
| Care Manager Jane Smith | jane@demo.aco | CareManager!2026x |
My Work queue, interventions, engagement, launching PAs |
| VBC Manager Alex Rivera | alex@demo.aco | VbcManager!2026xx |
Dashboard aggregates, population views |
| UM Nurse (RN) Sam Nurse | nurse@demo.payer | UmNurse!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.payer | MedDirector!2026 |
Adverse determinations with credential + specific reason |
| Medical Director 2 Dr. B. Rivera — MD, Ophthalmology | md2@demo.payer | MedDirector2!26x |
Independent appeal reviews (the original denier cannot decide an appeal) |
| Auditor Avery Auditor | audit@demo.aco | Auditor!2026xxxx |
Read-only decision traces |
| Platform Admin (backend) Pat Admin | admin@demo.aco | PlatformAdmin!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:
| State | Meaning |
|---|---|
CANDIDATE | Measure engine has flagged the member; not yet confirmed into the work stream |
OPEN | Confirmed gap, no active work, this is where Start Intervention appears |
IN_PROGRESS | An intervention is actively working the gap |
SCHEDULED | The closing service has an appointment on the books |
AWAITING_AUTH | Blocked on a pending prior authorization, the cross-module wait state |
SATISFIED | Measure numerator met per the engine; pending confirmation into CLOSED |
CLOSED | Closed on qualifying clinical evidence, the only way a gap closes |
EXCLUDED | Member meets a measure exclusion; gap leaves the denominator |
NOT_APPLICABLE | Measure does not apply to this member |
REOPENED | Evidence lapsed or was invalidated; the gap is live again |
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.
SCHEDULED → OUTREACH (no-show reopens outreach) ·
AWAITING_AUTH → OUTREACH (e.g. after a denial, re-engage on an alternative) ·
SCHEDULED → IN_PROGRESS → COMPLETED · COMPLETED → REOPENED.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.
WITHDRAWN is reachable from DRAFT, SUBMITTED and
PENDED. PARTIALLY_APPROVED follows the same appeal/closure paths as DENIED.
On approval the platform issues an auth number (AUTH-…) valid for 180 days.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.read | vbc.write | vbc.admin |
task.read | analytics.read |
pa.create | pa.read | pa.review |
pa.pend | pa.decide |
policy.read | policy.write |
audit.read | admin.all |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| care_manager | Yes | Yes | — | Yes | — | Yes | Yes | — | — | — | — | — | — | — |
| vbc_manager | Yes | Yes | Yes | Yes | Yes | — | Yes | — | — | — | — | — | — | — |
| executive | Yes | — | — | — | Yes | — | — | — | — | — | — | — | — | — |
| provider | Yes | Yes | — | — | — | Yes | Yes | — | — | — | — | — | — | — |
| pa_coordinator | Yes | — | — | — | — | Yes | Yes | — | — | — | — | — | — | — |
| um_nurse | — | — | — | — | — | — | Yes | Yes | Yes | — | — | — | — | — |
| medical_director | — | — | — | — | — | — | Yes | Yes | Yes | Yes | — | — | — | — |
| policy_admin | — | — | — | — | — | — | — | — | — | — | Yes | Yes | — | — |
| platform_admin | — | — | — | — | — | — | — | — | — | — | Yes | — | — | Yes |
| auditor | Yes | — | — | — | — | — | Yes | — | — | — | — | — | Yes | — |
= 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#
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
- 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.
- 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.
- 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.
- 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.
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.
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.
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.
The Patients list
- 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
- 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. - 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)
- On an
OPENgap, click Start Intervention. - 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.
- The intervention appears in the Interventions panel in state
IDENTIFIED, its first timeline event reading "created, context inherited from care gap". The gap moves toIN_PROGRESS. - 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
- On the
IDENTIFIEDintervention, click Begin outreach → state becomesOUTREACH. - Click Patient responded →
ENGAGED. Every click appends a timestamped event to the timeline. - Click Schedule →
SCHEDULED— unless a linked PA is still pending. If you launched a PA that has not been decided, the server refuses with409 Scheduling gated: PA-2026-xxxxx is IN_REVIEW.What you should seeWhile the PA is pending the intervention sits inAWAITING_AUTHwith a stopwatch chip ("awaiting payer decision") and no schedule button at all. The moment the PA is approved, the intervention flips toAUTH_APPROVEDautomatically and the button returns as Schedule (auth on file).
Recording completion evidence & auto-closure
- On a
SCHEDULEDintervention, click Record completion evidence. This simulates the EHR/claims completion feed a production deployment receives automatically. - Choose the evidence type —
lab,encounterorprocedure, and enter the CPT observed (e.g.83036for the HbA1c gap,92250for the retinal exam), then Send event. - 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
COMPLETEDtoGAP_CLOSEDand the gap becomesCLOSED. On a mismatch: "Evidence insufficient, gap stays IN_PROGRESS (logged)" and an audit event records exactly what was offered and rejected. - 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
- 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.
- 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.
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.
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
- 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. - 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_AUTHand the gap showsAWAITING_AUTHtoo. - The policy package and version in force are snapshotted onto the request, and the submission passes intake
validation into
IN_REVIEWwith 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
- Click Review on an
IN_REVIEWPA, 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". - 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
- In the review dialog, record the criterion outcomes. Outcomes are 5-state —
MET,NOT_MET,UNKNOWN,NOT_APPLICABLE,NEEDS_HUMAN_REVIEW, never collapsed to yes/no. - 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.
- 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. - 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 answers422 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 →DENIEDwith the reason stored on the request, yourMD:Ophthalmologycredential 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:
| State | What it means | What 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 |
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 about | Example | Needs |
|---|---|---|
| A request's status | What is the status of PA-2026-0001? | pa.read |
| What is blocking a request | What is blocking my authorizations? | pa.read |
| Deadlines | Which requests are close to their SLA? | pa.read |
| Why authorization is needed | Why does Sarah Martinez need a prior auth for 92250? | pa.read |
| Open care gaps | Which care gaps are open for John Doe? | patient.read |
| Your queue | What should I work on next? | patient.read |
| Risk scoring | What is driving John Doe's risk score? | patient.read |
| Recommendations | What do you recommend I do next? | patient.read |
| Discharges | Who was discharged recently? | patient.read |
| Population | How many members are attributed? | patient.read |
| Quality rates | What are our measure rates? | analytics.read |
| Cost | What is our PMPM? | analytics.read |
| Contract position | How are we tracking against the benchmark? | analytics.read |
| Outcomes | Which 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#
| Control | Behaviour |
|---|---|
| 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.
| # | Actor | Action | System response |
|---|---|---|---|
| 1 | Care Manager (jane) | Patients → open Sarah Martinez's 360 | HIGH risk with evidence (HbA1c 10.1% · lab); gap Diabetic Retinal Exam is OPEN |
| 2 | Care Manager | Start 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 |
| 3 | Care Manager | Launch 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 |
| 4 | Care Manager | Attempt to schedule anyway | 409 Scheduling gated: PA-2026-xxxxx is IN_REVIEW, the appointment cannot outrun the authorization |
| 5 | UM Nurse (nurse) | Open Review Queue → Review the PA | SLA clock, policy snapshot v3 and criteria context displayed |
| 6 | UM Nurse | Run AI criteria analysis | Advisory row recorded (apex-um v1.8) — no state change |
| 7 | UM Nurse | Press Deny | 403 Adverse determinations require a medical director, refusal is itself audited |
| 8 | Medical 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 |
| 9 | Care Manager | Schedule (auth on file) | Gate passes, intervention → SCHEDULED. Note: the gap is still open. Approval ≠ care delivered |
| 10 | Care Manager | Record completion evidence: procedure / CPT 92250 |
Deterministic match → intervention COMPLETED → GAP_CLOSED; gap → CLOSED; toast
"Care gap closed automatically; evidence recorded" |
| 11 | Auditor (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 |
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
Trends and projection#
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.
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.
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.
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.
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.
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.
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.
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 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)#
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.
Consent, in their own hands#
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.
| Method | Path | Permission | Purpose |
|---|---|---|---|
| POST | /auth/login | public | Sign in; returns access + refresh tokens, role and name. Throttled; lockout after 5 failures (423) |
| POST | /auth/refresh | valid refresh token | Exchange a refresh token for a fresh token pair |
| GET | /auth/me | any authenticated | Current identity: user ID, tenant, role, credential |
| GET | /population/patients/{id}/trajectory | patient.read |
First vs latest value per tracked measure, with direction. One reading = not evaluable |
| GET | /population/outcomes/clinical | analytics.read |
Population observed change plus the 30-day readmission rate |
| GET | /ingest/batches | patient.read |
Inbound files with row counts and status |
| GET | /ingest/batches/{id}/rows | patient.read |
Row-level outcome, reason and raw payload |
| POST | /ingest/upload | vbc.write |
Stage and promote a roster, lab, ADT, condition or C-CDA file. Duplicate files recognised by digest |
| POST | /edi/278/{ref}/build | pa.create |
Render an authorization as an X12 278 request and record the envelope |
| POST | /edi/278/{ref}/response | pa.read |
Parse and record a 278 response. Recording only — it cannot approve or deny |
| GET | /scheduling/slots | patient.read |
Open appointment slots by service or specialty |
| POST | /scheduling/appointments | vbc.write |
Book a slot. Refused (409) while a linked authorization is undecided |
| POST | /scheduling/appointments/{id}/transition | vbc.write |
Confirm, arrive, complete, cancel or no-show. Cancelling releases the slot |
| GET | /mpi/candidates | patient.read |
Probable duplicate records with score and basis |
| POST | /mpi/candidates/{id}/merge | vbc.admin |
Confirm a match. Consolidates identity only, and stays reversible |
| GET | /voice/scripts | patient.read |
Approved call scripts. Simulated — no telephony is connected |
| POST | /voice/calls | vbc.write |
Start a simulated call. Refused on opt-out or quiet hours |
| POST | /voice/calls/{id}/respond | vbc.write |
Advance a call with the member's reply. Off-script responses refused |
| GET | /me/profile | member.self |
Member's own record. Bound to one patient; risk tier deliberately excluded |
| GET | /me/care | member.self |
What care is due, in member terms |
| GET | /me/authorizations | member.self |
Status, dates and the specific denial reason. No review detail |
| PUT | /me/preferences | member.self |
The member sets their own contact consent |
| POST | /me/reply | member.self |
Member reply. Opt-out keywords honoured first |
| GET | /population/patients/{id}/clinical | patient.read |
Longitudinal record: labs, problem list, SDOH, medications, procedures, encounters |
| GET | /population/patients/{id}/risk | patient.read |
Current score, contributing factors with weights, and scoring history |
| POST | /population/risk/recompute | vbc.write |
Rescore the tenant; writes a new history row per member |
| GET | /population/measures | patient.read |
Active measure definitions with version and steward |
| GET | /population/quality | analytics.read |
Measure rates with denominator, numerator, exclusions and open gaps |
| POST | /population/quality/evaluate | vbc.write |
Re-run evaluation and reconcile care gaps. Idempotent |
| GET | /population/next-best-actions | patient.read |
Ranked recommendations with reasoning. Advisory only; creates nothing |
| GET | /population/attribution | patient.read |
Attributed members for the open period, each with its method and rationale |
| POST | /population/attribution/run | vbc.admin |
Attribute the population to the open period. Idempotent |
| GET | /population/cohorts | patient.read |
Saved cohort definitions with live member counts |
| POST | /population/cohorts | vbc.write |
Save a cohort. Unknown filter keys are refused (422), never ignored |
| GET | /population/patients/{id}/enrollments | patient.read |
Programme enrollment history with status, dates and consent |
| POST | /population/patients/{id}/enrollments | vbc.write |
Record an enrollment transition. Updates the existing row rather than stacking duplicates |
| GET | /finance/cost | analytics.read |
Allowed, paid, member liability, PMPM over member-months, utilization per 1,000/year |
| GET | /finance/claims | analytics.read |
Claim headers with service lines |
| GET | /finance/contracts | analytics.read |
Benchmark vs actual PMPM, quality gate, estimated settlement. Always labelled an estimate |
| GET | /finance/outcomes | analytics.read |
Outcome metrics with method and trend; unmeasurable claims returned as NOT MEASURED |
| POST | /finance/outcomes/snapshot | vbc.admin |
Freeze today's measured outcomes so they can be trended |
| GET | /finance/contract-health | analytics.read |
Executive summary: PMPM, savings, quality score, settlement, gaps, risk mix — each with its status and a drill-down |
| GET | /finance/cost-drivers | analytics.read |
PMPM by cost category against the contract's category benchmark, with month-over-month move |
| GET | /finance/cost-drivers/{category}/members | analytics.read |
The members behind one cost category |
| GET | /finance/utilization | analytics.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}/members | analytics.read |
The members behind one utilization measure |
| GET | /finance/pmpm-trend | analytics.read |
PMPM per calendar month; the current month is flagged partial |
| GET | /population/quality-opportunity | analytics.read |
Measures ranked by the composite points sitting inside them, against their configured target |
| GET | /population/quality-opportunity/{measure}/members | analytics.read |
Members in one measure's gap, with barrier, in-flight work and recommended action |
| GET | /population/gate-path | analytics.read |
Current score, the gate, and the specific gaps that close the shortfall |
| POST | /population/scenario | analytics.read |
Model additional gap closures against the gate and settlement. Stores nothing; closures capped at gaps open |
| GET | /population/actions | analytics.read |
Population-level next best actions. Advisory — creates nothing |
| GET | /population/priorities | analytics.read |
Today's priorities with why, action, owner, due, status and escalation |
| GET | /population/concentration | analytics.read |
Highest-opportunity members: spend weighted by unworked open gaps and risk |
| GET | /population/providers | analytics.read |
Provider-level panel: attributed members, risk mix, open gaps, gap closure, PMPM |
| GET | /population/attribution-summary | analytics.read |
Aggregate attribution by provider and method, with additions, removals and unattributed members |
| POST | /population/campaigns | vbc.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/campaigns | analytics.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}/close | vbc.admin |
Close a campaign. Tasks already created stay |
| GET | /population/assignable | analytics.read |
Users who can act on care work, for assigning a campaign. Not /admin/users — assigning work is not administering accounts |
| GET | /population/productivity | analytics.read |
Care-team delivery per person, outcomes first. No composite score, by design. Unmeasurable metrics declared with their reason |
| GET | /population/productivity/{user}/work | analytics.read |
Every task and intervention behind one person's figures, with audit-derived timings |
| GET | /vbc/summary | patient.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/panel | patient.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-through | patient.read |
Of the interventions you own: started, reached, completed, gaps closed, and the stalled ones |
| GET | /population/trends | analytics.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/capture | vbc.admin |
Freeze today's values as trend points. Idempotent per day: a second run replaces the day rather than double-weighting it |
| GET | /mpi/candidates | patient.read |
Probable duplicate records with match score and the basis for it |
| POST | /mpi/scan | vbc.admin |
Scan the tenant for duplicates, writing candidates |
| POST | /mpi/candidates/{id}/merge | vbc.admin |
Confirm a match. Consolidates identity only, and stays reversible |
| GET | /transitions | patient.read |
Recent discharges with contact and visit clocks measured from the discharge date |
| POST | /transitions/sweep | vbc.write |
Raise follow-up tasks for open windows. Idempotent |
| GET | /engagement/templates | patient.read |
Approved outreach templates by channel and locale |
| POST | /engagement/send | vbc.write |
Send an approved template. Refused on opt-out or quiet hours (409). Cannot deliver externally in a synthetic deployment |
| POST | /engagement/inbound | vbc.write |
Record a member reply. Opt-out keywords honoured first; a confirmation advances the intervention |
| GET | /vbc/patients | patient.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/interventions | vbc.write |
Create an intervention from a care gap; blocks duplicates (409); gap → IN_PROGRESS |
| POST | /vbc/interventions/{id}/transition | vbc.write |
Move an intervention through its state machine; illegal moves and PA-gated scheduling → 409 |
| POST | /vbc/events/completion | vbc.write |
Completion-evidence feed (simulates EHR/claims); deterministic gap auto-closure on match |
| GET | /vbc/tasks?scope=mine|team | task.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/summary | patient.read |
Dashboard aggregates computed live from rows, so KPIs always reconcile to drill-downs |
| POST | /pa/requirement-check | pa.read |
CRD-style discovery: is PA required for this member + CPT? Returns yes / no / unknown + policy + docs |
| POST | /pa | pa.create |
Create + submit a PA (idempotency key honored); snapshots policy version; stamps the SLA deadline |
| GET | /pa | pa.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/readiness | pa.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}/readiness | pa.read |
The same check against an existing request. Both sides of the desk see the same picture |
| POST | /pa/{ref}/attach-document | pa.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/ask | any 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/suggestions | any authenticated | Opening questions appropriate to the caller's role, and whether phrasing is enabled |
| GET | /audit | audit.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/actions | audit.read |
The action vocabulary actually present, with counts |
| GET | /audit/verify | audit.read |
Recompute the hash chain; returns ok, how many were checked, and the first break |
| POST | /pa/{ref}/ai-advisory | pa.review |
Record an AI advisory row (model + version logged) — cannot change PA state |
| POST | /pa/{ref}/decision | pa.review (deny: + pa.decide + MD credential) |
Human decision: approve / pend / deny. Deny additionally requires a specific reason code + text |
| GET | /branding | public | Brand config (name, tagline, company), the SPA reads this at boot |
| GET | /health | public | 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.
| Status | You will see it when… | What it means & what to do |
|---|---|---|
| 401 | Any 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. |
| 403 | An 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. |
| 404 | Opening 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. |
| 422 | Denying 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. |
| 423 | Account 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. |
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.