Splice Health

Connecting Stedi to an EHR or practice management system

Vendor APIs · · Abhinav Srivastava

Stedi accepts an 837P and returns a 277CA and an 835. The practice keeps its encounters, charges and payments in an EHR or practice management system. Your job is the code between the two. That code must read an encounter, build a claim Stedi accepts, track the claim, and post the payment back where the billing staff work.

The data flow

  1. Read signed encounters and their charges from the EHR or PM system.
  2. Check eligibility before the visit, or before the claim if the visit already happened.
  3. Map the encounter to a claim body and submit it with an idempotency key.
  4. Store Stedi's claim ID and your patient control number on the encounter.
  5. Receive 277CAs and 835s through events or polling.
  6. Post payments and adjustments back to the PM system, line by line.

Reading encounters from the EHR

EHR access varies by vendor. Some expose FHIR R4 resources, some expose a vendor REST or SOAP API, and some expose a database or file export. Pick the source that gives you signed encounters with procedure codes, modifiers, units, charges, diagnosis pointers, rendering and billing provider, and the patient's insurance. A FHIR Encounter often lacks the charge lines, so check where the vendor stores them before you design the mapping. Our open source eClinicalWorks connectors in Python and TypeScript show one way to pull this data.

Read only encounters that the practice marks as ready to bill. A draft encounter can change after you submit it, and the claim then disagrees with the chart.

Building the claim

Stedi's Create Professional Claim JSON endpoint follows the CMS-1500 form. Billing staff know that form, so a field-by-field mapping table is easy to review with them. The claims guide lists the top-level objects: payer, insured, patient, encounter, serviceLines, authorization, submitter and billing.

Several mapping rules come straight from Stedi's guide.

  • If the patient is the policyholder, send only insured. If the patient is a dependent with no member ID of their own, send both. A dependent with their own member ID goes in insured. An eligibility check returns that member ID in dependents[].memberId.
  • Generate a random patient control number of 17 characters or fewer from the basic character set. Stedi suggests a library such as nanoid. Store it on the encounter before you submit.
  • Set lineItemControlNumber on each service line to your charge line ID. The 835 returns it per line.
  • Keep leading zeros on payer IDs. Stedi's example is 00540 for SISCO.

The CMS-1500 endpoint does not take secondary or tertiary claims. If the practice bills secondary payers, you also need the legacy 837P JSON endpoint, which follows the X12 loops. Plan both mappings from the start.

Derive the Idempotency-Key from the encounter ID and a submission counter. A retry after a network error then cannot send the claim twice. Stedi keeps the key for 24 hours, per the API reference.

Handling Stedi's edits

Stedi repairs formatting problems, such as dashes in a phone number, and rejects claims that fail its edits. The edits page states that repairs never change clinical content. A rejected claim never reaches the payer. On the CMS-1500 endpoint, the response is HTTP 201 in both cases, with an errors array when edits fail. Write each error back to the encounter as a work item for the biller. Do not retry automatically, because the same data fails the same edit. Stedi asks you to resubmit an edit rejection with the same claim frequency code as the original.

Tracking the claim

Subscribe an event destination to transaction.processed, or poll the transactions endpoint. The responses guide covers both. Use events for speed and keep a poller as a backstop. Stedi retries failed event deliveries for up to 48 hours, and its events API returns only the last 30 days.

Your first 277CA comes from Stedi in the submission response. Later 277CAs can come from intermediaries and from the payer. In the JSON report, transactions[].payers[].organizationName shows the sender. Map each 277CA status to an encounter state in your system, and keep the raw report for audit.

When a claim needs correction after the payer adjudicates it, Stedi's resubmission page gives frequency code 7 for a replacement and 8 for a void on non-Medicare payers. Medicare professional claims resubmit with code 1. Stedi adds that payer companion guides can differ.

Posting the 835 back

Fetch each 835 with the 835 ERA Report endpoint. One ERA can hold payments for many claims, and one claim can show up in more than one ERA. Walk transactions[].detailInfo[].paymentInfo[]. Match each entry on claimPaymentInfo.patientControlNumber, case-insensitive, then match lines on serviceLines[].lineItemControlNumber.

For each line, post lineItemProviderPaymentAmount as the payment. Post each serviceAdjustments entry as an adjustment with its group code and reason code. Group PR is patient responsibility and moves to the patient balance. The PM system's payment API decides how much of this you can write. Some systems accept line-level payments and adjustments. Others accept only a payment total, which forces manual posting of the adjustments.

Stedi notes that payers sometimes resend an identical 835. Store the check or EFT trace number from paymentAndRemitReassociationDetails.checkOrEFTTraceNumber and skip a second ERA with the same number. Stedi also does not let you delete transactions, so record every transaction ID you have processed.

Enrollment and payer quirks

Each provider needs enrollment per payer and per transaction type when the payer requires it. ERA enrollment is always required and moves ERA delivery away from the previous clearinghouse, per Stedi's enrollment page. Schedule that change with the practice so remits do not split between two clearinghouses in the middle of a month. Subscribe to enrollment.activated and enrollment.task.assigned events so your system knows when a provider can bill and when a task waits on the practice.

For eligibility, Stedi's troubleshooting guide recommends automatic retries for AAA errors 42 and 80, unless the follow-up action is C, N or S. For checks a person waits on, it recommends retries for up to 2 minutes.

If you want a second opinion on this mapping before you write it, Splice Health offers a free 30 minute consult on Stedi integrations.

All posts