08 · Submit
Pre-flight should hold no surprises. Run it as often as you like: it spends nothing and sends nothing.
Everything below elaborates this sentence.
Why this stop exists
Two things happen here, in order, and they get conflated: the visit closes, then the claim is sent. Closing needs consent from the patient. Sending needs the claim to be complete.
Close the visit with consent
A visit closes the way it opened, with consent. Discharge is authorised in its own right rather than treated as a formality.
Three checks run before the closing consent is spent, so a refusal never burns it: a passing claim pre-flight has been recorded, the patient's copay is settled, and the claim is complete.
Ask for the closing authentication. It is the same call as visit open, with purpose set to
visit_end. See 03 · Consent.
authentication_idClosing is a purpose of its own. A proof taken to open the visit can never satisfy it, however strong and however recent it is, so there is nothing to reuse here and no shortcut worth hunting for. The reason for that rule, and what it prevents, is at one proof, spent more than once.
The authentication id it returns is the consent_ref you send on close. Where the consent was an
OTP, its verification reference travels to the payer as the discharge authorisation.
Pre-flight: what is checked
Check the claim before closing. No body. Send the facility headers. Add claim_ref as a query
parameter to check one claim only.
readyRun it before you ask the patient for the closing consent, not after. Close refuses on a failing claim before the consent is spent, but the patient has already been asked by then, and pre-flight lets the desk fix what it names while they are still at the counter.
The call records the run. Close is refused with 422 PREFLIGHT_REQUIRED unless the last recorded run
for the visit passed, so run it again after fixing what it named. Three things are enforced:
| Call | What it does | What you keep |
|---|---|---|
Diagnosis present | A primary diagnosis is linked to each billed service line. Recorded at 05 · Treat rather than assembled here | DIAGNOSIS_REQUIRED |
Doctor on the visit | At least one doctor is attached to the visit, since every claim the payer accepts carries a clinician | DOCTOR_REQUIRED |
No empty claim | Every Slade claim has at least one bill line. A claim opened by an invoice number that nothing was billed to would be refused when sent. The reason carries the claim's `claim_ref` and `invoice_number` | CLAIM_EMPTY |
With claim_ref, only that claim is checked and the run is not recorded, so it never satisfies
close. Use it to show one invoice's readiness on its own screen; the unscoped call is the one close
reads. A claim_ref that names no claim on the visit is 409 CLAIM_NOT_ON_VISIT.
ready: false is still a 200, with the outstanding items in reasons[]. A visit with no used cover
has nothing to check and comes back ready: true. service_line_ref is empty on a claim
pre-flight and request_type is absent.
Code
Every reason carries fix_stage, fix_endpoint and fix_label. Use them to send the operator
straight to the screen that can resolve the problem, rather than showing a list and leaving them to
hunt. Every reason returned today is a blocker. The other fields a reason can carry (element,
document_type, role, missing_element) belong to the preauth pre-flight and are not set here.
Errors: 404 VISIT_NOT_FOUND, 409 VISIT_NOT_OPEN once the visit is closed, and 409 CLAIM_NOT_ON_VISIT for a claim_ref the visit does not hold.
Close and submit
Spend the closing consent, end the encounter and dispatch every claim on the visit.
claims[].claim_refCode
consent_ref and discharge_reason (RECOVERED, REFERRED, DECEASED, ABSCONDED or OTHER)
are required. discharged_on is an RFC 3339 time and defaults to now; notes is relayed to the
payer. idempotency_key is accepted but not read today, so do not rely on it to make a retry safe.
Close refuses, before spending the consent, on a missing passing pre-flight (422 PREFLIGHT_REQUIRED),
an unsettled copay (402 COPAY_OUTSTANDING) and an incomplete claim (422 CLAIM_NOT_READY, with the
reasons in detail.reasons). The answer:
Code
There is one endpoint, whichever payer the claim is destined for, and there is no per-claim submit:
close sends every claim on the visit, each to its own payer. A SHA cover is one comprehensive claim,
under the invoice_number sent when the visit opened (why). A Slade
cover has one claim per invoice number the bill items named, so claims can hold several entries for
the same cover. A purely clinical visit closes with claims empty. A payer
that refuses a cover's claim does not fail the close: that cover comes back needs_more with the
payer's verdict in refusal, and the others proceed. A payer that cannot be reached stops the close
with 502 PAYER_UNREACHABLE, and it can be retried.
Nothing has to be registered before you close. Later status changes reach you at the tenant endpoint
you registered with entity_type claim and the operation action status_changed
(POST /api/v1/tenants/{tenant_id}/endpoints). The body is a status event: subject_guid,
provider_claim_no, from_state, to_state, facility_fr_code, entity_type, timestamp, and
notes where there is one. There is no X-Callback-Url header and no event called claim.decision.
A claim that missed the payer
Re-dispatch the covers whose claim is stuck in DRAFT or came back needs_more.
claims[].resubmittedFix what the payer asked for first, for instance by uploading the missing document at
07 · Bill. The body is the close request, with a
fresh consent_ref. It works on a closed visit. A cover already submitted or decided is reported
untouched, and resubmitted: true marks the ones that were re-sent. The result state is
resubmitted; the visit itself stays closed. Resubmit does not require a pre-flight.
The state the rail reports
Each claim in the close answer carries one coarse state, the same set for every payer, and the
payer's own workflow state alongside it as payer_state.
| Call | What it does | What you keep |
|---|---|---|
submitted | Dispatched, and the payer is adjudicating. No verdict yet | claim_ref |
needs_more | Never reached the payer (DRAFT, or a failed push) or was sent back for clarification or documents. This is the state resubmit acts on | refusal |
decided | The payer reached a verdict: approved or rejected. A partial is an approval short of the requested amount | payer_state |
closed | Terminal: paid, closed or time-barred | — |
Anything else a payer reports reads as submitted. Read the claim back at any time with
GET /api/v2/visits/{visit_ref}/claims, which returns each claim's claim_ref, cover,
invoice_number, state, payer_state, total_amount, documents, doctors and a reconciliation of what the payer pays and what the
patient bears.
Six kinds of claim, and eleven states
Two counts are worth correcting, because a state machine built to the wrong one leaves cases with no branch.
There are six claim types, not four. Ambulance is live and behaves differently from everything else, and oncology is live for named facilities rather than still in development. Treat the type as data on the claim and switch on it, rather than assuming one shape.
The authorisation moves through eleven states, not four. These are the states of the authorisation that opened and closed the visit, a different enum from the twelve a preauthorisation moves through, and the two are easy to confuse. Anything modelled on "pending, approved, rejected, done" will meet states it quietly drops. Read the enum from the reference, and when a value arrives that you do not know, hold the claim instead of discarding it.
Missing documents have their own terminal deadline. A claim returned for documents can be resubmitted, but only inside a window, after which it is rejected and cannot be resubmitted at all. It is the one deadline where nothing can be recovered afterwards, so it belongs on a worklist of its own rather than in a general queue.
After it lands
Submissions are reviewed in batches with rules doing part of the work, but anything with an issue goes to a person. Expect decision latency to reflect that.
A status change reaches your claim endpoint as an event that says where the claim moved from and
to, not what was decided line by line:
Code
Read the amounts and the verdict from GET /api/v2/visits/{visit_ref}/claims when it arrives.
A claim can also come back returned, meaning sent back with feedback for correction rather
than rejected. It surfaces as needs_more, keeps its claim_ref, and is re-sent with the resubmit
call above. A resubmitted bill may not exceed the original.
Documents are checked in two places
The required list is per procedure, and it splits across two moments: some documents belong to the preauthorisation and some to the claim. An inpatient submission needs a discharge summary and a final bill; maternity needs only the discharge summary. Collecting the right documents at the wrong stage still fails here.
Two practical rules. Uploads must be idempotent, so a retry does not create a second attachment. PDFs must be sent with the correct content type, because the failure you get when they are not is confusing rather than explicit.
What breaks here
| Where it surfaces | What the desk sees | What actually went wrong |
|---|---|---|
| Submit | "It will not let us submit and we cannot see why" | Pre-flight returned blockers. Every one carries a fix_stage, so surface them as actions rather than as a wall of text. |
| Submit | "PREFLIGHT_REQUIRED" | Close ran before a passing claim pre-flight was recorded, or the last run failed. Run POST close/preflight, fix the reasons, and close again. |
| Submit | "The visit will not close" | The closing consent was not a visit_end authentication of its own (CONSENT_NOT_REDEEMABLE), the copay is outstanding (COPAY_OUTSTANDING), or the claim is incomplete (CLAIM_NOT_READY). |
| After | "We closed again and got VISIT_NOT_OPEN" | The visit is already closed. A claim stuck in DRAFT or returned needs_more is re-sent with resubmit, not by closing a second time. |
Errors
| HTTP | error | When |
|---|---|---|
| 400 | validation sentence | consent_ref missing, or discharge_reason not one of the five. |
| 400 | CONSENT_NOT_FOUND | consent_ref is not an authentication id. |
| 402 | COPAY_OUTSTANDING | The patient's copay is not settled. The message names the amount. |
| 404 | VISIT_NOT_FOUND | No visit under that reference. |
| 404 | CONSENT_NOT_FOUND | No consent exists under that reference. |
| 409 | VISIT_NOT_OPEN | The visit is already closed. |
| 409 | CONSENT_NOT_REDEEMABLE | Expired, consumed, wrong purpose or wrong subject; the policy's code is in detail.refusal. |
| 422 | PREFLIGHT_REQUIRED | No passing claim pre-flight is recorded. detail.run_preflight names the call. |
| 422 | CLAIM_NOT_READY | The claim is incomplete. The outstanding items are in detail.reasons. |
| 502 | PAYER_UNREACHABLE | A payer did not answer. Nothing was set there; retry the close. |
Resubmit shares the consent, validation and 404 VISIT_NOT_FOUND rows, and none of the pre-flight,
copay or open-visit gates.
| Where it surfaces | What the desk sees | What actually went wrong |
|---|---|---|
| Submit | "The claim sat in our queue and the window closed" | Someone assumed the time bar was generous. It is disputed and may be short, so show the deadline on the claim and alert before it rather than after. |
| Submit | "A returned claim can no longer be resubmitted" | The missing-documents window elapsed. That deadline is terminal, and nothing recovers the claim afterwards. |
| Anywhere | "Our state machine has no branch for this status" | Eleven authorisation states and six claim types, and preauthorisations have their own twelve. Handle an unknown value by holding the claim rather than dropping it. |
Next
Next: 09 · Reconcile

