Submit insurance discovery checks - API

You can submit insurance discovery checks through the Stedi API. Visit Overview for how insurance discovery works, or Submit insurance discovery checks - Manual to submit them in the Stedi portal.

Transaction enrollment

Transaction enrollment isn't required for insurance discovery, but we do strongly recommend it. Enrollment lets Stedi run MBI lookups as part of the discovery check in addition to checks to other large payers that require enrollment, improving your results.

To enroll, submit an enrollment request for the Stedi Insurance Discovery payer (payer ID: DISCOVERY):

  1. Create a provider record with the information required for enrollment. If you already have a record for the provider, you can skip this step. Stedi portal | API endpoint
  2. Submit an enrollment request for Real-time eligibility checks. Use DISCOVERY as the payer ID (Stedi Insurance Discovery). Stedi portal | API endpoint

Medicaid Provider ID

A Medicaid Provider ID is a unique identification number that state Medicaid programs assign to healthcare providers when they enroll. It's different than a provider's National Provider Identifier (NPI), which the federal government assigns. If a provider works in multiple states, they'll have different Medicaid IDs for each state.

Medicaid Provider IDs aren't required for transaction enrollment with Stedi, but they can enhance insurance discovery check results for certain payers. To add Medicaid Provider IDs to transaction enrollment requests, email enrollments@stedi.com with a CSV file containing the NPI and Medicaid ID(s) for each provider.

Many state Medicaid agencies have a provider lookup tool you can use to find a provider's Medicaid Provider ID. For example, providers practicing in Texas can use the Texas Medicaid & Healthcare Partnership Provider lookup tool to search for enrolled providers. You can also call the state Medicaid agency to request a provider's ID.

Required patient information

Provide as much patient demographic information as possible when submitting insurance discovery checks. The more information you provide, the more likely Stedi is to find matching coverage.

Minimum required

At a minimum, you must provide the patient's:

  • First name
  • Last name
  • Date of birth (DOB)

We strongly recommend providing as much of the following additional information as possible:

  • State from the current address or previous addresses. This ensures that Stedi's insurance discovery logic finds localized, relevant payers. Without the state, Stedi can only determine coverage from the large, national payers.
  • Social Security Number (SSN). For medical use cases, the patient's full SSN is best, but even the last 4 digits of the SSN can help narrow down matching coverage. For dental use cases, providing the SSN is especially important. Many dental payers accept SSN in place of a member ID. When included, Stedi can search with additional dental carriers that accept SSN instead of member ID. If you don't have the subscriber's member ID but can include the SSN, Stedi is more likely to return active dental coverage.

Submit insurance discovery checks

You can submit an insurance discovery check using the Insurance Discovery Check endpoint.

  • Supply as much patient demographic information as possible. Visit Required patient information for guidance.
  • Set encounter.serviceTypeCodes to ["35"] to narrow the search to dental payers. Omit it to default to searching medical payers.

Request

The following examples show insurance discovery checks for a patient named Jane Doe. The medical example omits encounter.serviceTypeCodes to default to searching medical payers. The dental example includes encounter.serviceTypeCodes: ["35"] to narrow the search to dental payers.

curl --request POST \
  --url "https://healthcare.us.stedi.com/2024-04-01/insurance-discovery/check/v1" \
  --header "Authorization: <api_key>" \
  --header "Content-Type: application/json" \
  --data '{
      "provider": {
        "npi": "1999999984"
      },
      "encounter": {
        "beginningDateOfService": "20250326",
        "endDateOfService": "20250328"
      },
      "subscriber": {
        "dateOfBirth": "20010925",
        "firstName": "Jane",
        "lastName": "Doe",
        "ssn": "123456789",
        "address": {
          "address1": "1 MAIN ST",
          "address2": "UNIT 1",
          "city": "ANYTOWN",
          "state": "MO",
          "postalCode": "12341"
        }
      }
  }'
curl --request POST \
  --url "https://healthcare.us.stedi.com/2024-04-01/insurance-discovery/check/v1" \
  --header "Authorization: <api_key>" \
  --header "Content-Type: application/json" \
  --data '{
      "provider": {
        "npi": "1999999984"
      },
      "encounter": {
        "beginningDateOfService": "20250326",
        "endDateOfService": "20250328",
        "serviceTypeCodes": ["35"]
      },
      "subscriber": {
        "dateOfBirth": "20010925",
        "firstName": "Jane",
        "lastName": "Doe",
        "ssn": "123456789",
        "address": {
          "address1": "1 MAIN ST",
          "address2": "UNIT 1",
          "city": "ANYTOWN",
          "state": "MO",
          "postalCode": "12341"
        }
      }
  }'

