Two staff-authenticated routes ask a payer the X12 270 question — “is this member covered for this service on this date?” — and return the parsed 271 answer: coverage status, deductible and out-of-pocket remaining, copay, coinsurance, network, and prior-auth flags. One verifies a single patient in real time; the other batch-verifies a whole day’s appointment roster. LIVE lookups run under the org clearinghouse credentials; when those credentials are unconfigured the surface refuses rather than inventing coverage.
This is the sandbox surface — the real request parsing, organization scoping, response shaping, and fail-closed logic. No live patient data or live payer traffic is reachable from this tier; production access is a separate approval. Where a live payer transport is absent, the route returns a typed 503 in production and only ever offers structured sandbox demo coverage in non-production — production never simulates an eligible member.
Both routes are staff-only JSON over HTTPS against your sandbox origin. Every API request is authenticated before it reaches a handler; authorization is scope-checked per request; every patient is re-checked against your clinic before any 270 fires.
Requests are automatically scoped to your organization from your credentials — you never pass an organization or clinic identifier in the body. A patient must belong to your clinic before a live 270/271 leaves the boundary, so one clinic can never query another’s members.
Both routes require eligibility:read. A session without it is refused with 403 before any payer call.
Each verify response carries a mode of "live" or "sandbox". Sandbox is structured demo data reachable in non-production only; production emits live or a typed refusal — never fabricated coverage.
If the real-time payer transport is unconfigured, the live lookup responds 503. If the batch transport is unconfigured, the roster route raises a typed configuration refusal. Neither path invents an “eligible” result.
Exactly two routes. There is no 271 callback webhook and no standalone benefits-detail endpoint.
Real-time single-patient insurance eligibility via X12 270/271. LIVE connects to the payer network in real time; non-production falls back to structured SANDBOX demo data. Production NEVER simulates coverage. Results are cached for 24h to avoid redundant lookups.
Staff session (org-scoped) + scope eligibility:read. The patient is re-scoped to your clinic and the read is written to the PHI access log.
| Name | Type | In | Required | Description |
|---|---|---|---|---|
patientId | string | body | Required | Clinic-scoped patient to verify. Re-checked against your clinic before any 270 fires. |
firstName | string | body | Required | Member first name as it appears on the plan. |
lastName | string | body | Required | Member last name as it appears on the plan. |
dob | string | body | Required | Member date of birth, YYYY-MM-DD. |
memberId | string | body | Required | Payer member / subscriber ID. |
payerName | string | body | Required | Payer name; mapped to a payer network ID when payerId is omitted. |
payerId | string | body | Optional | Explicit payer network ID override. |
npi | string | body | Required | Rendering provider NPI. |
serviceType | string | body | Optional | X12 service type code — '30' vision, '5' diagnostic, '1' medical. |
dos | string | body | Optional | Date of service, YYYY-MM-DD. Defaults to today. |
| Field | Type | Description |
|---|---|---|
mode | 'live' | 'sandbox' | Source of the answer. Production returns live or a 503 refusal; sandbox is non-production only. |
eligible | boolean | Whether the member has active coverage for the requested service and date. |
memberName | string | Member name as returned on the 271. |
planName | string | Plan / product name. |
groupNumber | string | Group number from the 271. |
deductible | { individual, met, remaining } | Individual deductible amounts. |
outOfPocket | { individual, met, remaining } | Individual out-of-pocket maximum amounts. |
copay | { specialist, primaryCare } | Copay amounts by provider type. |
coinsurance | number | Member coinsurance percentage. |
priorAuthRequired | boolean | Whether prior authorization is flagged for the service. |
priorAuthCodes | string[] | Codes the payer flags as requiring prior auth. |
network | 'IN' | 'OUT' | 'UNKNOWN' | Network status of the rendering provider. |
verifiedAt | string | ISO-8601 timestamp of the verification. |
# Real-time single-patient eligibility (270 -> 271)
curl -X POST "https://your-sandbox-origin.example/api/eligibility/verify" \
-H "Content-Type: application/json" \
-H "Cookie: $SHTEG_SESSION" \
-d '{
"patientId": "pt_8a31",
"firstName": "Jordan",
"lastName": "Rivera",
"dob": "1979-04-12",
"memberId": "W1234567801",
"payerName": "Aetna",
"npi": "1922051234",
"serviceType": "30"
}'const res = await fetch("/api/eligibility/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({
patientId: "pt_8a31",
firstName: "Jordan",
lastName: "Rivera",
dob: "1979-04-12",
memberId: "W1234567801",
payerName: "Aetna",
npi: "1922051234",
serviceType: "30",
}),
});
// 503 in production if the live payer transport is unconfigured
const eligibility = await res.json();{
"mode": "sandbox",
"eligible": true,
"memberId": "W1234567801",
"memberName": "Jordan Rivera",
"planName": "Aetna Choice POS II",
"groupNumber": "0084213",
"deductible": { "individual": 1500, "met": 620, "remaining": 880 },
"outOfPocket": { "individual": 4000, "met": 620, "remaining": 3380 },
"copay": { "specialist": 45, "primaryCare": 25 },
"coinsurance": 20,
"coverageStart": "2026-01-01",
"coverageEnd": null,
"priorAuthRequired": false,
"priorAuthCodes": [],
"network": "IN",
"verifiedAt": "2026-07-14T15:22:41.108Z"
}| Status | Meaning |
|---|---|
400 | Missing patientId or a required member field. |
401 | Request is not authenticated. |
403 | Session lacks the eligibility:read scope, or the patient is outside your clinic scope. |
503fail-closed | Fail-closed: the live payer transport is unconfigured in production — the handler refuses rather than fabricating sandbox coverage. |
Example only — this panel makes no live request. mode:"sandbox" is non-production demo data; production returns mode:"live" or a typed 503 and never simulates coverage. Requires a staff session with scope eligibility:read.
curl -X POST https://api.shteg.ai/api/eligibility/verify \
-H "Authorization: Bearer $SHTEG_TOKEN" \
-H "Content-Type: application/json" \
-H "Cookie: $SHTEG_SESSION" \
-d '{
"patientId": "pt_8a31",
"firstName": "Jordan",
"lastName": "Rivera",
"dob": "1979-04-12",
"memberId": "W1234567801",
"payerName": "Aetna",
"npi": "1922051234",
"serviceType": "30"
}'Batch 270/271 across a day's appointment roster — pre-verify tomorrow's schedule before patients arrive. Per-appointment failures surface as 'unknown', never a fabricated success; appointments missing a member or payer id are skipped.
Staff session (org-scoped) + scope eligibility:read. Every appointment’s patient is re-scoped to your clinic first; if any is missing a patientId or falls outside scope, the whole batch is rejected.
| Name | Type | In | Required | Description |
|---|---|---|---|---|
appointments | Appointment[] | body | Required | The roster to verify. Each item needs a patientId; memberId/payerId may be per-item or defaulted. |
provider | EligibilityProvider | body | Optional | Rendering / billing NPI and name applied to the batch. |
defaultPayerId | string | body | Optional | Fallback payer id for appointments that omit one. |
serviceTypeCodes | string[] | body | Optional | Service type codes to query — e.g. ['30'] medical, ['AL'] vision. |
concurrency | number | body | Optional | Max in-flight 270/271 lookups. |
| Field | Type | Description |
|---|---|---|
summary | { total, active, inactive, unknown } | Counts across the roster. unknown = a lookup that could not be resolved (never a fabricated pass). |
results | RosterResult[] | Per-appointment outcome: the appointment, a skipped flag, and the parsed 271 result when available. |
# Pre-verify tomorrow's schedule
curl -X POST "https://your-sandbox-origin.example/api/eligibility/roster" \
-H "Content-Type: application/json" \
-H "Cookie: $SHTEG_SESSION" \
-d '{
"appointments": [
{ "patientId": "pt_8a31", "memberId": "W1234567801", "payerId": "60054", "start": "2026-07-15T09:00:00Z" },
{ "patientId": "pt_51bc", "memberId": "U9987654322", "payerId": "87726", "start": "2026-07-15T09:20:00Z" }
],
"provider": { "npi": "1922051234", "name": "Retina Associates" },
"serviceTypeCodes": ["30"]
}'const res = await fetch("/api/eligibility/roster", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({
appointments: tomorrowsRoster, // [{ patientId, memberId, payerId, start }]
provider: { npi: "1922051234", name: "Retina Associates" },
serviceTypeCodes: ["30"],
}),
});
// 503 if the batch eligibility transport is unconfigured
const { summary, results } = await res.json();{
"summary": { "total": 2, "active": 1, "inactive": 0, "unknown": 1 },
"results": [
{
"appointment": { "patientId": "pt_8a31", "start": "2026-07-15T09:00:00Z" },
"skipped": false,
"result": { "status": "active", "network": "IN" }
},
{
"appointment": { "patientId": "pt_51bc", "start": "2026-07-15T09:20:00Z" },
"skipped": false,
"result": { "status": "unknown" }
}
]
}| Status | Meaning |
|---|---|
400 | Empty appointments[], or an appointment missing a patientId. |
401 | Request is not authenticated. |
403 | Session lacks the eligibility:read scope, or an appointment patient is outside your clinic scope. |
503fail-closed | Fail-closed: the batch eligibility transport is unconfigured — the route raises a typed configuration refusal instead of a batch of fabricated results. |
Example only — this panel makes no live request. Per-appointment failures surface as "unknown", never a fabricated pass; when the batch transport is unconfigured the route fails closed. Requires a staff session with scope eligibility:read.
curl -X POST https://api.shteg.ai/api/eligibility/roster \
-H "Authorization: Bearer $SHTEG_TOKEN" \
-H "Content-Type: application/json" \
-H "Cookie: $SHTEG_SESSION" \
-d '{
"appointments": [
{ "patientId": "pt_8a31", "memberId": "W1234567801", "payerId": "60054", "start": "2026-07-15T09:00:00Z" },
{ "patientId": "pt_51bc", "memberId": "U9987654322", "payerId": "87726", "start": "2026-07-15T09:20:00Z" }
],
"provider": { "npi": "1922051234", "name": "Retina Associates" },
"serviceTypeCodes": ["30"]
}'Two routes, no more. What eligibility guarantees is not only these endpoints: it is also enforced internally, before a claim can be submitted.
The public eligibility surface is exactly POST /api/eligibility/verify and POST /api/eligibility/roster. There is no 271 callback webhook and no standalone benefits-detail endpoint — the 271 answer is parsed synchronously and returned inline on the same request. We document the routes that exist and nothing more.
LIVE eligibility depends on payer network connections that are not yet available in this tier. Unconfigured, the surface fails closed: production returns a typed 503, and only a non-production deployment offers structured sandbox demo coverage. Production never simulates an eligible member.
Eligibility is also enforced as an inline pre-condition on claim submission: a claim cannot proceed to submission without a satisfied eligibility check upstream. It is described here so the guarantee is legible, but it exposes no additional callable surface.
In the sandbox, the parsing, scoping, and fail-closed branches run on real logic against zero live data. Where a payer connection is unavailable, the route returns a typed refusal, not a fabricated 271.