Submitting a Claim
There are a few ways to submit a claim through the ClaimRev API. Pick the one that fits the system you're integrating from.
| Path | Claim types | Endpoint | Use when… |
|---|---|---|---|
| Raw EDI upload | 837P, 837I, 837D | POST /api/InputFile/v1 | Your system already generates X12 837 EDI. |
| JSON → EDI translation | 837P, 837I, 837D | POST /api/SharpRcm/v1/CreateClaimEdi | Your system has the structured editor model and you'd rather not generate EDI yourself. |
| Form boxes → claim | 837P, 837I | POST /api/ManualClaimEntry/v1/CreateClaimFromForm | You have CMS-1500 / UB-04 form data and want the simplest path — see Creating a Claim from Form Boxes. |
All paths feed the same downstream processing pipeline; the JSON/form paths simply run the data through ClaimRev's EDI builder before handing off.
Path A — Raw EDI upload
Wrap your 837 file content in a JSON envelope and POST it. The endpoint accepts the EDI text either as a plain string or as base64 — choose whichever your client makes easier:
POST /api/InputFile/v1
Authorization: Bearer <token>
Content-Type: application/json
{
"ediFileContent": "ISA*00* *00* *ZZ*SUBMITTERID *...",
"fileName": "claims_20260427.txt"
}
(If you'd rather send base64, use base64EdiFileContent instead of ediFileContent.)
The response confirms whether the file landed:
{
"message": "File uploaded successfully.",
"isError": false
}
What's accepted. The same endpoint accepts raw 837 (P/I/D) and raw 835. Other transaction types (999, 277CA, 270, 271) are not accepted as direct uploads.
Step 2: confirm the file landed
The submission response doesn't include a server-issued file id. To find your uploaded file (and check its parsing/ack status), search the input-file list:
POST /api/InputFile/v1/GetFileList
Authorization: Bearer <token>
Content-Type: application/json
{
"fileName": "claims_20260427.txt",
"receiveStartDate": "2026-04-27T00:00:00Z"
}
The response is a list of CustomerInputFileViewModel rows:
[
{
"id": "65e2f4...",
"fileName": "claims_20260427.txt",
"receiveDate": "2026-04-27T14:32:10Z",
"userEmailAddress": "you@example.com",
"accountNumber": "DEMO1",
"fileStatusName": "Processed",
"ackStatusName": "Accepted",
"claimAckStatusName": "Accepted",
"claimCount": 12,
"exceptionMessage": null
}
]
| Field | What it tells you |
|---|---|
id | Server-side file id. Use it as inputFileObjectId when searching claims. |
fileStatusName | File-level processing status (was the EDI parsed cleanly?). |
ackStatusName | 999 functional acknowledgment status from the clearinghouse. |
claimAckStatusName | 277CA claim-level acknowledgment status from the payer. |
claimCount | How many claims ClaimRev parsed out of the file. |
exceptionMessage | Populated when parsing or processing failed. |
The 999 functional ack arrives quickly — it's generated by ClaimRev's processing system once your file has been parsed. The 277CA claim acknowledgment arrives later (typically the day after submission). Adjudication outcomes (the 835) take days to weeks.
Use this endpoint to confirm the file was accepted and watch its ack progression; use claim search to track the individual claims afterwards.
Tracking individual claims. Because you wrote the EDI, you already know the control numbers you stamped — the ISA13 in your envelope, and CLM01 (patient control number) on each claim. Search for individual claims by those numbers, by patient demographics, or by the id you got from GetFileList (passed as inputFileObjectId).
Path B — JSON → EDI translation
If your system has structured claim data (a clinical encounter, a charge capture, a Healthie appointment), you can hand it to ClaimRev as JSON and let ClaimRev build and submit the 837. It builds an 837P unless you set claimType (see Building an 837I and Building an 837D):
POST /api/SharpRcm/v1/CreateClaimEdi
Authorization: Bearer <token>
Content-Type: application/json
The request body is a ClaimEdiCreationViewModel:
{
"claimType": "professional",
"claims": [ /* array of FullClaimEditorViewModel */ ],
"envelop": { /* IsaSegment — ISA control envelope */ },
"submitterId": "ClmRevEdiBuilder",
"submitterName": "...",
"submitterContactName": "...",
"submitterPhone": "...",
"receiverName": "ClaimRev"
}
The control number fields (transactionControlNumber, groupControlNumber, bhtControlNumber) auto-default to safe values if you don't supply them. The full shape of FullClaimEditorViewModel (billing/rendering provider, subscriber, claim, service lines, supplemental info, etc.) is in the API Reference — it mirrors the X12 837P loop structure but with developer-friendly field names. The CreateClaimEdi Field Map shows where every field lands in the 837P. The claim model is the same for every claim type; each type writes the fields its 837 uses.
The response includes the control numbers ClaimRev stamped on the EDI it built, plus the upload result:
{
"bhtControlNumber": "SHRPRCMEDIB202604271423",
"groupControlNumber": "142312345",
"transactionControlNumber": "638503476520012345",
"uploadFileResult": {
"message": "...",
"isError": false
}
}
These are the values you'll use to find the claim later.
Building an 837I
Set claimType to institutional and ClaimRev builds an 837I (005010X223A2) from the same claim model. Leave envelop.gs08 empty and it is set for you; if you do set it, it must be an institutional version, or the request is rejected with a 400. Leaving claimType off builds an 837P, as before.
The claim model is shared, so an institutional claim uses some fields differently:
| 837I | Field | Notes |
|---|---|---|
| Type of bill, CLM05 | claimEditorView.placeOfService and claimEditorView.claimFreqCode | placeOfService holds the facility type code. 13 and 1 give CLM05 = 13:A:1 (hospital outpatient, original). |
| Statement period, DTP*434 | claimEditorView.dateStatementDate | dateValue as a range, 20260110-20260110; it is sent as RD8. |
| Admission type, source, patient status, CL1 | claimEditorView.claimCode | admissionTypeCode, admissionSourceCode, patientStatusCode. CL1 is written when patientStatusCode is set. |
| Attending provider, 2310A | claimEditorView.attendingProvider | Same shape as the other providers; entityCode 71. |
| Service line, SV2 | serviceLinesEditorView[] | revenueCode (SV201, required on every line), procedureCodeQualifier and procedureCode (SV202, when the payer wants a HCPCS code), lineChargeAmount, serviceUnitMeasurementCode, serviceUnitCount. |
"claimType": "institutional",
"claims": [{
"claimEditorView": {
"patientControlNumber": "DAFFYINST0000000001",
"totalClaimChargeAmt": "700.00",
"placeOfService": "13",
"claimFreqCode": "1",
"dateStatementDate": { "dateValue": "20260110-20260110" },
"claimCode": { "admissionTypeCode": "1", "admissionSourceCode": "7", "patientStatusCode": "01" },
"diagnosisCodes": [{ "diagnosisCode": "S0990XA", "diagnosisOrder": 1, "diagnosisType": "ABK" }],
"attendingProvider": { "nm1Data": { "entityType": "1", "lastName": "FUDD", "firstName": "ELMER", "npi": "1679576722" } }
},
"serviceLinesEditorView": [
{ "lineNumber": 1, "revenueCode": "0450", "procedureCodeQualifier": "HC", "procedureCode": "99283",
"lineChargeAmount": "500.00", "serviceUnitMeasurementCode": "UN", "serviceUnitCount": "1",
"dateService": { "dateValue": "20260110" } },
{ "lineNumber": 2, "revenueCode": "0300", "lineChargeAmount": "150.00",
"serviceUnitMeasurementCode": "UN", "serviceUnitCount": "1", "dateService": { "dateValue": "20260110" } }
]
/* ...billing provider, subscriber and payer as for an 837P... */
}]
produces, in part:
ST*837*123456793*005010X223A2~
CLM*DAFFYINST0000000001*700***13:A:1**A*Y*Y~
DTP*434*RD8*20260110-20260110~
CL1*1*7*01~
HI*ABK:S0990XA~
NM1*71*1*FUDD*ELMER****XX*1679576722~
LX*1~
SV2*0450*HC:99283*500*UN*1~
DTP*472*D8*20260110~
LX*2~
SV2*0300**150*UN*1~
A complete, runnable sample is in the portal under Practice Admin → API Checks: Create claim EDI (institutional, 837I). Where each field lands in the 837I is in the 837I field map, and in the 837D in the 837D field map.
Building an 837D
Set claimType to dental and ClaimRev builds an 837D (005010X224A2). As for an 837I, an empty envelop.gs08 is filled in, and one naming another claim type is rejected.
| 837D | Field | Notes |
|---|---|---|
| Place of service, CLM05 | claimEditorView.placeOfService and claimEditorView.claimFreqCode | 11 and 1 give CLM05 = 11:B:1 (office, original). |
| Rendering dentist, 2310B | claimEditorView.renderingProvider | With nm1Data.taxonomyCode for the PRV. |
| Procedure, SV301 | serviceLinesEditorView[].procedureCodeQualifier and procedureCode | AD and the CDT code, such as D2391. |
| Charge, SV302 | serviceLinesEditorView[].lineChargeAmount | |
| Procedure count, SV306 | serviceLinesEditorView[].procedureCount | |
| Tooth, TOO | serviceLinesEditorView[].teethInformation[] | One entry per tooth: toothCode (TOO02) and up to five surfaces, toothSurfaceCode1 to toothSurfaceCode5 (TOO03). TOO01 is always JP (universal numbering). |
| Oral cavity, SV304 | serviceLinesEditorView[].oralCavityDesignationCode1 to 5 | For procedures by area of the mouth rather than tooth. |
"claimType": "dental",
"claims": [{
"claimEditorView": {
"patientControlNumber": "BUGSDENT00000000001",
"totalClaimChargeAmt": "300.00",
"placeOfService": "11",
"claimFreqCode": "1",
"renderingProvider": { "nm1Data": { "entityType": "1", "lastName": "FUDD", "firstName": "ELMER", "npi": "1679576722", "taxonomyCode": "1223G0001X" } }
},
"serviceLinesEditorView": [
{ "lineNumber": 1, "procedureCodeQualifier": "AD", "procedureCode": "D1110", "lineChargeAmount": "120.00",
"procedureCount": "1", "dateService": { "dateValue": "20260110" } },
{ "lineNumber": 2, "procedureCodeQualifier": "AD", "procedureCode": "D2391", "lineChargeAmount": "180.00",
"procedureCount": "1", "teethInformation": [{ "toothCode": "30", "toothSurfaceCode1": "O" }],
"dateService": { "dateValue": "20260110" } }
]
/* ...billing provider, subscriber and payer as for an 837P... */
}]
produces, in part:
ST*837*123456794*005010X224A2~
CLM*BUGSDENT00000000001*300***11:B:1*Y*A*Y*Y~
NM1*82*1*FUDD*ELMER****XX*1679576722~
PRV*PE*PXC*1223G0001X~
LX*1~
SV3*AD:D1110*120****1~
DTP*472*D8*20260110~
LX*2~
SV3*AD:D2391*180****1~
TOO*JP*30*O~
DTP*472*D8*20260110~
A complete, runnable sample is in the portal under Practice Admin → API Checks: Create claim EDI (dental, 837D).
When the patient is not the subscriber (dependents)
The subscriber is the policyholder. The patient is the person who was treated. When they are the same person, send subscriberEditorView only. When the patient is a dependent (a child on a parent's policy, a spouse), the relationship code moves from the subscriber to the patient:
| Patient is the subscriber | Patient is a dependent | |
|---|---|---|
subscriberEditorView.relationshipCode | "18" (self) | Leave it out (or send null or "") |
patientEditorView | Leave it out | Required, with relationshipCode set ("19" child, "01" spouse, "20" employee, "21" unknown, "39" organ donor, "40" cadaver donor, "53" life partner, "G8" other) |
Subscriber address, birthDay, gender | Required (they are the patient's) | Optional |
Patient name, address, birthDay, gender | n/a | Required, on patientEditorView |
How ClaimRev maps these fields to the 837P:
subscriberEditorView.relationshipCodeis written to SBR02 (loop 2000B) exactly as you send it. ClaimRev does not fill in a default. Leave it out and SBR02 is empty, which is what the X12 TR3 requires when there is a patient loop.- A
patientEditorViewwhoserelationshipCodehas a value creates the 2000C/2010CA patient loop. That code becomes PAT01. IfpatientEditorViewhas norelationshipCode, no patient loop is written. - The subscriber's HL04 is set to
1(has a child level) whenever the subscriber relationship code is not18.
Sending "relationshipCode": "18" on the subscriber and a patientEditorView produces SBR02 = 18 next to a patient loop. CreateClaimEdi accepts the request, but the claim is rejected during validation with "It appears the subscriber is the patient, but a patient loop is found". For a dependent, leave the subscriber's code out.
A dependent claim, trimmed to the parts that differ from a self claim:
"subscriberEditorView": {
"payerResponsibility": "P",
"claimFilingIndicator": "MC",
"entityType": "1",
"lastName": "BUNNY",
"firstName": "HONEY",
"subscriberPrimaryIdent": "999000111"
},
"patientEditorView": {
"relationshipCode": "19",
"lastName": "BUNNY",
"firstName": "LOLA",
"address1": "2 Rabbit Hole Rd",
"city": "TOONTOWN",
"state": "CA",
"zip": "90001",
"birthDay": "20200101",
"gender": "F"
}
produces:
HL*2*1*22*1~
SBR*P********MC~
NM1*IL*1*BUNNY*HONEY****MI*999000111~
NM1*PR*2*TOONCARE MEDICAID*****PI*TOONX~
HL*3*2*23*0~
PAT*19~
NM1*QC*1*BUNNY*LOLA~
N3*2 RABBIT HOLE RD~
N4*TOONTOWN*CA*90001~
DMG*D8*20200101*F~
CLM*TOONDEP0000000000002*75***11:B:1*Y*A*Y*Y~
Both shapes are available as complete, runnable samples in the ClaimRev portal under Practice Admin → API Checks: Create claim EDI (self) and Create claim EDI (patient is a dependent). Compare them side by side to see exactly which fields change.
Sending a secondary claim
A secondary claim goes to the second payer after the primary has paid. It carries the primary's adjudication: what it paid and how it adjusted the rest, copied from the primary's 835. There are two shapes, and which one you send depends on how the primary adjusted the claim:
- Claim-level adjustments. The primary's 835 adjusted the claim as a whole (CAS in its 2100 loop). Send the adjustments once, on the claim.
- Service-line adjustments. The primary's 835 adjusted each service line (CAS in its 2110 loop). Most 835s do this. Send each line's paid amount and adjustments on that line.
In both, the top-level subscriber and payer are now the secondary, and the primary moves into secondaryEditorView:
| Field | What to send | 837P |
|---|---|---|
subscriberEditorView.payerResponsibility | "S" (secondary) or "T" (tertiary) | 2000B SBR01 |
subscriberEditorView, payerEditerView | The secondary payer's member and payer ids | 2010BA, 2010BB |
secondaryEditorView[] | One entry per payer that has already adjudicated the claim | 2320 |
.otherSubscriber | The member on the primary's policy. payerResponsibility ("P") and relationshipCode are both required; without them the entry is left off the claim. | 2320 SBR, 2330A |
.payerEditorView | The primary payer. For claim-level adjustments, include adjudicationPaymentDate, the date the primary paid. | 2330B, DTP*573 |
.lineAdjustments[] | Claim-level adjustments only, one entry per group code (CO, PR, OA, PI). Each entry has up to six reason/amount pairs, adjustmentGroup1 to adjustmentGroup6. | 2320 CAS |
.payerPaidAmt | What the primary paid on the whole claim | 2320 AMT*D |
.amountOwed | Optional. What the patient still owes after the primary. | 2320 AMT*EAF |
serviceLinesEditorView[].adjudications[] | Service-line adjustments only. One entry per payer that adjudicated the line. | 2430 |
ClaimRev rejects a secondary claim that does not balance, before it reaches the payer. For the claim-level shape: what the primary paid plus all its claim adjustments equals the claim charge. For the service-line shape: on each line, what the primary paid plus that line's adjustments equals the line charge, and payerPaidAmt equals the sum of the line paid amounts.
Claim-level adjustments
The primary allowed $50.00 of a $75.00 charge (CO-45, $25.00 over the fee schedule), applied $20.00 to the deductible (PR-1) and paid $30.00. $30.00 + $25.00 + $20.00 = $75.00.
"subscriberEditorView": {
"payerResponsibility": "S",
"relationshipCode": "18",
"claimFilingIndicator": "MC",
"entityType": "1",
"lastName": "BUNNY",
"firstName": "LOLA",
"subscriberPrimaryIdent": "999000111"
},
"payerEditerView": {
"organizationName": "Tooncare Medicaid",
"payerIdentifier": "TOONX"
},
"secondaryEditorView": [
{
"otherSubscriber": {
"payerResponsibility": "P",
"relationshipCode": "18",
"policyNumber": "ACMEGRP01",
"claimFilingIndicator": "CI",
"entityType": "1",
"lastName": "BUNNY",
"firstName": "LOLA",
"subscriberPrimaryIdent": "TCH000222333"
},
"payerEditorView": {
"organizationName": "Tooncare Health Plan",
"payerIdentifier": "TOONC",
"adjudicationPaymentDate": { "dateValue": "20260905" }
},
"lineAdjustments": [
{ "adjustmentGroupCode": "CO", "adjustmentGroup1": { "adjustmentReasonCode": "45", "amount": "25.00" } },
{ "adjustmentGroupCode": "PR", "adjustmentGroup1": { "adjustmentReasonCode": "1", "amount": "20.00" } }
],
"payerPaidAmt": { "monetaryAmt": "30.00" },
"amountOwed": { "monetaryAmt": "20.00" },
"benefitsAssignmentCertIndicator": "Y",
"releaseOfInfoCode": "Y"
}
]
produces:
SBR*S*18*******MC~
NM1*IL*1*BUNNY*LOLA****MI*999000111~
NM1*PR*2*TOONCARE MEDICAID*****PI*TOONX~
CLM*TOONSEC0000000000001*75***11:B:1*Y*A*Y*Y~
SBR*P*18*ACMEGRP01******CI~
CAS*CO*45*25~
CAS*PR*1*20~
AMT*D*30~
AMT*EAF*20~
OI***Y***Y~
NM1*IL*1*BUNNY*LOLA****MI*TCH000222333~
NM1*PR*2*TOONCARE HEALTH PLAN*****PI*TOONC~
DTP*573*D8*20260905~
Service-line adjustments
Two lines. On line 1 ($75.00) the primary paid $40.00, wrote off $20.00 (CO-45) and left a $15.00 copay (PR-3). On line 2 ($100.00) it paid nothing: $70.00 went to the deductible (PR-1) and $30.00 was written off (CO-45). secondaryEditorView still names the primary and carries the total paid ($40.00), but has no lineAdjustments and no payment date of its own; those move onto the lines.
"secondaryEditorView": [
{
"otherSubscriber": { /* same as above */ },
"payerEditorView": {
"organizationName": "Tooncare Health Plan",
"payerIdentifier": "TOONC"
},
"payerPaidAmt": { "monetaryAmt": "40.00" },
"amountOwed": { "monetaryAmt": "85.00" },
"benefitsAssignmentCertIndicator": "Y",
"releaseOfInfoCode": "Y"
}
],
"serviceLinesEditorView": [
{
"lineNumber": 1,
"procedureCodeQualifier": "HC",
"procedureCode": "92507",
"modifier1": "GN",
"lineChargeAmount": "75.00",
/* ...the rest of the line... */
"adjudications": [
{
"otherPayerPrimaryIdentifier": "TOONC",
"serviceLinePaidAmt": "40.00",
"productServiceIdQualifier": "HC",
"procedureCode": "92507",
"modifier1": "GN",
"paidServiceUnitCount": "1",
"lineAdjustments": [
{ "adjustmentGroupCode": "CO", "adjustmentGroup1": { "adjustmentReasonCode": "45", "amount": "20.00" } },
{ "adjustmentGroupCode": "PR", "adjustmentGroup1": { "adjustmentReasonCode": "3", "amount": "15.00" } }
],
"dateClaimPaid": { "dateValue": "20260905" },
"amountOwed": { "monetaryAmt": "15.00" }
}
]
},
{
"lineNumber": 2,
"procedureCodeQualifier": "HC",
"procedureCode": "92526",
"modifier1": "GN",
"lineChargeAmount": "100.00",
/* ...the rest of the line... */
"adjudications": [
{
"otherPayerPrimaryIdentifier": "TOONC",
"serviceLinePaidAmt": "0.00",
"productServiceIdQualifier": "HC",
"procedureCode": "92526",
"modifier1": "GN",
"paidServiceUnitCount": "1",
"lineAdjustments": [
{ "adjustmentGroupCode": "PR", "adjustmentGroup1": { "adjustmentReasonCode": "1", "amount": "70.00" } },
{ "adjustmentGroupCode": "CO", "adjustmentGroup1": { "adjustmentReasonCode": "45", "amount": "30.00" } }
],
"dateClaimPaid": { "dateValue": "20260905" },
"amountOwed": { "monetaryAmt": "70.00" }
}
]
}
]
produces, after the 2320/2330 loops:
LX*1~
SV1*HC:92507:GN*75*UN*1*11**1~
DTP*472*D8*20260819~
SVD*TOONC*40*HC:92507:GN**1~
CAS*CO*45*20~
CAS*PR*3*15~
DTP*573*D8*20260905~
AMT*EAF*15~
LX*2~
SV1*HC:92526:GN*100*UN*1*11**1~
DTP*472*D8*20260819~
SVD*TOONC*0*HC:92526:GN**1~
CAS*PR*1*70~
CAS*CO*45*30~
DTP*573*D8*20260905~
AMT*EAF*70~
Things that trip integrations up:
otherPayerPrimaryIdentifiermust match the primary'spayerIdentifierinsecondaryEditorView[].payerEditorView. That is how the payer ties each line's adjudication to the right prior payer. A line adjudication without it is left off the claim.- Copy the procedure code and modifiers the primary adjudicated, from its 835 SVC segment. If the primary paid under a different code than you billed, send the code it paid under.
- One
lineAdjustmentsentry per group code. Two CO reasons on one line go in the same entry, asadjustmentGroup1andadjustmentGroup2, not as two CO entries. - Do not send the same adjustments at both levels. Use claim-level
lineAdjustmentsor line-leveladjudications, as the primary's 835 did, never both for the same dollars. - The qualifiers fill themselves in.
amountQualCode(D,EAF) anddateQualifier(573) can be left out; ClaimRev sets them.
Both shapes are complete, runnable samples under Practice Admin → API Checks: Create claim EDI (secondary, claim-level adjustments) and Create claim EDI (secondary, service-line adjustments).
Creating a Claim from Form Boxes does not carry the primary's adjudication into the 837, so a secondary claim cannot be sent that way. Use CreateClaimEdi, or upload your own 837 with the 2320 and 2430 loops (Path A).
What happens after submission
Once an 837 is in the system:
- Parse and validate. ClaimRev parses the EDI. Structural problems are surfaced in the file's
exceptionMessageandfileStatusName(visible via GetFileList). - Route to the payer. Each claim is routed to the appropriate payer using the master payer list (with any account-level overrides applied).
- Batched send. Claims are not transmitted to payers individually as they arrive — they're sent on a regular processing schedule. Don't expect transmission to be instant after upload.
- 999 functional ack. Generated by the clearinghouse and reflected on the file record (
ackStatusNameonGetFileList). Typically available shortly after the file is processed. - 277CA claim acknowledgment. Arrives the next day or so. If the payer responds within the window, the 277CA contains the payer's specific acceptance/rejection details. If the payer is silent, ClaimRev generates a generic 277CA so your downstream workflow isn't blocked. Status is reflected on
claimAckStatusName. - 835 ERA. Days to weeks later, when the payer adjudicates. See Retrieving ERAs.
Downloading the 999 / 277CA bytes
The status fields (ackStatusName, claimAckStatusName) tell you whether an ack has been received, but if you want the raw 999 or 277CA EDI itself, the bytes are available via a legacy ClaimRev API — the same one the customer portal uses for its file-search/download UI. This endpoint is not yet published to the developer-facing Swagger; ask ClaimRev support for access. A clean v1 endpoint to retrieve ack files by input-file id is on the roadmap.
4. Eventually the payer adjudicates and returns an 835 ERA — see Retrieving ERAs.
To see where your claim is in this lifecycle, see Checking Claim Status.
Limits
The endpoint accepts files of normal claim batch sizes. There's no documented hard cap today. If you're submitting unusually large files (thousands of claims in a single envelope) or running into upload size errors, contact ClaimRev support.