Skip to content
LambogeniusDocs

HL7 Edge Gateway

CAQH CORE

Real-time eligibility, batch claims and batch results (835, 277CA, 999) with payers' and clearinghouses' CAQH CORE servers, C4.0.0 and 2.2.0.

Runs onWindows · Linux · macOS · DockerFree developer license for test traffic. Production traffic needs a paid license.

Payers and clearinghouses take X12 over the connectivity rules of CAQH CORE: a SOAP envelope around the interchange, sent over TLS with a client certificate or credentials. A caqh_core destination does that for an X12 route, in real time (a 270, answered with the payer's 271) or as batch submissions (837 claims through the queue).

Real-time eligibility

Registration posts a 270 to the gateway and gets the payer's 271 back in the same request. The route is pass-through, so nothing is queued and the answer is the payer's own.

# Registration asks the payer about a patient's coverage: a 270 in, the payer's 271 back.
[[route]]
name = "eligibility"
mode = "passthrough"
format = "x12"
request_timeout = "20s"
passthrough = { ack_from = "response_body" }   # answer the sender with the payer's 271
source = { type = "http", listen = "0.0.0.0:8443", path = "/x12/eligibility", auth = { type = "api_key", keys = { registration = "${REGISTRATION_KEY}" } } }

[[route.destinations]]
name = "acme"
type = "caqh_core"
url = "https://core.acmehealth.example/CORE/RealTime"
sender_id = "NORTHSIDE"            # SenderID and ReceiverID, as the payer's connectivity guide gives them
receiver_id = "ACMEHEALTH"
tls = { client_cert = "/etc/hl7-edge-gateway/core-client.crt", client_key = "/etc/hl7-edge-gateway/core-client.key" }
auth = { type = "oauth2", token_url = "https://login.acmehealth.example/oauth2/token", client_id = "northside", client_secret = "${ACME_CORE_SECRET}" }

What the gateway sends, C4.0.0 with the interchange as an MTOM attachment:

POST /CORE/RealTime HTTP/1.1
Authorization: Bearer eyJ…
Content-Type: multipart/related; boundary="MIMEBoundary_…"; type="application/xop+xml";
  start="<root.…@hl7-edge-gateway>"; start-info="application/soap+xml"; action="RealTimeTransaction"

--MIMEBoundary_…
Content-Type: application/xop+xml; charset=UTF-8; type="application/soap+xml"

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body>
  <ns1:COREEnvelopeRealTimeRequest xmlns:ns1="http://www.caqh.org/SOAP/WSDL/CORERuleC4.0.0.xsd">
    <PayloadType>X12_270_Request_005010X279A1</PayloadType>
    <ProcessingMode>RealTime</ProcessingMode>
    <PayloadID>9b0c6a0e-…</PayloadID>
    <TimeStamp>2026-10-26T14:30:05Z</TimeStamp>
    <SenderID>NORTHSIDE</SenderID>
    <ReceiverID>ACMEHEALTH</ReceiverID>
    <CORERuleVersion>C4.0.0</CORERuleVersion>
    <Payload><xop:Include href="cid:payload.…@hl7-edge-gateway" xmlns:xop="http://www.w3.org/2004/08/xop/include"/></Payload>
  </ns1:COREEnvelopeRealTimeRequest>
</soapenv:Body></soapenv:Envelope>
--MIMEBoundary_…
Content-Type: application/octet-stream
Content-ID: <payload.…@hl7-edge-gateway>

ISA*00*          *00*          *ZZ*NORTHSIDE      *ZZ*ACMEHEALTH     *…~
…

Batch claims

On a store-and-forward route, each interchange is a batch submission (BatchSubmitTransaction), with PayloadLength and a SHA-1 CheckSum of the payload. The billing system gets the gateway's 999 at once; the queue delivers in order and retries while the server is busy.

# Claims through the queue to the clearinghouse, as CORE batch submissions.
[[route.destinations]]
name = "clearinghouse"
type = "caqh_core"
url = "https://edi.clearinghouse.example/core"
sender_id = "NORTHSIDE"
receiver_id = "CLEARHOUSE"
processing_mode = "batch"          # the default on store-and-forward routes
tls = { client_cert = "/etc/hl7-edge-gateway/core-client.crt", client_key = "/etc/hl7-edge-gateway/core-client.key" }

Batch results

The server keeps what comes back from a batch for you to pick up: 999s for your submissions, 277CA claim acknowledgments and 835 remittances. A caqh_core source (0.8.1 or later) asks for them every poll_interval, for each PayloadType in retrieve, until the server has nothing left (X12_005010_Response_NoBatchResultsFile). Each file is taken in like any X12 interchange (checked, stored, filtered, transformed and delivered), and for a batch result the gateway's 999 goes back to the server, as CORE asks.

# 835 remittances and 277CA claim acknowledgments waiting on the clearinghouse's CORE server,
# picked up every 15 minutes and posted to the revenue system.
[[route]]
name = "remits"
mode = "store_and_forward"         # required: each result is stored before it is acknowledged
format = "x12"

[route.source]
type = "caqh_core"
url = "https://edi.clearinghouse.example/core"
sender_id = "NORTHSIDE"
receiver_id = "CLEARHOUSE"
retrieve = ["X12_835_Request_005010X221A1", "X12_277CA_Request_Batch_Results_005010X214E1_2"]
poll_interval = "15m"              # default 5m
# max_files = 100                  # per PayloadType and poll; the rest come next poll
# acknowledge = true               # send the gateway's 999 back for each result
tls = { client_cert = "/etc/hl7-edge-gateway/core-client.crt", client_key = "/etc/hl7-edge-gateway/core-client.key" }

[route.destination]
type = "http"
url = "https://revenue.northside.example/api/remits"
retrieveThe gateway asks with
X12_835_Request_005010X221A1, X12_277CA_Request_Batch_Results_005010X214E1_2, other resultsA Batch Results Retrieval (BatchResultsRetrievalTransaction); each result is acknowledged with a Batch Results Acknowledgement Submission carrying the gateway's 999.
X12_999_RetrievalRequest_005010X231A1 (and TA1)A Batch Submission Acknowledgement Retrieval (BatchSubmitAckRetrievalTransaction); acknowledgments aren't acknowledged.

The source takes the same rule_version, transport, tls, auth, username and password as the destination. A file the server hands out again is caught as a duplicate: acknowledged again, delivered once. In a cluster one node at a time polls. hl7edge_core_retrievals_total counts by route and result (retrieved <PayloadType>, nothing waiting, acknowledged, failed).

Rule versions

rule_versionEnvelopeSigning in
C4.0.0SOAP 1.2, the payload as an MTOM (XOP) attachment. The default.A client certificate (tls, X.509 over TLS 1.2+), and OAuth 2.0 client credentials (auth) where the server asks for a bearer token.
2.2.0SOAP 1.2 with the payload inline, or transport = "mime" (HTTP MIME multipart).A WS-Security UsernameToken, or form fields for MIME: username and password.
# A server still on Phase II connectivity (CORE 2.2.0)
[[route.destinations]]
name = "regional-payer"
type = "caqh_core"
rule_version = "2.2.0"
transport = "soap"                 # SOAP with a WS-Security UsernameToken; or "mime" (HTTP MIME multipart)
url = "https://edi.regionalpayer.example/core"
sender_id = "NORTHSIDE"
receiver_id = "REGIONAL"
username = "northside"
password = "${REGIONAL_CORE_PASSWORD}"

payload_type is X12_<ST-01>_Request_<guide> unless you set it, such as X12_270_Request_005010X279A1; processing_mode is real_time on pass-through routes and batch on store-and-forward ones. headers adds static headers.

What the server's answer does

The server answersThe gateway
SuccessDelivered. In real time, the payload (the 271) goes back to the sender as it came; a payer's 999 that rejects the request is a rejection.
UnAuthorized, a Receiver fault, 401, 403, 408, 429, 5xx, a timeoutRetried (store-and-forward), or 503 to the sender (pass-through). Fix the credentials or wait for the server.
Other ErrorCodes (PayloadTypeIllegal, VersionMismatch, …), a Sender faultRejected, with the code and message in the reason: dead letter or block, as on_reject says; 400 in pass-through.

hl7edge_core_requests_total counts the answers by route, destination and ErrorCode (or the HTTP status or fault when there was no envelope).

Testing with a payer

There is no public CORE test server. Payers and clearinghouses give you a test endpoint, IDs and credentials when you enroll; send test interchanges (ISA-15 = T) there first.