Response

Stedi's synchronous response to the Insurance Discovery Check endpoint can take up to 120 seconds, though it's often faster (typically 3.5-12 seconds).

The synchronous response can have one of two status values, each returned with its own HTTP status code: PENDING (HTTP 202) or COMPLETE (HTTP 200).

Pending

Stedi returns HTTP 202 with a PENDING status when it's still processing the insurance discovery check for the patient. You can use the discoveryId in the response to retrieve the results asynchronously.

The following example response shows a PENDING insurance discovery check.

{
  "discoveryId": "12345678-abcd-4321-efgh-987654321abc",
  "meta": {
    "applicationMode": "production",
    "traceId": "1-abcdef12-123456789abcdef123456789"
  },
  "status": "PENDING",
  "items": []
}

Complete

Stedi returns HTTP 200 with a COMPLETE status when the insurance discovery check for the patient is finished. If Stedi finds coverage, the items array contains the results. If Stedi doesn't find any coverage, the items array is empty.

The following example response shows a COMPLETE insurance discovery check. Stedi found one instance of potential matching coverage for the patient, Jane Doe. Insurance discovery checks only return active coverage for the provided date-of-service range, so old plans with expired coverage aren't in the results.

Information about the potential match is available in the items array.

  • The payer is Aetna. Payer names and IDs aren't normalized, so you may need to handle matching these results to Stedi's Payer Network or your own internal payer list.
  • The patient in the request, Jane, is a dependent on the Aetna plan because her demographic information appears in the dependent object in the response. The dependent's last name is slightly different from the patient's last name in the insurance discovery request. However, all the other demographic details in the dependent object - first name, date of birth, address - match the patient from the request. The two-part last name, Smith Doe, appears to be the complete version of the last name in the request, Doe. Based on this information, you can confirm that this is active coverage for the patient. Visit Verify the match for the full set of checks to run on every coverage in the response.
  • The benefitsInformation object (truncated to keep this example concise) contains the patient's benefits details. These are the same benefits details an eligibility check returns, so you can interpret them the same way. In the following example, the patient has active medical coverage under their health plan for the service dates in the request. Visit Active coverage and benefits to learn more about interpreting the benefits information in the insurance discovery check response.
{
  "coveragesFound": 1,
  "discoveryId": "12345678-abcd-4321-efgh-987654321abc",
  "items": [
    {
      "provider": {
        "providerName": "THE DOCTORS OFFICE",
        "entityType": "Non-Person Entity",
        "npi": "1999999984"
      },
      "subscriber": {
        "memberId": "J9606211996",
        "firstName": "JOHN",
        "lastName": "DOE",
        "groupNumber": "012345607890008",
        "groupDescription": "SAMPLE HEALTH GROUP",
        "insuredIndicator": "Y"
      },
      "dependent": {
        "firstName": "JANE",
        "lastName": "SMITH DOE",
        "gender": "F",
        "dateOfBirth": "20010925",
        "planNumber": "0123654",
        "relationToSubscriber": "Child",
        "relationToSubscriberCode": "19",
        "address": {
          "address1": "1 MAIN ST",
          "address2": "UNIT 1",
          "city": "ANYTOWN",
          "state": "MO",
          "postalCode": "12341"
        }
      },
      "payer": {
        "entityIdentifier": "Payer",
        "entityType": "Non-Person Entity",
        "lastName": "Aetna",
        "name": "Aetna",
        "payorIdentification": "100003"
      },
      "planInformation": {
        "planNumber": "0123654"
      },
      "planDateInformation": {
        "planBegin": "20250101",
        "eligibilityBegin": "20250101",
        "service": "20250327"
      },
      "benefitsInformation": [
        {
          "code": "1",
          "name": "Active Coverage",
          "coverageLevelCode": "FAM",
          "coverageLevel": "Family",
          "serviceTypeCodes": ["30"],
          "serviceTypes": ["Health Benefit Plan Coverage"],
          "insuranceTypeCode": "PS",
          "insuranceType": "Point of Service (POS)",
          "planCoverage": "Aetna Choice POS II",
          "inPlanNetworkIndicatorCode": "W",
          "inPlanNetworkIndicator": "Not Applicable"
        },
        // truncated for brevity
        {
          "code": "W",
          "name": "Other Source of Data",
          "benefitsRelatedEntities": [
            {
              "entityIdentifier": "Payer",
              "entityType": "Non-Person Entity",
              "entityName": "AETNA",
              "address": {
                "address1": "PO BOX 981106",
                "city": "EL PASO",
                "state": "TX",
                "postalCode": "79998"
              }
            }
          ]
        }
      ],
      "confidence": {
        "level": "REVIEW_NEEDED"
      }
    }
  ],
  "meta": {
    "applicationMode": "production",
    "traceId": "1-67e5a730-75011daa6caebf3c6595bf7c"
  },
  "status": "COMPLETE"
}

