Tables and columns¶
oxedi reads every 835 into the same five tables. This page shows how they link and lists every column, with its type, the element it comes from and what it holds.
New to the 835? The 835 in five minutes explains payments, claims and adjustments first.
How the tables link¶
erDiagram
payments ||--o{ claims : "payment"
payments ||--o{ provider_adjustments : "payment"
claims ||--o{ services : "claim"
claims ||--o{ adjustments : "claim"
services ||--o{ adjustments : "service"
rownumbers the rows of a table, from 0.payment,claimandservicehold therowof the parent.- A service joins to its claim on
services.claim = claims.row. - An adjustment of a claim has a null
service. An adjustment of a service line has the line'srowinservice. - Children also carry the
rowof every table above them. A service has bothclaimandpayment.
Several files
Row numbers restart in every file. When you combine files, join on the file name as well as the row. The Polars and lakehouse recipes show how.
Details: row numbers in one file
row counts across the whole file, not per transaction. In a file with two payments, the
claims of the second payment carry on from the last claim of the first.
Columns every table has¶
| Column | Type | Holds |
|---|---|---|
row |
int64 | The row's number in its table, from 0. |
segment |
int64 | The index of the segment the row starts at: its CLP for a claim, its CAS for an adjustment. result.document[segment] gives that segment. |
payment |
int64 | The row of the payment the row belongs to. Not in payments. |
claim |
int64 | The row of the claim. In services and adjustments. |
service |
int64 | The row of the service line, or null for a claim adjustment. In adjustments. |
The tables list these columns first and the others in name order.
Types¶
| Type here | Python (Arrow) | Polars | DuckDB |
|---|---|---|---|
| text | binary |
Binary |
VARCHAR (BLOB with binary := true) |
| decimal(38, 2) | decimal128(38, 2) |
Decimal(38, 2) |
DECIMAL(38,2) |
| decimal(38, 4) | decimal128(38, 4) |
Decimal(38, 4) |
DECIMAL(38,4) |
| date | date32 |
Date |
DATE |
| int64 | int64 |
Int64 |
BIGINT |
- Text keeps the bytes of the file. Cast to a string type to read or group by it.
- A text element the segment does not have is null. One it has but leaves empty is
"". - In the other types, a value that is missing, empty or does not parse is null. The parse reports a value that does not parse as a diagnostic.
How to read the source column¶
BPR02is element 2 of theBPRsegment.SVC01-2is component 2 of element 1 ofSVC.of NM1*QCmeans theNM1segment whose first element isQC. A claim has severalNM1segments, and this one is the patient.- When a segment repeats where the table reads it, the column takes the first one.
The glossary explains each segment id. The spec defines all of this, and you can change it: see Customizing the spec.
payments¶
One row per payment, which is one transaction (ST to SE). The payer is loop 1000A and the
payee loop 1000B.
| Column | Type | Source | Holds |
|---|---|---|---|
handling_code |
text | BPR01 |
The transaction handling code. |
total_payment_amount |
decimal(38, 2) | BPR02 |
The total actual payment to the provider. |
credit_debit_flag |
text | BPR03 |
Whether the amount is a credit (C) or a debit (D). |
payment_method |
text | BPR04 |
The payment method code, such as CHK or ACH. |
payment_format |
text | BPR05 |
The payment format code. |
payment_date |
date | BPR16 |
The check issue date or the EFT effective date. |
trace_number |
text | TRN02 |
The check or EFT trace number. |
payer_identifier |
text | TRN03 |
The payer identifier of the trace. |
receiver_id |
text | REF02 of REF*EV |
The receiver identification. |
production_date |
date | DTM02 of DTM*405 |
The production date. |
payer_name |
text | N102 of N1*PR |
The payer's name. |
payer_id_qualifier |
text | N103 of N1*PR |
The kind of id in payer_id. |
payer_id |
text | N104 of N1*PR |
The payer's identification code. |
payer_address |
text | N301 in loop 1000A |
The payer's address. |
payer_city |
text | N401 in loop 1000A |
The payer's city. |
payer_state |
text | N402 in loop 1000A |
The payer's state or province code. |
payer_zip |
text | N403 in loop 1000A |
The payer's postal code. |
payer_technical_contact_name |
text | PER02 of PER*BL |
The name of the payer's technical contact. |
payer_technical_contact_qualifier |
text | PER03 of PER*BL |
The kind of number in payer_technical_contact_number. |
payer_technical_contact_number |
text | PER04 of PER*BL |
The technical contact's communication number. |
payee_name |
text | N102 of N1*PE |
The payee's name. |
payee_id_qualifier |
text | N103 of N1*PE |
The kind of id in payee_id. |
payee_id |
text | N104 of N1*PE |
The payee's identification code. |
payee_address |
text | N301 in loop 1000B |
The payee's address. |
payee_city |
text | N401 in loop 1000B |
The payee's city. |
payee_state |
text | N402 in loop 1000B |
The payee's state or province code. |
payee_zip |
text | N403 in loop 1000B |
The payee's postal code. |
4010 files
The 4010 spec has no payer technical contact (PER*BL), so on 4010 files the
payer_technical_contact_* columns are usually null.
claims¶
One row per claim (CLP, loop 2100).
| Column | Type | Source | Holds |
|---|---|---|---|
claim_id |
text | CLP01 |
The claim submitter's identifier: the provider's id for the claim. |
claim_status |
text | CLP02 |
The claim status code. See claim status. |
charge_amount |
decimal(38, 2) | CLP03 |
The total charge of the claim. |
payment_amount |
decimal(38, 2) | CLP04 |
The amount paid for the claim. |
patient_responsibility |
decimal(38, 2) | CLP05 |
The patient responsibility amount. |
filing_indicator |
text | CLP06 |
The claim filing indicator code. |
payer_control_number |
text | CLP07 |
The payer's claim control number. |
facility_type |
text | CLP08 |
The facility type code. |
frequency_code |
text | CLP09 |
The claim frequency type code. |
drg_code |
text | CLP11 |
The DRG code. |
drg_weight |
decimal(38, 4) | CLP12 |
The DRG weight. |
discharge_fraction |
decimal(38, 4) | CLP13 |
The discharge fraction. |
header_number |
int64 | LX01 |
The number of the LX group (loop 2000) the claim is in. |
patient_last_name |
text | NM103 of NM1*QC |
The patient's last name. |
patient_first_name |
text | NM104 of NM1*QC |
The patient's first name. |
patient_id_qualifier |
text | NM108 of NM1*QC |
The kind of id in patient_id. |
patient_id |
text | NM109 of NM1*QC |
The patient's identification code. |
rendering_provider_entity_type |
text | NM102 of NM1*82 |
The entity type qualifier of the service provider. |
rendering_provider_name |
text | NM103 of NM1*82 |
The service provider's last or organization name. |
rendering_provider_id_qualifier |
text | NM108 of NM1*82 |
The kind of id in rendering_provider_id. |
rendering_provider_id |
text | NM109 of NM1*82 |
The service provider's identification code. |
statement_from |
date | DTM02 of DTM*232 |
The first date of the claim's statement period. |
statement_to |
date | DTM02 of DTM*233 |
The last date of the claim's statement period. |
coverage_amount |
decimal(38, 2) | AMT02 of AMT*AU |
The coverage amount. |
group_number |
text | REF02 of REF*1L |
The group number. |
services¶
One row per service line (SVC, loop 2110).
| Column | Type | Source | Holds |
|---|---|---|---|
procedure_qualifier |
text | SVC01-1 |
The kind of code in procedure_code, such as HC. |
procedure_code |
text | SVC01-2 |
The procedure or service code. |
charge_amount |
decimal(38, 2) | SVC02 |
The line item charge. |
payment_amount |
decimal(38, 2) | SVC03 |
The amount paid for the line. |
units_paid |
decimal(38, 2) | SVC05 |
The units of service paid. |
original_units |
decimal(38, 2) | SVC07 |
The original units of service. |
service_date |
date | DTM02 of DTM*472 |
The service date. |
line_item_control_number |
text | REF02 of REF*6R |
The line item control number. |
allowed_amount |
decimal(38, 2) | AMT02 of AMT*B6 |
The allowed amount. |
adjustments¶
One row per adjustment. A CAS segment holds up to six adjustments of one group: the reason,
amount and quantity of each sit in three elements in a row. Each becomes its own row.
| Column | Type | Source | Holds |
|---|---|---|---|
group_code |
text | CAS01 |
The claim adjustment group code, such as CO or PR. See group code. |
reason_code |
text | CAS02, CAS05, … |
The adjustment reason code (a CARC). |
amount |
decimal(38, 2) | CAS03, CAS06, … |
The adjustment amount. |
quantity |
decimal(38, 2) | CAS04, CAS07, … |
The adjustment quantity. |
provider_adjustments¶
One row per provider adjustment. A PLB segment holds up to six adjustments for one provider
and fiscal period: an identifier and an amount each. Each becomes its own row.
| Column | Type | Source | Holds |
|---|---|---|---|
provider_id |
text | PLB01 |
The provider identifier. |
fiscal_period_date |
date | PLB02 |
The fiscal period date. |
reason_code |
text | PLB03-1, PLB05-1, … |
The provider adjustment reason code. |
reference_id |
text | PLB03-2, PLB05-2, … |
The reference identification of the adjustment. |
amount |
decimal(38, 2) | PLB04, PLB06, … |
The provider adjustment amount. |
Adjustments that share a segment came from the same PLB. The writer keeps them together, as
Writing from your own tables explains.
diagnostics (DuckDB only)¶
In DuckDB, read_835(..., table_name := 'diagnostics') returns the findings of the parse as a
sixth table, one row per finding:
| Column | Type | Holds |
|---|---|---|
level |
BIGINT |
The SNIP level of the rule: 1, 2 or 3. |
kind |
VARCHAR |
The name of the rule, such as CodeNotInList. |
rule |
VARCHAR |
The rule as a sentence with its values. |
segment |
BIGINT |
The index of the segment at fault. |
element, component |
BIGINT |
The 1-based position in the segment, when there is one. |
path |
VARCHAR |
The loops open at that segment. |
datum |
VARCHAR |
The offending value. |
origin, code |
VARCHAR |
Who reported it, and that reporter's own code. |
In Python the same fields are on each oxedi.Diagnostic. See
Diagnostics.
See the columns of any table¶
The spec decides the columns, so a patched spec can add some. To list them:
import oxedi
result = oxedi.parse_file("remittance.835")
print(result.tables["services"].columns)
['row', 'segment', 'payment', 'claim', 'allowed_amount', 'charge_amount', 'line_item_control_number', 'original_units', 'payment_amount', 'procedure_code', 'procedure_qualifier', 'service_date', 'units_paid']
DESCRIBE SELECT * FROM read_835('remittance.835', table_name := 'services');
A schema that does not need a file is tracked in #138.