02 · Verify cover
This is where an identified person becomes a claim someone will pay. The answer is not a yes or a no; it is a named cover, plus the items that cover will pay for at this facility today.
Everything below elaborates this sentence.
What this step is
01 · Identify told you who is in front of you. This step answers the money question, and it asks two separate questions to do it. Do not run them together.
| Call | What it does | What you keep |
|---|---|---|
Which cover pays? | A member holds covers, plural. Someone has to say which one today's visit runs against, and that choice has to be visible rather than guessed. Two calls: list the covers, then select one. | selection_ref |
What does it pay for? | Benefits, then sub-benefits, then service lines, each with an access point, a tariff, what authorisation it needs, and a balance. One call, repeated once per level. | nodes[].code |
Keeping them separate is what stops a member with nine covers waiting on nine benefit walks before anyone has asked which one they are using. The covers list asks each payer whether the member is current and carries no balances. Balances arrive with the tree, for the one cover that was selected.
The two are ordered by the handle rather than by good manners. The benefit tree is read against a
selection_ref, and only covers/select mints one. There is no way to read the second before the
first has answered.
The rule that makes it work is that a person is not covered. A person holds covers, and exactly one of them pays for a given selection.
Two things are worth knowing before the detail starts, because they decide how much of this you have to build yourself.
| Call | What it does | What you keep |
|---|---|---|
Your part is small | List the covers, let a person choose one, send the choice back, then follow next_action down the tree. Which fund pays, which tariff applies and whose balance is read are the rail's problem. | three calls |
Selecting is not spending | A selection records which cover, not which visit. It is idempotent, it consumes nothing, and it can be made again as often as you like. | selection_ref |
All three calls sit under /api/v2/patients/{unique_patient_id} and all three refuse to run
without the facility headers: a call without X-Facility-Id and X-Facility-Id-Type answers 400
FACILITY_CONTEXT_MISSING, and a token claim does not stand in for them.
The four moves
There are four of them, and the split between the first two is the part that matters most.
- 01List the coversEvery entitlement this member holds with each payer, with who contributes to it and how long it runs. It carries no balances, so it is light enough to render a chooser at once. covers.list
- 02Select one, visiblyAn operator chooses. Do not auto-select silently. This is a financial decision, and nobody can audit a choice they cannot see. Send the back and keep theCode.Code
- 03Walk down to a billable serviceBenefit, then sub-benefit, then service line, one call per level. Follow next_action.type: filter_benefits goes one level deeper, anything else ends the walk on that node.
- 04Read the balance on the nodeEvery node carries . Where a payer reports a figure it is there; where it does not,Codeis false and both numbers are null. Null means unknown and is not zero. Read it before quoting an amount, because a limit may be shared with a pool that a sibling has already drawn on.Code
A missing balance is not a gate. It means the payer did not report one, and the visit stop
checks again with whatever it knows then. A balance of zero is different: that node comes back
blocked with blocked_by: "BENEFIT_EXHAUSTED" and offers no next step.
If you only remember one thing: the cover is chosen once, explicitly, and everything priced afterwards hangs off that choice.
What it looks like at the desk
Three ways to look at the step: the screens, the sequence and the branch points.
One usable cover here. The multiple-cover screen comes further down, where it is explained.
payer_name, statusvalid_toselectableThe same screen when nothing pays.
covers[].statusstatus_reasonremediationAnd when a payer could not be asked at all.
unavailable[].messageWith a cover chosen, the step continues downward: benefits, then sub-benefits, then the billable unit.
searchaccess_pointnodes[].codenodes[].access_pointnext_action: filter_benefitsnodes[].codenodes[].access_pointnext_action: filter_benefitsnodes[].codeaccess_pointauthorization.needs_preauthtariff.applicablenodes[].codeaccess_pointauthorization.needs_preauthtariff.applicablecoverage.uses_leftcoverage.amount_leftcoverage.amount_limit, coverage.shares_parent_limitcoverage.known: falsestatus, blocked_byA member can hold many covers, and be a dependant on some of them
This is the part that surprises people, so it is worth stating plainly before any payload.
| Call | What it does | What you keep |
|---|---|---|
One payer, several schemes | A single payer can grant more than one scheme at once. SHA's UHC and SHIF fund one another, so they come back as one cover with two entries in schemes[], each with its own window and status. Every other SHA scheme stands as a cover of its own. | schemes[] |
Several payers | A member may hold covers from more than one payer: a national scheme, an employer scheme, a private insurer. Each comes back with its own payer_code, payer_name and source. | one row each |
Principal or dependant, per cover | On some covers the member is the contributor. On others they are somebody's dependant, and both can be true of the same person on the same day. | membership.member_type |
So the unit is usually the payer, and a payer's schemes are inside the cover rather than beside it.
What multiplies rows is contributors: a dependant can hold an entitlement under each parent's
policy, and each of those is a separate cover with a separate cover_id.
membership.member_type says which case you are in, as the payer states it. membership.principal_relationship is the member's
relationship to the contributor, and is SELF for the principal. On a dependant cover,
membership.principal_contributor names the person whose cover it is: name, id_number,
id_type, cr_number, employment_type and employer, with the identifiers masked. It is absent
when the source named no contributor, which is most covers. Store it with the encounter: every
audit conversation about a dependant's claim opens with "under whose cover?".
The identifiers in the response are masked. membership.member_identifier shows a short leading
fragment and hides at least its last four characters, and cover_id is a hash that carries none of
it.
On screen, that looks like this.
payer_name, schemes[].namevalid_toschemes[].namevalid_toschemes[].namevalid_topayer_name, statusvalid_toMembers a payer knows but was not asked
known_memberships[] is a separate array on purpose. It lists payers the master index files this
member under that no source answered for. Each carries a note saying to confirm with the payer
directly. They are not covers, they cannot be selected, and covers/select would not find one.
An empty covers[] with a non-empty known_memberships[] is the member who holds a valid card with
a payer this rail did not ask. Show it as "ring the payer", not as "no cover".
is_alive sits at the top of the response because it is a fact about the person, not about any one
cover. It is true, false, or null when the source that carries it could not be reached, and
null does not mean deceased.
There is no ranking, so do not build one
A member with several covers is not a puzzle with a correct answer. Pick any of them and you see the benefits that cover supports. The covers frequently overlap, since the same eye-health benefit can exist on two of them, so there is nothing to sort and no precedence to apply.
| Call | What it does | What you keep |
|---|---|---|
What choosing a cover does | Scopes the benefit tree to what that cover supports. A different cover means a different tree, not a better or worse one. | the tree you get |
What it does not do | It does not rank, exclude or consume anything. Selecting a cover is not spending it. | — |
Overlap is normal | Two covers can both carry the same benefit, with different limits and tariffs. Neither is the right one in the abstract. | compare when it matters |
So the chooser has one job, which is to show what each cover is and who contributes to it, then let a person decide. Group rows by payer if that helps the eye, but do not pre-select across payers and do not imply an order that the rail does not enforce.
A dependant can be covered under more than one principal at the same time. A child whose mother holds one fund and whose father holds another comes back with covers from both households. One of them is used for a given selection, and which one decides the tariff, the limits, the required documents and the combination rules, so the choice is not cosmetic.
Benefits are also filtered by member type. Principal, spouse and child see different things on the same scheme, so do not cache one member's benefit list and reuse it for a relative. The tree is read per selection, and each selection is one member's cover.
The household behind the covers
A cover usually belongs to a household rather than to a person. That is where the extra covers on the previous screen come from: a member appears on their own cover as principal, and on a relative's cover as a dependant. One person, one day, two entries.
The covers response does not list the household. Reading it is a separate call,
GET /api/v2/patients/{unique_patient_id}/metadata?consent_ref=…, and it returns the unmasked
identity, contacts and dependents[], so it is gated on a verified pii_access consent for that
patient. A dependant the index knows comes back with a unique_patient_id of their own, and their
covers are listed by calling covers with that id. A dependant the index does not yet hold is
returned unregistered rather than dropped.
Where a limit applies to a household, a relative's admission may have drawn on the same pool. The
node's coverage.shares_parent_limit says when the amount left belongs to a pool rather than to
the line.
Reaching a dependant through a principal is covered at 01 · Identify.
One cover, or several
Everything so far has assumed you pick a cover and move on. That is the common case and it is worth stating separately from the harder one, because the two behave differently and the difference decides who pays.
The single-cover case
One usable cover. Select it, and the rail writes a FHIR Coverage for that patient to the clinical
store, keyed on the cover_id, and returns that record's id as the selection_ref. Selecting the
same cover again updates the record and returns the same selection_ref, so a retry after a timeout
is safe.
Three things about that record are easy to misread.
| Call | What it does | What you keep |
|---|---|---|
It records which cover, not its benefits | The tree is read live from the payer on every call. What is stored is the cover: payer, schemes, member and window. Balances and tariffs are not frozen at the moment of choosing. | selection_ref |
It does not expire | There is no time limit on a selection_ref and it carries no facility or visit. It lapses only when the cover does: once the cover's window ends or the record is inactive, the tree answers 409 SELECTION_LAPSED and you select again. | selection_ref |
It cannot be probed | A selection_ref belonging to another patient answers exactly like one that does not exist, 404 SELECTION_NOT_FOUND. The two are deliberately indistinguishable. | — |
Selection re-asks the payers before it writes, because this is the point of commitment: a cover that
lapsed between listing and selecting must not be the cover a visit opens against. That is why
facility_patient_no has to be sent again if the list took one. A cover from a source keyed on that
number cannot be confirmed without it, and the selection is refused as a stale handle.
The multiple-cover case
A member may hold several usable covers at once: their own, plus one or more where they are a dependant on somebody else's. All of them may be valid, and only one pays for a given item.
Each cover is selected on its own and gets its own selection_ref. Which one an item draws on is
settled at the visit: a service line names the cover it draws on, and that cover has to be one the
visit's selection holds, otherwise the visit stop answers 409 COVER_NOT_IN_SELECTION. A cover
outside it needs its own selection and its own consent. See Visit for how lines
name a cover.
Do not model the cover as a single value on the visit. A provider who bills two contributors in one
encounter and cannot see which line went where will not be able to answer a query about either, so
carry the cover_id and the contributor on each line you raise.
Once a cover is chosen
Everything above answers which cover pays. The second half of the question, what that cover pays for, is a tree you walk, and it is long enough to have its own page.
Continues onWhat a cover pays forBenefits, sub-benefits and service lines, one level at a time. How the tree is shaped, why the same rule lives at different depths for different payers, what a service line declares, and where balances come from, with the screens, the sequence and the calls to go with them.
The calls
Three calls answer the cover question, and they run in order: list, select, then walk. Each is
v2, each needs the facility headers, and each answers with its own next_action naming the one
that follows.
| Call | What it does | What you keep |
|---|---|---|
GET · covers | Every cover the member holds, asked of each payer at once. Partial failures are reported inside a 200. | covers[].cover_id |
POST · covers/select | Commits to one cover by cover_id and records it. The payers are asked again first. | selection_ref |
GET · eligibility/{selection_ref}/benefits | One level of the benefit tree. Repeated once per level, following next_action. | nodes[].code |
Open 02 · Verify cover in the Rail API reference to read or run them, with a Try it console for when the sandbox is live.
List the covers
Covers per payer, with nested schemes, the members the index knows but nobody asked, and any source that did not answer.
covers[].cover_id/api/v2/patients/{unique_patient_id}/coversEvery cover this member holds, asked of each payer at once.
| Parameter | Type | Description |
|---|---|---|
X-Facility-Idrequired | header | The facility this call is made from. |
X-Facility-Id-Typerequired | header | How to read it: mfl, license-number, fr-code, registration-number or fid. |
unique_patient_idrequired | path | From the lookup. |
facility_patient_no | query | What this facility calls the patient. Needed only for a source keyed on it, such as a SMART card session. Send it again on covers/select. It does not filter payers. |
{
"unique_patient_id": "2029090000000046",
"is_alive": true,
"covers": [
{
"cover_id": "SHA:Qm3xT9vKp2LrYw8d",
"source": "SHA",
"payer_code": "2029",
"payer_name": "Social Health Authority",
"membership": {
"member_identifier": { "type": "CLIENT_REGISTRY_NO", "value": "CR56********" },
"member_type": "PRIMARY",
"principal_relationship": "SELF"
},
"schemes": [
{ "code": "1", "name": "UHC", "policy_number": "SHA-POL-0001", "valid_from": "2024-10-01", "valid_to": "2034-09-30", "status": "ACTIVE" },
{ "code": "2", "name": "SHIF", "policy_number": "SHA-POL-0002", "valid_from": "2024-10-01", "valid_to": "2026-09-10", "status": "ACTIVE" }
],
"valid_from": "2024-10-01",
"valid_to": "2034-09-30",
"status": "ACTIVE",
"selectable": true,
"as_of": "2026-08-14T09:12:40Z"
}
],
"known_memberships": [],
"unavailable": [
{ "source": "SLADE", "reason": "PAYER_UNREACHABLE", "message": "SLADE could not be reached. Covers held there are not in this list." }
],
"next_action": {
"type": "select_cover",
"method": "POST",
"endpoint": "/api/v2/patients/2029090000000046/covers/select"
}
}curl -X GET "$RAIL_BASE_URL/api/v2/patients/{unique_patient_id}/covers" \
-H "Authorization: Bearer $RAIL_TOKEN"The response is the struct itself, with no message wrapper around it. Read it this way.
| Call | What it does | What you keep |
|---|---|---|
cover_id is a handle, not a value | Derived, opaque and free of personal data. Send it back unchanged. Its prefix names the source, and nothing after that is yours to parse. | cover_id |
status is a code, not a word | ACTIVE, INACTIVE, EXPIRED, SUSPENDED or UNKNOWN, normalised across payers. Switch on the code; show status_message and remediation, which are the payer's own words. A cover takes the best status among its schemes. | status, selectable |
selectable is the rail's answer | Today it means active, and today falls inside the window. A window the rail cannot read does not veto an active cover. Selecting a cover where it is false answers 409. | selectable |
Dates are ISO dates | valid_from and valid_to span every scheme in the cover: the earliest start and the latest end. Each scheme keeps its own inside schemes[]. as_of is when the payer actually answered. | valid_to, as_of |
An empty relationship is meaningful | Where the member is the contributor, principal_relationship is SELF and principal_contributor is absent. Render that as the member, not as missing data. | membership |
The next_action is a select_cover with method and endpoint and, only when you sent
facility_patient_no, a requires entry naming it: "the same number sent to this list". It is the
same {type, method, endpoint, requires[], then} shape every stop uses, described in
Next Action.
Failures on this call:
| Status | error | Meaning |
|---|---|---|
| 400 | FACILITY_CONTEXT_MISSING | A facility header is absent or X-Facility-Id-Type is not one of the five values. path names the header. |
| 404 | MEMBER_NOT_FOUND | The unique_patient_id is unknown, or the person carries no identifier any payer accepts. |
| 502 | NO_PAYER_REACHABLE | Every payer failed. The covers are unknown rather than absent: retry before turning the patient away. detail carries the same entries as unavailable[]. |
| 504 | REGISTRY_TIMEOUT | The person index did not answer. Retry once, then offer a pending state. |
A payer failing on its own is not an error. It appears in unavailable[] with a source, a reason
(PAYER_UNREACHABLE unless the source said more) and a message, and the call is still a 200.
Select one
Commit to one cover by its handle. The payers are asked again before anything is written.
selection_ref/api/v2/patients/{unique_patient_id}/covers/selectCommit to one cover from the list, and record the choice.
| Parameter | Type | Description |
|---|---|---|
X-Facility-Idrequired | header | The facility this call is made from. |
X-Facility-Id-Typerequired | header | How to read it: mfl, license-number, fr-code, registration-number or fid. |
unique_patient_idrequired | path | From the lookup. The path wins over anything the body says. |
cover_idrequired | string | The handle from covers[].cover_id, sent back unchanged. |
facility_patient_no | string | The same value sent to the covers list, if one was. |
{
"cover_id": "SHA:Qm3xT9vKp2LrYw8d"
}{
"selection_ref": "cov-8f3a21c4",
"unique_patient_id": "2029090000000046",
"cover": { "cover_id": "SHA:Qm3xT9vKp2LrYw8d", "payer_code": "2029", "status": "ACTIVE", "selectable": true },
"selected_at": "2026-08-14T09:14:03Z",
"next_action": {
"type": "check_eligibility",
"method": "GET",
"endpoint": "/api/v2/patients/2029090000000046/eligibility/cov-8f3a21c4/benefits"
}
}curl -X POST "$RAIL_BASE_URL/api/v2/patients/{unique_patient_id}/covers/select" \
-H "Authorization: Bearer $RAIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "cover_id": "SHA:Qm3xT9vKp2LrYw8d" }'The body is cover_id and, optionally, facility_patient_no. Nothing else is read. There is
deliberately no cover payload: a client that sent back the scheme, the window and the status could
select a cover the payer never offered. A scheme_id, or a facility identifier, is ignored if sent,
and a facility identifier never belongs in a body: the facility travels in the headers. The cover
in the response (abbreviated above) is the cover as the payer described it at the moment of
commitment, which can differ from the list, and selected_at is when the record was written.
Failures on this call:
| Status | error | Meaning |
|---|---|---|
| 400 | COVER_ID_REQUIRED | No cover_id. path is cover_id. |
| 400 | Bad Request | The body was not JSON. This one is the status text, not an UPPER_SNAKE code. |
| 404 | COVER_NOT_FOUND | No payer offers that handle now. The commonest cause is a screen left open: fetch the list again. detail echoes the handle. |
| 404 | MEMBER_NOT_FOUND | As on the list. |
| 404 | PATIENT_RECORD_NOT_FOUND | The index knows the person and the clinical store has no record of them, so there is nothing to record a cover against. |
| 409 | COVER_NOT_SELECTABLE | The payer will not honour the cover today. detail is the whole cover with the payer's reason, message and remediation. |
| 409 | PATIENT_RECORD_CONFLICT | The clinical store holds more than one patient under this id. The duplicates have to be merged first. |
| 502 | NO_PAYER_REACHABLE | Every payer failed on the confirming ask. This is not "that cover is gone". |
| 502 | COVERAGE_WRITE_FAILED | The clinical store failed. Nothing was recorded, and selecting again is safe. |
| 503 | COVERAGE_WRITE_FAILED | This deployment has no clinical store wired, so a selection cannot be recorded. |
COVER_NOT_FOUND and COVER_NOT_SELECTABLE mean different things at a desk. The first says the
list has moved on and should be fetched again. The second says the cover is real and unusable
today, and the reason is in detail.
Walk the tree
One level of the tree per call. Omit parent_code for the top, then follow each node's next_action. Every node carries its own balance.
nodes[].codeThe response, the walk rules and the errors are on the next page, What a cover pays for.
The handle is the selection_ref, not an eligibility reference: v2 has none. Reading the tree takes
no consent proof.
The next_action values you will meet here
| Call | What it does | What you keep |
|---|---|---|
select_cover | On the covers list. POST the endpoint it names. requires lists facility_patient_no only if you sent one. | endpoint |
check_eligibility | On covers/select. GET the endpoint: it is the top of the tree for your selection_ref. | endpoint |
filter_benefits | On a node with more beneath it. GET the endpoint, which is the same route with parent_code set to the node's code. | endpoint |
obtain_consent, start_visit, add_service_line, resolve_open_visits | On a billable service line. Which one depends on whether the patient has a visit open. Each is a hand-off to a later stop. | type, endpoint, requires, then |
none | On a blocked node. No endpoint. Read blocked_by. | blocked_by |
Public-officer and teachers' covers are a different rule set
They look like another scheme in the list. They are closer to a parallel rule set, and four differences matter enough that treating them as a variation produces claims that cannot be paid.
| Call | What it does | What you keep |
|---|---|---|
Benefits downgrade, they do not vanish | Where a facility has no active contract of that type, public-officer benefits are hidden and the universal or standard equivalent is shown instead. An empty list is the wrong reading; a substituted list is the right one. | which set you are looking at |
Teachers' members are panel-restricted | Outside their panel, they see only their standard benefits. The same member gets different answers at two facilities, correctly. | — |
Tariffs come from a different matrix | Public-officer tariffs are different figures, published separately. If the member is a public-officer member, the standard tariffs do not apply, and reading the standard matrix misprices every one of those claims. | the public-officer tariff |
Combination rules differ | The same procedure codes, different rules about what may be billed together. | — |
What you keep
| Call | What it does | What you keep |
|---|---|---|
The selected cover | Which payer, which scheme and which contributor. Tariff, limits, documents and combination rules all follow from it. | cover_id, membership |
The principal contributor | On a dependant cover, whose cover is being used. The first question any audit asks. | membership.principal_contributor |
The service line code | The unit everything later prices against, with its access point and authorization needs. | nodes[].code, access_point |
The selection reference | The handle the benefit tree is read against, returned by covers/select. It does not expire and carries no facility or visit; it lapses only when the cover does (409 SELECTION_LAPSED), and then you select again. There is no separate eligibility reference in v2. | selection_ref |
What breaks here
| Where it surfaces | What the desk sees | What actually went wrong |
|---|---|---|
| Cover selection | "There are several identical covers, which one do I pick?" | The contributor is not rendered. A dependant can hold a cover under each parent, and membership.member_identifier repeats across them; without principal_contributor they look like duplicates and the operator picks the first. |
| Cover selection | "We picked a cover and the claim was rejected as not the member's" | A dependant cover was treated as the member's own. The claim has to carry the principal contributor for that cover, not the patient. |
| Cover selection | "The system says not covered, so we sent them away" | status_reason and remediation were dropped instead of shown, or unavailable[] was ignored. Identity resolved; a payer that was not asked is not a payer with no cover. |
| Cover selection | "The list failed, so we told them to come back tomorrow" | 502 NO_PAYER_REACHABLE means every payer failed, so the covers are unknown. Retry now, and offer a pending state, before turning anyone away. |
| Cover selection | "Select answered COVER_NOT_FOUND for a cover we just listed" | The list was old, or a source keyed on facility_patient_no was asked without it on select. Fetch the list again and send the same facility_patient_no to both calls. |
| Bill | "The line was refused and the code looks correct" | Access point mismatch. An inpatient-only service line was selected during an outpatient visit, and nothing failed at selection time. |
| Bill | "It said covered, but the amount came back zero" | A null amount_left rendered as zero. Null is unknown, not zero, and a shared pool may already have been drawn on by a sibling line. |
| Preauth | "Nobody told us this needed approving" | authorization.needs_preauth was on the service line and was not read. The flag is available at selection, hours before the patient is on a table. |
| Claim query | "We cannot say which cover paid for which item" | The cover was modelled as one value on the visit. A service line names the cover it draws on, so keep the cover_id on each line. |
| Bill | "The price changed when we switched cover" | Correct. The payer resolves a tariff per fund and per facility, so the amount is a function of the service line, the facility and the cover being billed. |
| Tree | "It shows zero left on emergency care" | Either the payer reports no pool, in which case coverage.known is false and both numbers are null, or the render defaulted null to 0.00. Unknown tells a clinician the opposite of an empty pool. |
| Cover selection | "We used the mother's cover and it was refused for the child" | Benefits are filtered by member type, and a dependant may be covered under two principals. Select the cover whose contributor matches, and do not reuse one member's tree for a relative. |
| Preauth or Bill | "The document list on the service line looked short" | Requirements can be declared higher up the tree. Reading the leaf alone misses everything set above it; resolve the chain. |
| Next visit | "The selection_ref stopped working" | It does not expire, so the cover ended or the record went inactive (409 SELECTION_LAPSED), or the ref belongs to another patient (404 SELECTION_NOT_FOUND). List the covers and select again. |
One pattern runs through most of that list. The response said so and the screen did not. The contributor, the access point, the balance, the preauth flag and the payer's own reason are all present at this stop, and every one of them, dropped here, becomes somebody else's problem later.
Errors
These are the codes this stop's three calls emit, taken from the service. Where a cause is not a
domain failure, such as an unreadable body, error is the status text instead of a code.
error | Status | Call |
|---|---|---|
FACILITY_CONTEXT_MISSING | 400 | all three |
COVER_ID_REQUIRED | 400 | select |
FACILITY_NOT_IN_BENEFITS | 400 | tree |
MEMBER_NOT_FOUND | 404 | covers, select |
COVER_NOT_FOUND | 404 | select |
PATIENT_RECORD_NOT_FOUND | 404 | select |
SELECTION_NOT_FOUND | 404 | tree |
BENEFIT_NOT_FOUND | 404 | tree |
COVER_NOT_SELECTABLE | 409 | select |
PATIENT_RECORD_CONFLICT | 409 | select |
SELECTION_LAPSED | 409 | tree |
SELECTION_UNUSABLE | 409 | tree |
NO_PAYER_REACHABLE | 502 | covers, select |
COVERAGE_WRITE_FAILED | 502, 503 | select |
BENEFITS_UNAVAILABLE | 502, 503 | tree |
REGISTRY_TIMEOUT | 504 | covers, select |
| Code | HTTP | What it means | What to do | fix_stage |
|---|---|---|---|---|
NO_PAYER_REACHABLE | 502 | Every payer failed at once when listing covers, so the covers are unknown rather than absent. detail lists each source and why it failed. One payer failing alone is not an error: it appears in unavailable[] of a 200. | Retry before turning the patient away. Do not show it as "no cover". | cover |
COVER_ID_REQUIRED | 400 | The selection named no cover. | Send the cover_id exactly as the covers list returned it. | selection |
COVER_NOT_FOUND | 404 | No payer answered for that cover handle on this attempt. Most often the screen was left open and the member's cover changed since. | Fetch the covers list again and select from the current one. | selection |
COVER_NOT_SELECTABLE | 409 | The payer still knows the cover but will not honour it today — inactive, expired or outside its window. detail carries the payer's own status, reason and remediation. | Select another cover, or follow the remediation in detail. Read the payer's reason before telling the patient anything. | selection |
PATIENT_RECORD_CONFLICT | 409 | The unique patient id resolves to more than one patient in the clinical store, so a cover recorded now could attach to the wrong record. | The duplicates have to be merged first. Do not retry. | selection |
PATIENT_RECORD_NOT_FOUND | 404 | The person is known to the registry but has no clinical record yet, so there is nothing to record a cover against. | Register the patient in the clinical store, then select the cover again. | selection |
COVERAGE_WRITE_FAILED | 502 | The clinical store refused or failed the write, so the selection did not happen. It is 503 where selection is not available on the deployment. | Retry. Selecting the same cover twice is safe. | selection |
SELECTION_NOT_FOUND | 404 | No selection is known by that selection_ref. Another patient's selection is answered the same way. It is 400 when selection_ref is missing. | Select a cover first and use the selection_ref it returns. | eligibility |
SELECTION_LAPSED | 409 | The selection's cover has since stopped being usable — inactive or outside its window. | Fetch the covers list again and select a cover before walking benefits. | eligibility |
SELECTION_UNUSABLE | 409 | The selection cannot back this call. At benefits it names no payer. At consent it cannot decide which payer's rules apply (fix_stage consent; 503 if the deployment cannot resolve one). At visit open it is unknown, inactive or another patient's (fix_stage visit; 502 if it could not be read). | Select the cover again and use the new selection_ref. Retrying the same reference will not help. | eligibility |
BENEFIT_NOT_FOUND | 404 | parent_code names nothing in this member's benefit tree. | Walk down from the top of the tree again, using the codes the previous level returned. | eligibility |
BENEFITS_UNAVAILABLE | 502 | No benefits system could answer for this cover, so what it pays for is unknown rather than empty. It is 503 where benefits are not available on the deployment. | Retry before turning the patient away. An empty list would read as "nothing is covered"; this is not that. | eligibility |
FACILITY_NOT_IN_BENEFITS | 400 | The benefits system does not recognise the facility the call was made from. | Check X-Facility-Id and X-Facility-Id-Type. Retrying will not help. | eligibility |
What carries forward
| Call | What it does | What you keep |
|---|---|---|
Selection reference + cover | Taken by Consent, Visit, Preauth, Bill and the claim. The single most consequential choice on this page. | Store on the encounter |
Service line code | Priced at Bill, approved at Preauth where required. | With its access point |
The node's next_action | Names the hand-off: obtain_consent, start_visit or add_service_line. Follow it rather than composing the path. | Do not re-derive |
Next: 03 · Consent. With a cover chosen, the member has to confirm they are present and willing.
A complete brief for this step — the calls to make, what to persist, and every failure path to handle. Nothing on this page is assumed.

