Eligibility check Operation¶
| Canonical | ../OperationDefinition/eligibility-check |
|---|---|
| Status | draft · 1.26.0 |
| Invocation | POST [base]/$eligibility-check |
| Level | system |
| Affects state | yes -- not safe to repeat |
| Source | FSH · JSON |
Ask a patient's province, through one clinic's payer account, whether that patient may be billed, and report how that run ended.
POST [base]/$eligibility-check
The caller names a patient, never a card. The health card the payer is asked about is read from the patient's own record. No health number travels in the request, and a caller cannot ask about a card the record does not hold. The caller's identity is likewise taken from the session rather than from the body, so a disclosure cannot be attributed to somebody else.
affectsState is true. A run spends the clinic's payer credential, writes an audit record,
and on success replaces the settled answer stored on the patient's health card Identifier in the
health-card-eligibility extension. It is a
POST, it is not cacheable, and it is not safe to repeat blindly -- see the retry rule below.
There is no freshness rule and no cache. Every request asks the payer, unless a check for the same patient at the same clinic is already queued or running, in which case the second press collapses into the first rather than opening a second call. There is no parameter that forces, bypasses or re-asks: an unrecognised part in the body changes nothing and is ignored.
Do not feature-detect this operation from a running server's /metadata. Two different
documents are easy to conflate here. This guide's server statement has kind = requirements: it
says what a conformant endpoint must offer, and it does declare this operation. What a running
instance publishes at /metadata is its own kind = instance statement, and the gateway serves
this operation itself without advertising it there -- so a live capability lookup answers "absent"
while the operation works. Neither document is wrong; they answer different questions.
So do not branch on /metadata. A practice that has the feature switched off is refused with an
OperationOutcome at 403, and that refusal is
the signal to act on.
What a reply can say, and what never reaches one¶
A reply is published only for a request that settled, was taken on, or collapsed into one already
running. On a 200 the status parameter carries exactly one of four words:
status |
what it means |
|---|---|
answered |
The payer settled the question while the caller waited. The answer part is present. |
pending |
The request is queued. The answer lands on the patient's record when it arrives. Never means "not eligible". |
alreadyInFlight |
A check for this patient at this clinic was already running, so no second one was taken on. |
noAnswer |
The run reached the end and the payer settled nothing. Terminal: nothing further is queued and the record is unchanged. |
Every other ending is an OperationOutcome rather than a reply, so the words naming those endings
are never readable from a status parameter:
| HTTP | issue.code |
the endings that read this way |
|---|---|---|
403 |
forbidden |
The caller may not ask, or the practice has eligibility checking switched off. |
400 |
invalid |
The body does not describe a check, or the platform refused the request before the payer was asked. |
422 |
business-rule |
The practice's own data cannot produce a question: an unsupported province, no usable card on record, a card issued by another province, or a patient the store does not hold. |
503 |
transient |
A fault stopped the question being put, or stopped its outcome being read. |
Match on the HTTP status and issue.code, never on the sentence beside them. The sentence in
issue.diagnostics is developer-facing, is not a translation of anything, and is not stable.
Retrying is not free¶
A 503 covers two different situations that the wire does not distinguish. In one, the payer was
never asked and the identical request may be sent again. In the other -- a reply that could not be
read, or a wait that ran out after the request had gone out -- the check may already have run,
because the platform does not stop when the caller's wait does. Re-read the patient's card before
asking again rather than retrying on a timer. A 400, 403 or 422 is not worth repeating
unchanged.
Reading the answer¶
The answer part is present exactly when status is answered, and its three children are
named and typed exactly as the same three children are stored on the card in
health-card-eligibility, so one parser serves
both this reply and a Patient read.
Read it whole or not at all: eligible and answeredAt are both required for the answer to mean
anything. answeredAt is when the payer gave THIS answer, not when the patient was last checked --
a re-check that agrees leaves the moment alone, so no freshness may be inferred from it. An absent
coverageExpiry means the payer delivered none, not that coverage is unlimited.
For any status other than answered, this run settled nothing, and the patient's own record remains
the place to read the current answer.
Asking for a check requires both the eligibility-check execute right and the eligibility write
right. A caller holding one but not the other is refused at 403 before anything in the body is
judged, so that outcome describes nothing that was sent.
Request parameters¶
| Parameter | Card. | Type | Documentation |
|---|---|---|---|
patient |
1..1 | Reference→ Patient Profile |
The patient to ask about, as a literal relative reference of the form Patient/{id} and nothing else. It must be a Reference: a plain string carrying the same text is not read. The shape is judged exactly -- a bare id, an absolute URL, a versioned reference (Patient/1/_history/2), a reference to another type, and a trailing empty id are each refused, because each carries either no slash or more than one. |
clinicId |
1..1 | string |
The clinic whose payer account asks the question, and whose credential the run spends. A string that must parse as a UUID; a value that does not parse is refused. The clinic is verified against the caller's own tenant before anything is queued. |
serviceDate |
0..1 | date |
The day the check is asked for. CONTEXT ONLY: it gates nothing, opens no window, and does not change the answer -- it is carried alongside the queued work and echoed back. It must name a whole day: 2026-08 and 2026 are refused rather than rounded, and a string carrying the same text is not read. Omitting it is normal and is not read as today. |
Response parameters¶
| Parameter | Card. | Type | Documentation |
|---|---|---|---|
status |
1..1 | string |
How the run ended, published as a valueString. On a reply it is one of exactly four words: answered, pending, alreadyInFlight, noAnswer. The endings named featureDisabled, provinceNotSupported, noCardOnRecord, cardOutOfProvince, patientNotFound, patientUnreadable, credentialMissing and vendorUnavailable are published as OperationOutcomes and can never be read here. Tolerate a word this guide does not list: the platform and this guide ship on different cadences. |
problem |
0..1 | string |
Why the run ended the way it did, when it ran and produced a typed problem, published as a valueString. Finer-grained than status and never a substitute for it. The vocabulary is credentialMissing, vendorUnavailable, vendorRejected, vendorAnswerUnknown, vendorPending, rateLimited, pollTimeout, alreadyInFlight, patientNotFound, patientUnreadable, noCardOnRecord, cardOutOfProvince, cardChangedDuringCheck, provinceNotSupported, featureDisabled, answerNotStored. Not every word can accompany every status, and a word this guide does not list must not break a reader. |
message |
1..1 | string |
Display words a caller may show. Never a card value and never the payer's own text. Display it; do not branch on it. |
patient |
1..1 | Reference→ Patient Profile |
The patient the request named, echoed back exactly as sent, so a reply read out of band still says who it is about. |
serviceDate |
0..1 | date |
The day the request named, echoed back when it named one. Absent when the request named none; it is never filled in. |
answer |
0..1 | (parts) | The settled answer this run produced. Present EXACTLY when status is answered, and absent for every other status -- a pending reply carrying an answer is malformed. Its three children are named and typed exactly as the same three are stored on the card in the health-card-eligibility extension. |
↳ eligible |
1..1 | boolean |
The payer's settled answer for the card that was asked about. Required: an answer part without it carries no answer. |
↳ answeredAt |
1..1 | instant |
When the payer gave THIS answer -- NOT when the patient was last checked. The stored answer is rewritten only when it changes, so a re-check that agrees leaves this moment untouched and no freshness may be inferred from it. Required. |
↳ coverageExpiry |
0..1 | date |
The coverage expiry the payer delivered alongside the answer. Absent means the payer delivered none, NOT that coverage is unlimited. |