Retrieve results asynchronously

Use the discoveryId from a PENDING response to retrieve the results from the Insurance Discovery Check Results endpoint.

You can begin polling immediately after receiving the PENDING status response from the synchronous endpoint. Like the synchronous endpoint, the Insurance Discovery Check Results endpoint can take up to 120 seconds to return a response.

It's unlikely for the insurance discovery process to take more than a few minutes, so it's rare to have to poll the asynchronous endpoint more than once. However, if you receive a PENDING status, you can poll the endpoint immediately again, and continue this polling process until the status changes to COMPLETE.

Stedi stores insurance discovery results for 30 days. After that, the results are no longer available from this endpoint or in the Stedi portal.

Concurrency limit

Visit Concurrency limits for more information.

You may want to use an API client to make testing and debugging easier.

We don't recommend using Postman for requests containing Protected Health Information (PHI) because Postman defaults to storing request history - including full request payloads - on its cloud servers. You can't turn this feature off without impractical workarounds.

Visit API clients for a list of recommended clients you can use instead.

No matches

Insurance discovery checks can return zero matches for a patient even when they have active coverage.

{
  "coveragesFound": 0,
  "discoveryId": "0197a79a-ed75-77c3-af58-8ece597ea0be",
  "items": [],
  "meta": {
    "applicationMode": "production",
    "traceId": "1-685c0f14-1b559a954f0bd0127110d161"
  },
  "status": "COMPLETE"
}

Common reasons for zero matches include:

  • Recommended demographic data, like state or SSN, was missing from the request. Provide as much patient demographic information as possible to increase the chances of finding matching coverage.
  • The payer with the patient's active coverage doesn't respond to eligibility requests without certain patient demographic details. Provide as much patient demographic information as possible to increase the chances of finding matching coverage.
  • The patient's data doesn't exactly match what the payer has on file. For example, the patient demographics in the request don't match the patient's legal name or date of birth.
  • The payer that the patient has doesn't support real-time eligibility checks, which makes it impossible for Stedi to determine coverage.
  • The patient has coverage with a payer that supports eligibility checks, but Stedi's insurance discovery system couldn't find the payer.

We're always looking for feedback. If you know the patient has coverage and determined the payer through other means, reach out to Stedi's support team for further investigation.

Determine which matches to use

An insurance discovery check can return multiple coverages. Stedi returns all the coverage it finds for the patient without filtering the results, so more than one item in the items array doesn't necessarily mean the patient has more than one active plan. A response can include several coverages for the following reasons:

  • Some national payers return every plan they have on file, including inactive coverage.
  • Payers return coverage types you didn't request, so a check scoped to one service type code can come back with benefits for others.
  • Checks match on demographic data, so some items aren't your patient.

For every coverage in the response, verify the match and confirm active coverage on your date of service. When there is more than one active coverage for the patient, you need to determine the billing order (primacy) before you submit a claim.

Verify the match

For each coverage, always verify that the returned subscriber or dependent information matches the demographic information you submitted for the patient. Pay special attention to:

  • Name matching: The patient's name must match, though slight variations (like hyphenated or two-part last names) may appear.
  • Date of birth: Must match exactly.
  • Address: Must match or be a previous address.
  • Other details you collected at intake: For example, the patient's gender, ZIP Code, or state.

When the patient is a dependent, their details are in the dependent object and the policyholder's details are in the subscriber object. Their last names don't always match.

The confidence object doesn't indicate which coverage to use. Stedi sets confidence.level to REVIEW_NEEDED for every coverage it returns, so you must compare the patient's demographic details yourself to determine which coverage is a match.

Confirm active coverage on your date of service

After you verify the match, confirm that the coverage is active on your date of service.

Start by comparing the dates in the planDateInformation object to your date of service. The patient likely wasn't covered if your date of service falls after the earliest ending plan, eligibility, planEnd, eligibilityEnd, policyEffective, or policyExpiration value. For an appeal or a write-off, use that end date.

These dates apply to all the benefits in the patient's plan unless certain benefits have their own eligibility dates. Visit When is the patient eligible for benefits? for complete details.

In the following example, the plan ended on December 31, 2024, so the patient didn't have coverage for dates of service in 2025.

"planDateInformation": {
  "planBegin": "20240101",
  "planEnd": "20241231",
  "eligibilityBegin": "20240102"
}

