Eligibility
Check eligibility
POST/api/v1/eligibility
Asks the payer about a subscriber's (or a dependent patient's) dental coverage and returns the check: the coverage status, plan, maximums, deductibles, coverage by category and the detail for the procedure codes asked about. Needs an Idempotency-Key header: repeating a request with the same key returns the first answer. Add Cache-Control: no-cache to skip a recent identical answer. When the payer rejects the check, answers that it is unavailable, or the network fails, the reply is an error that carries the check (see the check field of the error). In test mode the member ID chooses a sandbox scenario.
Needs a key with submit permission.
Request
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Idempotency-Key | string | required | Makes the request safe to repeat: a request with the same key and body returns the first answer (the reply has an idempotent-replayed header), and the same key with a different request is refused. 1 to 255 printable characters; a UUID is a good choice.At least 1 character.At most 255 characters.Matches `^[\x21-\x7e]{1,255}$`. |
| Cache-Control | string | optional | Send no-cache to skip a recent identical answer and ask the payer again. Without it, an identical request made recently is answered from the saved check (cache.state is hit).One of: `no-cache`. |
Body
| Name | Type | Required | Description |
|---|---|---|---|
| office_id | string | required | The office the check is for (off_...), from GET /offices. Its mode must match the key. |
| payer_id | string | required | The payer: a Claim House payer ID (pyr_...), a payer ID or an alias from the payer directory. |
| subscriber | object | required | The plan holder. |
| subscriber.first_name | string | required | The subscriber's first name.At most 120 characters. |
| subscriber.last_name | string | required | The subscriber's last name.At most 120 characters. |
| subscriber.date_of_birth | string (date) | required | YYYY-MM-DD. |
| subscriber.member_id | string | required | The subscriber's member ID. In test mode it chooses the sandbox scenario.At most 80 characters. |
| subscriber.group_number | string or null | optional | The plan group number.At most 80 characters. |
| patient | object or null | optional | The patient, when a dependent is being checked. Leave it out when the subscriber is the person being checked. |
| patient.first_name | string | required | The patient's first name.At most 120 characters. |
| patient.last_name | string | required | The patient's last name.At most 120 characters. |
| patient.date_of_birth | string (date) | required | YYYY-MM-DD. |
| patient.relationship | string | required | How the patient relates to the subscriber: spouse, child, other.One of: `spouse`, `child`, `other`. |
| service_date | string (date) or null | optional | YYYY-MM-DD, within 12 months of today. Default: today, in the office's time zone. |
| procedure_codes | array of string or null | optional | Up to 10 CDT codes (D followed by 4 digits) to get coverage detail for.At most 10 items.Each item matches `^D\d{4}$`. |
| detail_level | string or null | optional | One of standard, enhanced. Default: standard.One of: `standard`, `enhanced`. |
| tenant_reference | string or null | optional | Your own reference for the check, returned with it. Up to 255 characters.At most 255 characters. |
| parent_id | value | optional | The rejected check this one corrects (elg_...), when resending it with the fixes. |
Response
The check, when the payer answered it (active or inactive coverage). Status 200.
| Name | Type | Description |
|---|---|---|
| id | string | An ID that starts with elg_. |
| object | string | Always `eligibility_check`. |
| office_id | string | An ID that starts with off_. |
| patient_id | string or null | The patient this is about (pat_...) in the patient directory: the dependent when there is one, else the subscriber. Set by Claim House when a check completes or a claim's patient is written; null while a check is pending, and when the patient has no date of birth or a name with no letters or digits to compare. |
| payer | object | |
| payer.id | string | An ID that starts with pyr_. |
| payer.payer_id | string | The payer's own payer ID. |
| payer.name | string | |
| state | string | One of: `pending`, `completed`. |
| outcome | string or null | How the check ended; null while pending.One of: `answered`, `rejected`, `payer_unavailable`, `error`. |
| status | string or null | The coverage status: active, inactive or unknown; null when there is no answer.One of: `active`, `inactive`, `unknown`. |
| billing | object or null | Null while the check is pending: it has no usage record yet. |
| billing.charged | boolean | |
| billing.state | string | One of: `billable`, `not_billable`. |
| billing.reason | string | One of: `test_mode`, `answered`, `payer_rejected`, `payer_side_rejection`, `payer_unavailable`, `network_error`, `internal_error`, `cache_hit`. |
| billing.label | string | Why the check was or was not charged, in a sentence. |
| cache | object | |
| cache.state | string | One of: `fresh`, `hit`. |
| cache.source_id | string or null | An ID that starts with elg_. |
| parent_id | string or null | The rejected check this one corrects. |
| request_id | string or null | The API request that made the check (the x-request-id of its response); null for a check made in the dashboard. |
| idempotency_key | value | The Idempotency-Key the check was sent with. |
| source | object or null | |
| source.network | value | |
| source.latency_ms | integer or null | At least -9007199254740991. |
| source.completed_at | string (date-time) | |
| tenant_reference | value | Your own reference, sent with the check. |
| created_at | string (date-time) | |
| request | object | |
| request.subscriber | object | |
| request.subscriber.first_name | string | |
| request.subscriber.last_name | string | |
| request.subscriber.date_of_birth | string (date) | |
| request.subscriber.member_id | string | |
| request.subscriber.group_number | value | |
| request.patient | object or null | |
| request.patient.first_name | string | |
| request.patient.last_name | string | |
| request.patient.date_of_birth | string (date) | |
| request.patient.relationship | string | One of: `spouse`, `child`, `other`. |
| request.service_date | string (date) | |
| request.procedure_codes | array of string | |
| request.detail_level | string | The level of detail asked for.One of: `standard`, `enhanced`. |
| request.office_npi | string | The NPI of the office when the check was made. |
| rejection | object or null | |
| rejection.kind | string | One of: `fix_and_resend`, `payer_unavailable`, `rejected`. |
| rejection.payer_code | string | The payer's own rejection code. |
| rejection.reasons | array of object | |
| rejection.reasons[].code | string | One of: `MEMBER_NOT_FOUND`, `PATIENT_IS_DEPENDENT`, `DATE_OF_BIRTH_MISMATCH`, `MEMBER_ID_INVALID`, `MEMBER_ID_DUPLICATE`, `NAME_MISMATCH`, `SERVICE_DATE_INVALID`, `REQUEST_INVALID`, `REQUEST_NOT_ACCEPTED`, `PROVIDER_NOT_ON_FILE`, `PROVIDER_NOT_ELIGIBLE`, `PATIENT_NOT_ELIGIBLE`, `PATIENT_DETAILS_REQUIRED`, `PAYER_NOT_ANSWERING`, `PAYER_REJECTED_OTHER`. |
| rejection.reasons[].about | string | Whose side the reason is on.One of: `subscriber`, `patient`, `provider`, `request`, `clearinghouse`, `payer`. |
| rejection.reasons[].field | value | The request field to change (for example subscriber.member_id), or null. |
| rejection.reasons[].message | string | |
| rejection.reasons[].fix | string | |
| rejection.reasons[].suggested_value | string | |
| coverage_effective_date | value or null | |
| coverage_termination_date | value or null | |
| plan | object or null | |
| plan.name | value | |
| plan.type | value | |
| plan.group_number | value | |
| plan.benefit_period | value | |
| network_status | string or null | One of: `in_network`, `out_of_network`, `unknown`. |
| maximums | array of object or null | |
| maximums[].level | string | One of: `individual`, `family`. |
| maximums[].period | string | One of: `calendar_year`, `plan_year`, `lifetime`. |
| maximums[].amount | string or null | Matches `^-?\d+\.\d{2}$`. |
| maximums[].used | string or null | Matches `^-?\d+\.\d{2}$`. |
| maximums[].remaining | string or null | Matches `^-?\d+\.\d{2}$`. |
| maximums[].applies_to | value | |
| deductibles | array of object or null | |
| deductibles[].level | string | One of: `individual`, `family`. |
| deductibles[].period | string | One of: `calendar_year`, `plan_year`, `lifetime`. |
| deductibles[].amount | string or null | Matches `^-?\d+\.\d{2}$`. |
| deductibles[].used | string or null | Matches `^-?\d+\.\d{2}$`. |
| deductibles[].remaining | string or null | Matches `^-?\d+\.\d{2}$`. |
| deductibles[].applies_to | value | |
| coverage | array of object or null | |
| coverage[].category | string | One of: `preventive`, `diagnostic`, `basic`, `endodontics`, `periodontics`, `oral_surgery`, `major`, `prosthodontics`, `implants`, `orthodontics`. |
| coverage[].percent_paid | number or null | At least 0.At most 100. |
| coverage[].covered | value | |
| coverage[].frequency | value | |
| coverage[].notes | value | |
| procedures | array of object or null | |
| procedures[].code | string | Matches `^D\d{4}$`. |
| procedures[].description | value | |
| procedures[].percent_paid | number or null | At least 0.At most 100. |
| procedures[].covered | value | |
| procedures[].frequency | object or null | |
| procedures[].frequency.count | integer or null | At least -9007199254740991. |
| procedures[].frequency.period | value | |
| procedures[].frequency.scope | value | |
| procedures[].max_age | integer or null | At least 0. |
| procedures[].last_service_date | string (date) or null | |
| procedures[].next_eligible_date | string (date) or null | |
| procedures[].status | string or null | One of: `covered_now`, `not_due_yet`, `downgraded`, `age_limit`, `not_covered`. |
| procedures[].note | value | |
| plan_rules | object or null | |
| plan_rules.missing_tooth_clause | value | |
| plan_rules.waiting_period | value | |
| plan_rules.pre_authorization | value | |
| plan_rules.coordination_of_benefits | value | |
| plan_rules.major_services_paid_on | string or null | One of: `prep_date`, `seat_date`. |
| payer_notes | array of string or null | |
| not_returned | array of string or null |
Errors
| HTTP status | Code | What it means |
|---|---|---|
| 401 | UNAUTHORIZED | A valid API key is required. Send it as "Authorization: Bearer <key>". |
| 403 | PERMISSION_DENIED | This API key is not allowed to do that. |
| 422 | INVALID_REQUEST | The request is not valid. |
| 400 | IDEMPOTENCY_KEY_REQUIRED | POST and PATCH requests need an Idempotency-Key header. |
| 422 | IDEMPOTENCY_KEY_REUSED | That Idempotency-Key was already used with a different request. |
| 409 | IDEMPOTENCY_KEY_IN_USE | A request with that Idempotency-Key is still running. Retry shortly. |
| 413 | PAYLOAD_TOO_LARGE | The request body is larger than 1 MB. |
| 504 | TIMEOUT | The request took too long to finish. It may still have taken effect: look it up before sending it again with a new Idempotency-Key. What it made is found with GET /api/v1/eligibility?request_id=<this request_id>, and the same filter on /api/v1/claims and /api/v1/attachments (a key with read permission). |
| 422 | PAYER_REJECTED | The payer rejected the check. The check object says what to change, and whether resending can help. |
| 502 | PAYER_UNAVAILABLE | The payer's system is not answering right now. Nothing needs to change: send the check again later. |
| 502 | NETWORK_ERROR | The check could not be completed because the payer network failed. It is not billed. Send it again later. |
| 503 | ELIGIBILITY_UNAVAILABLE | Eligibility checks are not available in live mode yet. Use a test key. |
| 500 | INTERNAL | Something went wrong on our side. Quote the request ID if you contact us. |
Example
Example request
curl -X POST "https://sandbox.myclaimhouse.com/api/v1/eligibility" \
-H "Authorization: Bearer $CLAIMHOUSE_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"office_id": "off_01JM000000E008000000000003",
"payer_id": "pyr_01JM000000E008000000000004",
"subscriber": {
"first_name": "Alex",
"last_name": "Example",
"date_of_birth": "1985-04-12",
"member_id": "CH-ACTIVE-FULL",
"group_number": "GRP-100"
},
"service_date": "2026-03-10",
"procedure_codes": [
"D0120",
"D1110"
],
"tenant_reference": "visit-1042"
}'Example response: 200
{
"id": "elg_01JM000000E00800000000000G",
"object": "eligibility_check",
"office_id": "off_01JM000000E008000000000003",
"patient_id": "pat_01JM000000E00800000000004K",
"payer": {
"id": "pyr_01JM000000E008000000000004",
"payer_id": "00000",
"name": "Example Dental Plan"
},
"state": "completed",
"outcome": "answered",
"status": "active",
"billing": {
"charged": false,
"state": "not_billable",
"reason": "test_mode",
"label": "Not billed: test mode"
},
"cache": {
"state": "fresh",
"source_id": null
},
"parent_id": null,
"request_id": "req_01JM000000E008000000000010",
"idempotency_key": "visit-1042-attempt-1",
"source": {
"network": "sandbox",
"latency_ms": 180,
"completed_at": "2026-03-10T15:30:00.430000+00:00"
},
"tenant_reference": "visit-1042",
"created_at": "2026-03-10T15:30:00.250000+00:00",
"request": {
"subscriber": {
"first_name": "Alex",
"last_name": "Example",
"date_of_birth": "1985-04-12",
"member_id": "CH-ACTIVE-FULL",
"group_number": "GRP-100"
},
"patient": null,
"service_date": "2026-03-10",
"procedure_codes": [
"D0120",
"D1110"
],
"detail_level": "standard",
"office_npi": "1900000000"
},
"coverage_effective_date": "2024-01-01",
"coverage_termination_date": null,
"plan": {
"name": "Example Dental Plan PPO",
"type": "PPO",
"group_number": "GRP-100",
"benefit_period": "Calendar year"
},
"network_status": "in_network",
"maximums": [
{
"level": "individual",
"period": "calendar_year",
"amount": "2000.00",
"used": "0.00",
"remaining": "2000.00",
"applies_to": null
},
{
"level": "individual",
"period": "lifetime",
"amount": "3000.00",
"used": "0.00",
"remaining": "3000.00",
"applies_to": "orthodontics"
}
],
"deductibles": [
{
"level": "individual",
"period": "calendar_year",
"amount": "50.00",
"used": "0.00",
"remaining": "50.00",
"applies_to": "basic and major services"
},
{
"level": "family",
"period": "calendar_year",
"amount": "150.00",
"used": "0.00",
"remaining": "150.00",
"applies_to": "basic and major services"
}
],
"coverage": [
{
"category": "preventive",
"percent_paid": 100,
"covered": true,
"frequency": null,
"notes": "Exams and cleanings 2 per calendar year; bitewings 1 per year"
},
{
"category": "diagnostic",
"percent_paid": 100,
"covered": true,
"frequency": null,
"notes": null
},
{
"category": "basic",
"percent_paid": 80,
"covered": true,
"frequency": null,
"notes": null
},
{
"category": "endodontics",
"percent_paid": 80,
"covered": true,
"frequency": null,
"notes": null
},
{
"category": "periodontics",
"percent_paid": 80,
"covered": true,
"frequency": null,
"notes": null
},
{
"category": "oral_surgery",
"percent_paid": 80,
"covered": true,
"frequency": null,
"notes": null
},
{
"category": "major",
"percent_paid": 50,
"covered": true,
"frequency": null,
"notes": "Every 5 years; paid on seat date"
},
{
"category": "prosthodontics",
"percent_paid": 50,
"covered": true,
"frequency": null,
"notes": null
},
{
"category": "implants",
"percent_paid": 0,
"covered": false,
"frequency": null,
"notes": null
},
{
"category": "orthodontics",
"percent_paid": 50,
"covered": true,
"frequency": null,
"notes": "To age 26; $3,000 lifetime maximum"
}
],
"procedures": [
{
"code": "D0120",
"description": "Periodic oral exam",
"percent_paid": 100,
"covered": true,
"frequency": {
"count": 2,
"period": "calendar_year",
"scope": null
},
"max_age": null,
"last_service_date": "2025-09-10",
"next_eligible_date": null,
"status": "covered_now",
"note": null
},
{
"code": "D1110",
"description": "Adult cleaning",
"percent_paid": 100,
"covered": true,
"frequency": {
"count": 2,
"period": "calendar_year",
"scope": null
},
"max_age": null,
"last_service_date": "2025-09-10",
"next_eligible_date": null,
"status": "covered_now",
"note": "Shares 4 visits a year with D4910"
}
],
"plan_rules": {
"missing_tooth_clause": "No",
"waiting_period": "None",
"pre_authorization": "Suggested over $300",
"coordination_of_benefits": "Standard, birthday rule",
"major_services_paid_on": "seat_date"
},
"payer_notes": [
"Posterior composites are paid as amalgam on molars.",
"Porcelain is downgraded on molars, not on bicuspids.",
"Multi-visit procedures are paid on the seat date.",
"D1110 and D4910 share 4 visits per calendar year.",
"D0210 and D0330 share one limit every 60 months."
],
"not_returned": [],
"rejection": null
}A rejection with something to fix: 422
{
"error": "PAYER_REJECTED",
"message": "The payer rejected the check. The check object says what to change, and whether resending can help.",
"errors": [],
"request_id": "req_01JM000000E008000000000010",
"check": {
"id": "elg_01JM000000E00800000000000G",
"object": "eligibility_check",
"office_id": "off_01JM000000E008000000000003",
"patient_id": "pat_01JM000000E00800000000004K",
"payer": {
"id": "pyr_01JM000000E008000000000004",
"payer_id": "00000",
"name": "Example Dental Plan"
},
"state": "completed",
"outcome": "rejected",
"status": null,
"billing": {
"charged": false,
"state": "not_billable",
"reason": "test_mode",
"label": "Not billed: test mode"
},
"cache": {
"state": "fresh",
"source_id": null
},
"parent_id": null,
"request_id": "req_01JM000000E008000000000010",
"idempotency_key": "visit-1042-attempt-1",
"source": {
"network": "sandbox",
"latency_ms": 180,
"completed_at": "2026-03-10T15:30:00.430000+00:00"
},
"tenant_reference": "visit-1042",
"created_at": "2026-03-10T15:30:00.250000+00:00",
"request": {
"subscriber": {
"first_name": "Alex",
"last_name": "Example",
"date_of_birth": "1985-04-12",
"member_id": "CH-NOT-FOUND",
"group_number": "GRP-100"
},
"patient": null,
"service_date": "2026-03-10",
"procedure_codes": [
"D0120",
"D1110"
],
"detail_level": "standard",
"office_npi": "1900000000"
},
"coverage_effective_date": null,
"coverage_termination_date": null,
"plan": null,
"network_status": null,
"maximums": null,
"deductibles": null,
"coverage": null,
"procedures": null,
"plan_rules": null,
"payer_notes": null,
"not_returned": null,
"rejection": {
"kind": "fix_and_resend",
"payer_code": "75",
"reasons": [
{
"code": "MEMBER_NOT_FOUND",
"about": "subscriber",
"field": "subscriber.member_id",
"message": "Example Dental Plan could not find this subscriber.",
"fix": "Check the member ID, name and date of birth against the insurance card, then send the check again."
}
]
}
}
}A payer that is not answering: 502
{
"error": "PAYER_UNAVAILABLE",
"message": "The payer's system is not answering right now. Nothing needs to change: send the check again later.",
"errors": [],
"request_id": "req_01JM000000E008000000000010",
"check": {
"id": "elg_01JM000000E00800000000000G",
"object": "eligibility_check",
"office_id": "off_01JM000000E008000000000003",
"patient_id": "pat_01JM000000E00800000000004K",
"payer": {
"id": "pyr_01JM000000E008000000000004",
"payer_id": "00000",
"name": "Example Dental Plan"
},
"state": "completed",
"outcome": "payer_unavailable",
"status": null,
"billing": {
"charged": false,
"state": "not_billable",
"reason": "test_mode",
"label": "Not billed: test mode"
},
"cache": {
"state": "fresh",
"source_id": null
},
"parent_id": null,
"request_id": "req_01JM000000E008000000000010",
"idempotency_key": "visit-1042-attempt-1",
"source": {
"network": "sandbox",
"latency_ms": 180,
"completed_at": "2026-03-10T15:30:00.430000+00:00"
},
"tenant_reference": "visit-1042",
"created_at": "2026-03-10T15:30:00.250000+00:00",
"request": {
"subscriber": {
"first_name": "Alex",
"last_name": "Example",
"date_of_birth": "1985-04-12",
"member_id": "CH-PAYER-DOWN",
"group_number": "GRP-100"
},
"patient": null,
"service_date": "2026-03-10",
"procedure_codes": [
"D0120",
"D1110"
],
"detail_level": "standard",
"office_npi": "1900000000"
},
"coverage_effective_date": null,
"coverage_termination_date": null,
"plan": null,
"network_status": null,
"maximums": null,
"deductibles": null,
"coverage": null,
"procedures": null,
"plan_rules": null,
"payer_notes": null,
"not_returned": null,
"rejection": {
"kind": "payer_unavailable",
"payer_code": "42",
"reasons": [
{
"code": "PAYER_NOT_ANSWERING",
"about": "payer",
"field": null,
"message": "Example Dental Plan is not answering right now.",
"fix": "Nothing needs to change. Try again in a few minutes."
}
]
}
}
}