The next_action contract
The client reads its next move out of the response instead of guessing it.
Everything below elaborates this sentence.
What it is
Most responses on the identify-to-visit path carry a next_action object. Its type names the
move; method and endpoint say how to make it, requires lists inputs the response does not
itself carry, and then is the call that follows when this one is a step on the way.
Code
Take the endpoint from the response. Do not construct it. What comes back already has the
identifiers interpolated, so you get /api/v2/patients/SIL-UPI-0091774/covers rather than a
template you have to fill.
Take the method from the response too. The endpoints are not all GET: /api/v2/visits and
the service-line and bill-line routes are POST-only, so a client that guesses GET is answered
405 with nothing to explain it. method is empty only when there is no endpoint to call.
requires[] names the request-body fields the call needs that this response cannot supply,
each with the field and, where another call produces it, that call's method and
endpoint. Start-visit, for example, needs a redeemable consent that no earlier stop returns.
Not every response has one. It is present on identity lookup, the covers list, cover selection, each node of the benefit tree, visit open, a service line that needs a stronger consent, and the consent decision. It is absent from authentications, practitioners, bill lines, pre-flight, preauth, close and resubmit, so a client must not assume the field.
Why it exists
There are two reasons, and the second is the one that bites in production.
The first is that it keeps payer branching out of your client. What a benefit leads to depends
on what the patient already has open and on what the payer's benefit tree says, and those
differences reach you as different next_action values, so you do not keep a code path per
payer.
The second is that it handles legitimate skips. Hard-coding the sequence works until a step
is correctly skipped: a visit already open, so add_service_line replaces the consent
ceremony, or a line whose cover needs a stronger consent than the visit holds. A client that
walks a fixed list either repeats work or stalls. Reading next_action avoids both, and it
takes no conditionals on your side.
The full enum
These are the values the rail emits today.
| Call | What it does | What you keep |
|---|---|---|
select_cover | Identity or the covers list was returned, so choose a cover and call cover selection | method, endpoint, requires |
check_eligibility | A cover was selected, so walk its benefits | method, endpoint |
filter_benefits | A benefit group has more beneath it, so call the same endpoint filtered on the node's code | method, endpoint |
obtain_consent | A billable line and no open visit, so run the consent ceremony; start_visit follows in then | endpoint, then |
start_visit | A billable line is ready to open a visit against | method, endpoint, requires |
add_service_line | A visit is open and needs a service line before charges can land | method, endpoint, requires |
resolve_open_visits | The patient has more than one visit open, and someone must close the finished ones; no endpoint | — |
add_bill_line | A service line is authorized, so charges may land | method, endpoint |
step_up_consent | The line's cover needs a stronger consent factor, so render the consent surface and resubmit the line | method, endpoint |
render_consent | Open the consent surface the endpoint names, in a frame or by navigating to it | endpoint |
none | Nothing further from this node, for example a blocked benefit, whose status and blocked_by say why | — |
The tree is walked with filter_benefits until a billable line ends it with obtain_consent,
start_visit or add_service_line, depending on what the patient already has open.
Planned but not emitted today: select_benefit, assign_location, authenticate, submit_challenge,
submit_preauth, manual_confirmation, await_decision, reserve, fix and submit. Do not build a
client that waits for them. Where a step has no next_action, such as bill lines, pre-flight,
preauth and close, the stop page says what to call next.
How to build against it
- 01Model next_action.type as an enum in your own code, with an explicit unknown case. New values can be added; an unknown value should surface as 'unhandled step' rather than crashing or silently stopping.
- 02Write one dispatcher that switches on the type. Every screen in your flow is reached from it. Resist adding a second path 'just for this case'.
- 03Always take method and endpoint from the response. If your code contains a string-concatenated rail URL, it will break the next time a payer changes something.
- 04Treat none as terminal for the current step, not for the flow. It means nothing more is needed from this node, and your own workflow decides what happens next.
- 05Do not assume a response has next_action. Where it is absent, follow the stop page.
- 06Log the type on every transition. When a flow stalls in production, the sequence of next_action values tells you where it stopped.
The common mix-up
none and resolve_open_visits both carry no endpoint, and both can look like a dead end
from a UI. They are not the same.
none means the node cannot be followed and, on the benefit tree, comes with a blocked
status saying why. resolve_open_visits means the patient holds more than one open visit,
and no call fixes that: someone has to close the visits that are finished before a line can
be added.
Errors carry their own routing
Failures use the same discipline. A coded error carries error and message, and usually
path and fix_stage; see the Error dictionary for the full shape.
Pre-flight returns every reason at once in reasons[], so a correction takes one round rather
than a queue of them.
fix_stage tells you where in the sequence the correction belongs. Combined with the
reasons[] array at pre-flight, you can route an operator straight to the screen that can
resolve the problem instead of asking them to hunt.
Next: see it drive a real sequence on the golden path.

