Read-back primitive guide

Patient verification automation, framed as a read-back loop.

Patient verification automation is what most healthcare ops teams actually mean when they say “eligibility automation”: unattended software that, before a visit, confirms a patient's identity, demographics, and insurance eligibility against the EHR and the payer. The structured part of the problem (the 270/271 round-trip through a clearinghouse) is well covered. The unstructured part (the long tail of payers whose eligibility data only lives inside a portal UI) is where the automation shape changes from a write loop to a read-back loop, and where the primitives that decide whether the workflow is auditable stop being side effects of action commands and start being a separable layer of their own. This is the layer.

M
Matthew Diakonov
11 min

Direct answer (verified 2026-04-29)

Patient verification automation is unattended software that confirms a patient's identity, demographics, and insurance eligibility before a visit by reading data from your scheduling or intake source, checking it against the EHR record and the payer's eligibility system, and writing the verified status back into the EHR. Half the work goes to a clearinghouse via X12 270/271 (Aetna, UHC, Cigna, BCBS plans that participate); half goes to a payer portal UI that has no API (regional plans, Medicaid managed care, workers' comp). The portal half is the read-back half: the automation drives the portal, waits for the result page to render, reads the literal eligibility status from the rendered page, and writes the status into the EHR coverage field. Mediar's runtime exposes the read-back primitives (validate_element, wait_for_element, get_window_tree) as a separate layer from the action primitives, defined in apps/desktop/src/lib/ask-mode-tools.ts and published in the open-source SDK at github.com/mediar-ai/terminator.

The two halves of the problem

Patient verification has a structured half and an unstructured half, and treating them as one workflow is the source of most of the stalled-automation stories on this. The structured half is the EDI eligibility transaction. A 270 inquiry goes out from your practice management system through a clearinghouse to the payer; a 271 response comes back with member status, plan coverage, copay, deductible, in-network and out-of-network benefits. For the payers and plans that participate cleanly, this round-trip finishes in well under a second and produces a structured record you can write straight into the EHR coverage tab.

The unstructured half is the long tail. Regional Blues plans where the 271 response comes back with the eligibility-detail field empty. Medicaid managed-care plans whose state contracts run through portals the clearinghouse never integrated. Workers' compensation plans, motor-vehicle plans, and out-of-state student health plans that have a portal but not an EDI gateway at all. For a hospital with thirty payers in the mix, the long tail is somewhere between 15% and 40% of the eligibility checks the front desk runs on a normal week, and 100% of the eligibility checks that the front desk has to do by hand because the clearinghouse came back empty.

Patient verification automation is the second half. The first half is already automated by a clearinghouse you probably pay for. The second half is the part that has registrars logging into ten portals a day, typing a member ID, clicking a button, reading the word “Active” off the screen, alt-tabbing back to Epic, and typing it into the coverage field. The shape of the workflow is unmistakable once you see it: it is a read loop with one write at the end, not a write loop with confirmation.

Why the read-back framing changes the workflow

The verification is one transaction. You send a 270, the payer answers with a 271, your stack parses the response into structured fields, and the workflow writes the parsed fields into the EHR. Failure modes are network errors and parse errors. Retry policy is 'try again, the answer is deterministic'. There is no separate 'read' step because the read is the parse, and the parse is local.

  • One transaction, structured response
  • Retry on transient errors is safe
  • Failure mode: network or parse
  • Covers the participating-payer half

The read-only primitive set

Mediar's runtime publishes a separate list of read-only primitives inside the Terminator MCP. The list lives in apps/desktop/src/lib/ask-mode-tools.ts under the constant ASK_MODE_ALLOWED_TOOLS. The list is the set of operations a workflow can take in “Ask mode” without modifying any application state. Nine entries:

  • get_window_tree. Returns the full accessibility tree of a target window. The verification workflow uses this once at the end to snapshot the rendered payer-portal result page into the audit log, alongside the extracted status. If a downstream auditor disputes whether the page actually said “Active”, the tree is the primary record.
  • get_applications_and_windows_list. Returns every running application and window. Used at workflow start to confirm the portal browser and the EHR client are both up and reachable before the verification pass even begins.
  • validate_element. Resolves a selector against the current accessibility tree and returns success or failure. The role-plus-name selector format (e.g. role:Text|name:Active) means a payer status check is a one-line gate. If the literal text is not on the rendered page, validation fails and the workflow stops with the on_error policy.
  • wait_for_element. Blocks until a selector resolves with a specified condition. The schema accepts exactly four conditions: visible, enabled, focused, exists. Default timeout 10000 ms. The four-way distinction is load-bearing on real payer portals (a tab can be visible without being enabled while the chart layer hydrates) and is what allows the verification pass to proceed at the right beat instead of polling.
  • capture_screenshot. Takes a PNG of the active screen. Used as a secondary audit artifact when the accessibility tree is not enough to settle a dispute (a portal that renders the eligibility status as a decorative SVG rather than as text, for example, where the tree does not contain a literal “Active” node and only the rendered pixels do).
  • highlight_element and hide_inspect_overlay. Visual debugging primitives. Used when a workflow author is recording or hand-tuning a verification pass against a portal, not at runtime.
  • delay. An explicit sleep. Different from wait_for_element because it is unconditional and named. The right primitive when the portal animates a result into view over a fixed duration that nothing in the accessibility tree expresses.
  • activate_element. Brings a window or pane to the foreground. Counted in the read-only set because activating a window does not modify application state, but it does change the OS focus, which is what every subsequent wait_for_element check evaluates against.

The action half (click_element, type_into_element, press_key, set_value, scroll_element, select_option, invoke_element) lives in a different list. A workflow runner that loads only the read-only list can run the entire verification pass right up to the point where the “Check Eligibility” button has to be clicked. That is the property that makes the read layer auditable on its own: the read-only run is a dry-run that walks the portal, confirms every selector resolves, confirms every wait condition fires, and surfaces a real result page without ever having sent the eligibility request itself.

A real eligibility pass, step by step

Below is an abbreviated portal-driven verification pass against a payer that does not respond to 270/271 cleanly. The workflow is shaped like every other one of these: foreground the portal, type the candidate identifiers, click check, wait for the result, validate the literal status, snapshot the page, switch to the EHR, type the verified status into the chart. The interesting part is how few action primitives appear and how many read primitives sit between them.

Portal-driven eligibility check
mk0r preview

activate_element(role:Window|name:Aetna Provider Portal) wait_for_element(role:Edit|name:Member ID, condition: visible, timeout: 10000)

1/5Bring the payer portal to the foreground, wait for the Member ID field to actually render. The wait condition is 'visible', not a sleep.

The shape of the pass matters more than the specific calls. activate_element opens the session. wait_for_element with the visible condition gates the typing. Two type_into_element calls write the identifiers. One click_element fires the request. wait_for_element gates again, this time on the result page rendering. validate_element decides whether the answer is “Active”. get_window_tree captures the full rendered page for the audit log. activate_element switches to Epic. wait_for_element with the enabled condition gates the typing into the chart, because Epic's coverage tab loads asynchronously after the chart opens and the difference between “visible” and “enabled” is the difference between the type going into a stub control and the type going into the live field.

The on_error directive on validate_element is the load-bearing policy. Set to stop, the workflow halts if the result page does not contain “Active”. It does not retry the click. It does not fall through to the EHR write. It surfaces a record that the eligibility came back something other than Active and lets a human decide whether the answer is “Inactive”, “Pending”, or “Plan terminated”. A retry-on-failure policy here is the bug, not the feature.

40-90s

A portal-driven eligibility check averages forty to ninety seconds of wall-clock time on the payer's portal. At the runtime meter, that's roughly fifty cents to a dollar of compute per check, against ten to fifteen minutes of registrar labor for the same check by hand. The cost shape only inverts when the payer supports 270/271 cleanly through your clearinghouse, which is why both belong in the same workflow stack.

Mediar pricing — $0.75/min runtime

Why role-plus-name selectors survive payer redesigns

The brittleness story most ops teams associate with desktop and portal automation is the “payer redesigned the portal and the bot broke for two weeks” story. The reason it broke is almost always selector choice: the recording captured the eligibility button by CSS class or by absolute XPath, and the redesign changed both. Mediar's recordings capture by accessibility role plus accessible name, the same pair a screen reader uses. That pair is what payer portals are required to keep stable for ADA compliance, and is what the WCAG 2.1 success criteria 1.3.1 and 4.1.2 specifically require: a programmatically-determinable name and role for every interactive control.

Concretely, the selector role:Edit|name:Member ID resolves on a layout change as long as the input still has an accessible name of “Member ID” and an accessibility role of “Edit”. CSS classes can change, layout can shift from grid to flex, the input can move three hundred pixels left, the button can grow or shrink, and the role plus name pair remains. When the portal renames the field from “Member ID” to “Subscriber ID” the selector does break, but it breaks loudly: the validate_element gate fails, the workflow stops, and the surfaced error names the missing selector. The fix is a one-line edit, not a workflow rewrite, and it is the kind of fix a registrar can ship after the first failed run.

The trade-off is not free. A portal that ships a custom control with no accessible role and no accessible name is a portal where the selector vocabulary cannot describe the element. The remediation inside the runtime is the Path selector (an accessibility-tree position) or the NativeId selector (a platform-specific automation id). The remediation outside the runtime is the same one a screen-reader user has on that portal: file a complaint and route the plan through the clearinghouse if it ever supports 270/271. The portals that fail accessibility tend to be the same portals that fail compliance audits for unrelated reasons, so the failure surface tracks.

What the read-only layer means for compliance

A read-only primitive set is the simplest answer to one of the first questions a HIPAA security review asks: can the automation run a partial pass that touches PHI but does not modify any system of record? The answer with Ask mode loaded is yes. The workflow can drive the portal, read the eligibility result, snapshot the rendered page into an audit artifact, and stop. No click on the EHR coverage field. No type into the chart. No state change in any system. That partial pass is the dry-run a security reviewer can watch before signing off on the full automation, and it is the same partial pass that runs every time the workflow has to surface an eligibility result for a human to interpret.

The runtime is SOC 2 Type II certified and operates within the existing security perimeter; there is no PHI egress unless the workflow author writes one. The audit log captures every primitive call with its selector, its result, and the timestamp; the accessibility-tree snapshot from get_window_tree is the secondary record when a downstream reviewer disputes what the screen actually said. The split between the read-only list and the action list is what makes the audit log readable: a verification pass shows up as eight read calls and two writes, not as ten opaque “execute” calls.

Where this approach refuses to be the answer

Two cases. First, plans that genuinely support 270/271 cleanly through your clearinghouse. Driving a portal there is slower (forty to ninety seconds versus under a second), more expensive per check, and adds an extra failure surface (the portal session, the captcha if the portal added one, the rendering wait) for no benefit. The right routing is structured first, portal second. The runtime exists for the second-second, not as a replacement for the first.

Second, payer portals whose result pages render the eligibility status only as a styled SVG or a CSS-painted glyph with no accessible text node. This is rare on portals built in the last five years (WCAG audits flag it), but it does occur on a few legacy plan portals. The selector vocabulary cannot describe a text node that does not exist, and validate_element on a literal like “Active” will fail because the text is not in the tree. The remediation is to fall back to capture_screenshot plus a visual-OCR pass on the rendered region, which the runtime supports but at the cost of the determinism that the accessibility-tree path provides. For a buyer, the question to ask any portal-driving runtime is which fallback it picks when the tree is empty; a runtime that silently drops to OCR without telling you is the one that produces the surprise denials six months in.

Watch a verification pass run on one of your payers

A 30-minute pilot call on your screen. We pick a non-270/271 payer from your registration backlog, record one verification pass against the portal, and you leave with the YAML the read-back primitives produced and the audit-log shape they emit at runtime.

Frequently asked questions

What is patient verification automation, in one sentence?

Unattended software that confirms a patient's identity, demographics, and insurance eligibility before a visit by reading data from your scheduling or intake source, checking it against the EHR record and the payer's eligibility system, and writing the verified status back into the EHR. The hard half is the payer-eligibility check against payers that do not expose X12 270/271 cleanly through your clearinghouse, where the automation has to drive a payer portal UI and read the result back from the rendered page rather than parse a structured response.

Why frame this as a read-back loop instead of a data-entry loop?

Because the operation that decides whether the workflow succeeds is the read, not the write. Patient verification is a check. Writing the status into the EHR is a side effect of a successful check. If you frame the loop as data-entry-with-confirmation, every retry policy in your stack is wrong: the right answer to 'eligibility came back denied' is to stop and surface, never retry. The right answer to 'the result page never rendered' is wait longer or escalate, never resubmit. Treating the read as a separate primitive (with its own conditions, timeouts, and on_error policy) is the design that makes those distinctions explicit.

What read-only primitives does Mediar publish?

Nine, defined in apps/desktop/src/lib/ask-mode-tools.ts under ASK_MODE_ALLOWED_TOOLS: get_window_tree (the full UI tree of a window), get_applications_and_windows_list, validate_element (does this selector resolve), wait_for_element (block until a condition holds), capture_screenshot, highlight_element (visual debug), hide_inspect_overlay, delay (explicit sleep), and activate_element (focus a window without modifying state). The wait_for_element primitive accepts exactly four conditions: visible, enabled, focused, exists. The fact that this list is published as a separate constant from the action tools is the point — Ask mode lets a workflow run the whole verification pass without ever clicking anything.

How does this differ from clearinghouse-based verification (Availity, Waystar, Change Healthcare, Experian Health)?

Clearinghouses are the right answer for any payer that supports X12 270/271 cleanly through the clearinghouse network. Most large national payers do; the response is a structured record and the verification finishes in milliseconds. The gap is the long tail of regional and Medicaid managed-care plans that do not respond well over 270/271 — the response is incomplete, the eligibility-detail field is empty, or the plan is not in the clearinghouse's panel at all. For those, the existing ops answer is 'a human logs into the payer portal and reads the screen'. Patient verification automation is what replaces step 2. Mediar specifically replaces step 2; clearinghouses still own step 1.

Why is wait_for_element a separate primitive and not just a timeout on click_element?

Because the wait condition has a vocabulary. The four allowed conditions (visible, enabled, focused, exists) describe four genuinely different states a payer-portal element can be in, and the workflow that picks the wrong one fails differently. 'visible' means the DOM or accessibility tree contains the element with a positive bounding box. 'enabled' means the element accepts input (a Coverage tab in Epic is often visible before it is enabled, because the chart's data layer loads asynchronously). 'focused' means keyboard focus is on the element (relevant when the next step types into it). 'exists' means the selector resolves at all (the loosest check, used when the element is off-screen or in a collapsed pane). Folding all four into 'click with a timeout' silently picks one of them, usually 'exists' or 'visible', and silently fails when the workflow needed one of the others.

Is this the same workflow as clinical decision support or prior auth automation?

No. Prior auth is a write-then-wait loop: you submit a request, the payer responds asynchronously (sometimes hours, sometimes days), and you reconcile when the response lands. Clinical decision support is a real-time advice layer that runs inside the chart while a clinician documents. Patient verification automation runs in the registration-and-scheduling phase, before either of those, and is bounded by a single sync transaction (a portal session or a 270/271 round-trip). The same primitive set can drive a prior-auth submission flow, but the gate functions are different: a verification flow stops on a denied result, a prior-auth flow stores a pending tracking number and returns later.

What stops the recorded workflow from breaking when the payer portal redesigns the UI?

The selector vocabulary. The recording captures elements by accessibility role plus accessible name (role:Edit|name:Member ID), not by visual position or DOM XPath. When a portal vendor renames a CSS class, swaps a layout from grid to flex, or shifts a button five pixels, none of those changes the role or the name. When the portal renames the field 'Member ID' to 'Subscriber ID', that is a change you have to acknowledge — but it is a single edit (one selector), not a workflow rewrite, and it is exactly the kind of change a human registrar would notice and flag the first time they ran the automation on a redesigned screen.

Where does this approach refuse to be the answer?

Two cases. First, payer portals that hide their content inside an iframe of an iframe with no accessibility tree (rare, but it happens with a few legacy plan portals). The selectors cannot describe an element the OS does not expose. The remediation is to push the payer to expose UIA or to fall back to the clearinghouse for that plan. Second, plans that genuinely do support 270/271 cleanly through your clearinghouse — driving a portal in those cases costs more per check and is slower than the structured EDI round-trip. The rule of thumb on the desk: route 270/271-supported plans through the clearinghouse, route the rest through the portal-driving runtime, and reconcile both into the same EHR coverage field with the same downstream audit log.

What does Mediar charge for a single verification pass?

$0.75 per minute of runtime, drawn against a $10,000 turn-key program fee that converts to credits. A real portal-driven eligibility check averages 40 to 90 seconds of wall-clock time depending on the payer's portal latency, so the per-check meter is on the order of fifty cents to a dollar of compute. The unit cost reads small only if the alternative is the registrar's per-minute fully-loaded labor; it does not compete with a 270/271 round-trip on price-per-check, which is why both belong in the same workflow stack instead of one replacing the other.

Is the runtime open source?

The Terminator runtime, including the selector vocabulary, the wait-condition primitives, and the accessibility-tree readers, is published as an SDK at github.com/mediar-ai/terminator with TypeScript bindings on the Rust core. The product on top (the no-code recorder, the cloud executor, the workflow synthesis pipeline) is closed-source. The split matters for healthcare buyers because the layer that touches PHI is the runtime, and the runtime can be audited end-to-end before any of the closed components see a real patient identifier.