Then check the benefitsInformation objects in the response.

  • Status codes: Codes 1 - 5 indicate active coverage. Codes 6 - 8 indicate inactive coverage, which means that plan has ended - the patient may still have active coverage under a different plan in the same response. Codes 5, 7, and 8 indicate a pending investigation or eligibility update on the payer's side, so verify the coverage with the payer before you submit a claim.
  • Service types: Check serviceTypeCodes to confirm the coverage applies to the services you plan to bill.
  • Implied coverage: Payers don't always send an explicit status for every service type. However, when the response includes co-payments, deductibles, or other benefits information for a service type, you can assume the patient has active coverage for it.

Visit Active and inactive coverage for complete details.

The following examples show active and inactive coverage for service type code 30 (Health Benefit Plan Coverage).

{
  "code": "1",
  "name": "Active Coverage",
  "serviceTypeCodes": ["30"],
  "serviceTypes": ["Health Benefit Plan Coverage"],
  "insuranceTypeCode": "C1",
  "insuranceType": "Commercial",
  "planCoverage": "Gold Plan HMO"
}
{
  "code": "6",
  "name": "Inactive",
  "serviceTypeCodes": ["30"],
  "serviceTypes": ["Health Benefit Plan Coverage"],
  "inPlanNetworkIndicatorCode": "W",
  "inPlanNetworkIndicator": "Not Applicable"
}

When a coverage passes these checks, you can use its benefits details to determine the patient's eligibility for services. You don't need to run a follow-up eligibility check since the insurance discovery response includes the same benefits information.

Handle multiple active coverages

For each coverage Stedi returns, verify the match and confirm active coverage on your date of service.

When more than one coverage is active for your patient, you need to know which payer pays first (primacy) before you submit a claim. Start by identifying every payer in the response, then determine the order.

Identify payers

Insurance discovery checks aren't guaranteed to return every active plan, so treat the payers in the response as a starting point rather than a complete list.

Each item's payer object identifies the payer for that coverage, including the payorIdentification you use to submit claims.

"payer": {
  "entityIdentifier": "Payer",
  "entityType": "Non-Person Entity",
  "lastName": "Aetna",
  "name": "Aetna",
  "payorIdentification": "100003"
}

Check for benefitsInformation objects with code = R (Other or Additional Payor). These objects are where payers report other coverage they know about for the patient, in the benefitsRelatedEntities array. For example, Medicare coverage often names the patient's supplemental plan, and the supplemental plan often names Medicare.

You can send an eligibility check to additional payers in benefitsRelatedEntities. The response confirms the coverage and contains a member ID you can use to submit claims.

The following example shows a benefitsInformation object that names an additional payer.

{
  "code": "R",
  "name": "Other or Additional Payor",
  "serviceTypeCodes": ["30"],
  "serviceTypes": ["Health Benefit Plan Coverage"],
  "benefitsRelatedEntities": [
    {
      "entityIdentifier": "Payer",
      "entityType": "Non-Person Entity",
      "entityName": "AETNA",
      "entityIdentification": "PI",
      "entityIdentificationValue": "60054"
    }
  ]
}

If benefitsRelatedEntities is empty and you later receive a claim denial citing coordination of benefits (COB):

  • Check the subscriber and dependent objects in the response for demographic details you didn't submit, such as a complete address.
  • Run another insurance discovery check with those details. This can surface coverage the first check didn't return.

Determine primacy for billing

Once you've identified all the payers, you need to know the billing order (primacy). Insurance discovery checks don't indicate primacy. You can establish primacy from a prior claim denial or by running a coordination of benefits (COB) check.

Prior claim denial

When you're sure the patient has only two active coverages and one of those payers has already denied a claim citing COB, the other payer is primary.

For example, a check returns active coverage with both Aetna and Cigna, and Aetna denied your claim citing COB. In this case, Cigna is primary:

  1. Resubmit the claim to Cigna and wait for adjudication.
  2. Bill Aetna with a secondary claim built from Cigna's 835 Electronic Remittance Advice (ERA). Use the payment and adjustment amounts exactly as the ERA reports them. Visit Submit to a secondary or tertiary payer for the properties to include on the claim.

COB check

If you're not sure about primacy, run a coordination of benefits (COB) check. COB checks determine primacy across a member's known coverage and report the billing order in the response. A COB check can also return payers that the insurance discovery check didn't. Always set the billing order from a COB response, not from an insurance discovery or eligibility response.

Original Medicare isn't part of the COB database, though many Medicare Advantage plans are. When a patient has Original Medicare, a COB check can't determine the billing order, so you must apply the primacy rules yourself. Visit Primacy rules for the guidelines Stedi follows, including how Medicare coordinates with other plans.

Visit Coordination of benefits (COB) checks for instructions on running a COB check.

On this page