# The integrator's view

  Every call on this rail is made while one person stands in front of another, waiting.

## Why this page exists

Endpoint documentation tells you what a payload contains. It does not tell you which screen
the payload fills, who is waiting on it, or what happens to that person when it fails.

That is what this page covers. Everything below runs in three lanes: the desk, which is what
a person sees; your system, which is what you render and call; and the rail, which answers.

The screens below are low fidelity on purpose. They are an argument about what has to be on
screen at each moment, and which field puts it there, not a UI to copy.

## The shape of a visit

Start with the shape, before any screens. A visit behaves less like a form submission and
more like a conversation with pauses in it, and the pauses are where integrations get
designed badly.

<Mermaid chart={`sequenceDiagram
    autonumber
    participant D as Front desk
    participant S as Your system
    participant R as The rail
    D->>S: Patient presents an ID
    S->>R: Resolve identity, with consent
    R-->>S: unique_patient_id + masked contacts
    S->>R: List covers
    R-->>S: Every usable cover
    D->>S: Operator picks a cover
    S->>R: Select cover
    R-->>S: selection_ref
    S->>R: Read the benefit tree
    R-->>S: billable items
    S->>R: Ask for an authentication
    R-->>S: A URL to embed
    S->>D: Render consent (member proves presence)
    R-->>S: authentication id
    S->>R: Open the visit
    R-->>S: visit_id
`} />

Note what the desk does and does not do. It presents an identifier and it picks a cover.
Everything else is your system and the rail talking. If your design has the operator making
decisions at any other point, you have moved work onto a person who does not want it.

## Screen 1 · The front desk

The first screen has one job, which is to turn a human being into an identifier. It usually
fails because the identifier type was never captured alongside the value.

<Wireframe
  title="Find patient"
  actor="Front desk"
  rows={[
    [
      { label: "Identifier type", kind: "field", span: 1, from: "GET /concepts/?system=IDENTIFIER-TYPES", note: "A picker, never free text. One code system on the terminology service." },
      { label: "Identifier value", kind: "field", span: 2, note: "Scanned or typed" },
      { label: "Find", kind: "action", span: 1 },
    ],
    [
      { label: "Patient", kind: "text", span: 2, from: "biodata.name, dob, gender", note: "Enough to confirm the right human" },
      { label: "Consent taken", kind: "text", span: 2, from: "consent.receipt", note: "Evidence, not decoration. Keep it." },
    ],
    [
      { label: "Contact channels", kind: "text", span: 4, from: "contacts.phones[].verified", note: "Masked. Only verified channels can receive a code later." },
    ],
  ]}
  caption={<>The identifier is always a pair. A field for the value with no field for the type is the commonest cause of a false "patient not found".</>}
/>

When this screen fails, a conflict is not an error to retry. It means the identifier matched
more than one person, so the screen should ask for a second identifier instead of showing a
red box. Design that path, because you will use it.

## Screen 2 · Choosing a cover

This is the screen where benefit coordination becomes real. A member with several covers
needs a person to choose between them, and that person needs enough to choose *with*.

<Wireframe
  title="Select cover for this visit"
  actor="Front desk"
  rows={[
    [
      { label: "Patient", kind: "text", span: 4, from: "unique_patient_id", note: "Carried from the previous screen, never re-resolved" },
    ],
    [
      { label: "Covers", kind: "list", span: 4, from: "covers[]", note: "Payer · member number · policy window · status. Already filtered to what is usable here." },
    ],
    [
      { label: "Scheme", kind: "field", span: 2, from: "covers[].schemes[]", note: "Only where a cover runs on several. Hidden otherwise." },
      { label: "Why this cover?", kind: "text", span: 1, note: "Optional, but it is what an audit asks" },
      { label: "Use this cover", kind: "action", span: 1 },
    ],
  ]}
  caption={<>Silently auto-selecting is the tempting shortcut here. Resist it. This is a financial decision, and nobody can audit a choice they could not see being made.</>}
/>

  Some covers run on more than one scheme at once. Render schemes as covers so the operator
  makes one selection instead of two, and so the screen looks the same whichever payer is
  behind it.

## Screen 3 · Consent

