There is no dedicated errors endpoint. This page documents the error model the rails actually exhibit — the typed refusals returned by real routes when an integration is unconfigured, when a single-use credential is replayed, when a signature fails, or when the settlement gate halts on doubt. Every shape below is cited against a real path. None is invented.
The governing rule is fail closed, and fail loud. An unconfigured integration returns a typed 501 or 503 with zero network calls — never a silent all-clear, never a fabricated success. Production never simulates a happy path it cannot really perform.
One table for the whole surface. Each status has a single, consistent meaning across the rails; the codes below are the ones the real routes return.
| Status | Meaning |
|---|---|
400 | Invalid or expired single-use input — most notably a replayed or expired OAuth state on the EMR callback. The one-time value is already spent; the exchange refuses. |
401 | Authentication failure or an unverified webhook signature. No valid session, or a signature that does not match the raw body under the shared secret. |
403 | Authenticated but not authorized — the action was denied for this session’s scope or role. |
501 | Integration not configured — a typed refusal (e.g. CDS_NOT_CONFIGURED) with zero network calls. An unconfigured knowledge base returns 501, never a silent all-clear. |
503fail-closed | Fail-closed: a required rail or dependency is unconfigured or halted. The route refuses with zero network calls and fabricates nothing. |
The 501 and 503 rows share one property that is the brand’s differentiator: the refusal is fail-safe, not silent. A gap in configuration surfaces as a loud typed error, so an operator sees an unconfigured integration rather than mistaking a missing check for a clean result.
When an integration has no credentials or no backing store, the route returns a typed 501 refusal before it touches the network. It never degrades into a silent success and never simulates a pull.
An unconfigured clinical knowledge base returns a typed CDS_NOT_CONFIGURED refusal — fail-safe, not silent. The alternative (a silent all-clear) would be a clinical hazard: an empty rule set must never read as “no alerts.”
With no managed FHIR store configured, the sync returns a typed refusal rather than pretending to write. Nothing is queued and no resource id is synthesized.
Without a configured SMART client assertion the pull refuses. Production never simulates a successful pull — an unconfigured EMR link is a typed error, not a stubbed record set.
# An unconfigured KB returns a typed 501 — never a silent all-clear
curl -i -X POST "https://your-sandbox-origin.example/api/cds/evaluate" \
-H "Content-Type: application/json" \
-H "Cookie: $SHTEG_SESSION" \
-d '{"encounterId": "enc_9f2a41"}'const res = await fetch("/api/fhir/gcp-sync", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ resourceType: "Encounter", id: "enc_9f2a41" }),
});
if (res.status === 503) {
const { code } = await res.json();
// code === "NOT_CONFIGURED" | "CDS_NOT_CONFIGURED" ...
// fail closed: surface the gap, do NOT treat as success
}{
"code": "CDS_NOT_CONFIGURED",
"configured": false,
"message": "Clinical decision support is not configured. Refusing with zero network calls."
}The EMR authorization callback consumes a single-use state value bound to the pending exchange. A replayed or expired state is spent — the callback refuses the token exchange rather than completing a second time.
# A single-use state cannot be redeemed twice or after expiry
curl -i "https://your-sandbox-origin.example/api/emr-sync/callback?code=AUTH_CODE&state=SPENT_STATE"{
"code": "INVALID_STATE",
"message": "OAuth state is expired or already redeemed. Restart the authorization flow."
}This is a replay defense, not a transient error: retrying with the same state will keep failing. Begin a fresh authorization to mint a new one.
On the money path, doubt is not advanced through. The deterministic six-condition gate is the sole authorizer; when a condition fails, it halts the instruction and names the condition rather than moving on.
Provider legitimacy lives inside the money path: a live OIG-LEIE + NPPES + credentialing composite is evaluated as one of the six conditions. When the screen returns PROVIDER_SCREEN_EXCLUDED, INDETERMINATE, or STALE, the gate does not advance on doubt — it halts the instruction into the review queue and reports the first failing condition.
{
"code": "DISPOSED",
"passed": false,
"status": "HALTED",
"firstFailed": "provider_screening",
"reason": "PROVIDER_SCREEN_EXCLUDED"
}A halt is a correct, expected outcome — not a server error. The HTTP transaction succeeds; the disposition is a refusal to authorize. A stale or indeterminate screen is treated exactly like an exclusion: no money moves until the condition clears.
Every /api/* request is authenticated before it reaches a handler. Webhook endpoints add a signature check over the raw body; in production an unverified signature is rejected before any work is done.
# Production rejects an unverified webhook signature
curl -i -X POST "https://your-sandbox-origin.example/api/webhooks/partner" \
-H "Content-Type: application/json" \
-H "X-Shteg-Signature: BAD_OR_MISSING" \
-d '{"event_type": "example.updated"}'{
"code": "SIGNATURE_INVALID",
"message": "Webhook signature could not be verified. Rejected."
}The same status covers an ordinary session failure: no valid session at the edge yields a 401 before the route body ever runs.
Authentication proves who you are; authorization decides what you may do. When your scope or role does not permit an action, the route returns a 403 — authenticated, but not permitted.
{
"code": "PERMISSION_DENIED",
"message": "Your session is not permitted to perform this action."
}The shapes above are error shapes available in the sandbox. The two codes below are part of the stated fail-closed doctrine — design intent — not a claim about a specific endpoint.
402 Payment RequiredDoctrineReserved as the fail-closed signal for a money action that cannot proceed until a funding or entitlement precondition is satisfied. Presented as design intent for the settlement doctrine — not asserted against a specific route today.
409 ConflictDoctrineThe idempotency and single-execution signal — one encounter, one instruction, keyed on a canonical WORM key so a replay never double-books. Stated as doctrine here; where a money route already enforces it, its own reference documents the exact conditions.
The distinction is the point: we will not dress a design intention up as a shipped guarantee. Available-in-sandbox means the shape is exercised by real logic; doctrinal means we are telling you where the model is headed, honestly labelled as such.
Unconfigured integrations return typed 501/503 refusals with zero network calls; single-use credentials cannot be replayed; the settlement gate halts and names its failing condition rather than advancing on doubt. No live money or live patient data is reachable from the sandbox — these boundaries are how the rails stay honest under load.