This is the screen you mostly do not build. Which proofs are legal at that moment is
resolved from the member, the facility, the payer and the benefit, so what you render is
whatever the rail returns, in a frame.

<Wireframe
  title="Confirm the member is present"
  actor="Member, at the desk"
  rows={[
    [
      { label: "Embedded consent", kind: "list", span: 4, from: "embed_url", note: "The permitted factors appear here. You do not choose them, and should not try to." },
    ],
    [
      { label: "Session expires", kind: "text", span: 2, from: "expires_at", note: "Show it. A front desk gets interrupted." },
      { label: "Start again", kind: "action", span: 2, note: "Reject the stale session first, then open a new one" },
    ],
  ]}
  caption={<>Roughly three adults in ten fail biometric matching, so the fallback is a main path rather than an edge case. Build the recovery screens with the same care as the happy one, because you will see them more often.</>}
/>

## Screen 4 · Billing

Your billers live in this screen. Two things belong on it that are routinely left off: the
lines that are *waiting*, and the cover being billed against.

<Wireframe
  title="Visit billing"
  actor="Biller"
  rows={[
    [
      { label: "Billing against", kind: "text", span: 3, from: "cover + visit_id", note: "Persistent. A biller who cannot see the cover will bill the wrong one." },
      { label: "Add item", kind: "action", span: 1 },
    ],
    [
      { label: "Lines", kind: "list", span: 4, from: "lines[].status", note: "active · held (awaiting approval) · retired. Held lines must be visible, or they get added twice." },
    ],
    [
      { label: "Documents", kind: "text", span: 2, from: "requiredDocuments", note: "Checklist from the item, ticked as attached" },
      { label: "Readiness", kind: "text", span: 1, from: "preflight.reasons[]", note: "Live, not at the end" },
      { label: "Close visit", kind: "action", span: 1, note: "Consent again, because discharge is its own event" },
    ],
  ]}
  caption={<>The readiness panel is worth more than anything else on this screen. Every check pre-flight will run is available while billing, so a claim should never surprise anyone at submission.</>}
/>

## The pauses, and how to design for them

Three moments in the journey are asynchronous. Each needs a state in your system, not a
spinner.

<Mermaid chart={`stateDiagram-v2
    [*] --> Billing
    Billing --> AwaitingApproval: item needs preauthorisation
    AwaitingApproval --> Billing: approved (per item, possibly partial)
    AwaitingApproval --> Returned: sent back for correction
    Returned --> AwaitingApproval: resubmit, same reference
    Billing --> Submitted: pre-flight clean, visit closed
    Submitted --> Returned2: returned for correction
    Returned2 --> Submitted: resubmit, same external_id
    Submitted --> Decided: approved / partially approved / rejected
    Decided --> Paid: remittance matched to invoice
    Paid --> [*]
`} />

Awaiting approval is a state you persist. Honour the polling interval, and let a callback
settle it if one arrives first. A user should be able to close the browser.

Returned is not a rejection but a request that is waiting on you. A returned item that
nobody resubmits is the second commonest reason money never arrives.

Awaiting payment is the gap between approved and paid. Model it explicitly, or your finance
team will build a spreadsheet to do it for you.

## What the desk should never be asked

A short list, all of it learned the expensive way:

<CostTable
  rows={[
    { where: "Identity", symptom: "\"Which ID type is this?\"", cause: "The type should come from the scan or the picker, not from the operator's judgement." },
    { where: "Identity", symptom: "\"Whose licence is on this line?\"", cause: "The clinician is an identity too, resolved by regulator plus registration number. Picked from a typed list, it is only a name with nothing behind it." },
    { where: "Consent", symptom: "\"Should we use fingerprint or a code?\"", cause: "The permitted factors are resolved for you. Offering a choice invents one that may not be legal." },
    { where: "Billing", symptom: "\"Is this covered?\"", cause: "Items were filtered to what is billable at eligibility. If it is on screen it is billable; if it is not, say why." },
    { where: "Submission", symptom: "\"Why was this rejected?\"", cause: "Every reason carries a fix_stage. Route them to the screen that fixes it instead of showing the message." },
  ]}
/>

**Next:** [Ride the rail](/rail/stops/Authenticate) · or read
[benefit coordination](/rail/why/Benefit-Coordination)
