Metadata-Version: 2.4
Name: x12sdk
Version: 1.1.0
Summary: Typed Pydantic models and a streaming SDK/CLI for HIPAA ASC X12 5010 health care transactions (837P, 837I, 835, 834, 270, 271, 276, 277).
Author: owgreen-dev
License: Apache-2.0
Project-URL: Homepage, https://github.com/owgreen-dev/x12sdk
Project-URL: Repository, https://github.com/owgreen-dev/x12sdk
Project-URL: Issues, https://github.com/owgreen-dev/x12sdk/issues
Project-URL: Changelog, https://github.com/owgreen-dev/x12sdk/blob/main/CHANGELOG.md
Project-URL: Upstream (LinuxForHealth x12), https://github.com/LinuxForHealth/x12
Keywords: x12,edi,hipaa,837,835,834,270,271,276,277,claims,pydantic
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Intended Audience :: Healthcare Industry
Classifier: Intended Audience :: Developers
Classifier: Topic :: Office/Business :: Financial
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: pydantic<3,>=2
Requires-Dist: pydantic-settings>=2
Requires-Dist: python-dotenv>=0.19.0
Provides-Extra: pandas
Requires-Dist: pandas>=2.0; extra == "pandas"
Provides-Extra: dev
Requires-Dist: pandas>=2.0; extra == "dev"
Requires-Dist: pytest>=7.1; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: pre-commit>=2.14; extra == "dev"
Requires-Dist: hypothesis>=6; extra == "dev"
Dynamic: license-file

# x12sdk

Typed [Pydantic v2](https://docs.pydantic.dev/) models and a streaming SDK/CLI
for HIPAA ASC X12 5010 health care transactions.

![License](https://img.shields.io/github/license/owgreen-dev/x12sdk)
![CI](https://github.com/owgreen-dev/x12sdk/actions/workflows/continuous-integration.yml/badge.svg)
![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)

> x12sdk is the maintained continuation of
> [LinuxForHealth x12](https://github.com/LinuxForHealth/x12), which stopped at
> 0.57.0 in June 2022. It runs on **Pydantic v2 and Python 3.10–3.13**.

Supported transaction sets:

| Set | Implementation | What it is |
|---|---|---|
| 837P | 005010X222A2 | Professional claim |
| 837I | 005010X223A3 | Institutional claim |
| 835 | 005010X221A1 | Claim payment / remittance advice |
| 834 | 005010X220A1 | Benefit enrollment and maintenance |
| 270 / 271 | 005010X279A1 | Eligibility inquiry / response |
| 276 / 277 | 005010X212 | Claim status inquiry / response |

Every transaction is parsed into a validated Pydantic model and can be
serialized back to X12; the test suite asserts that round trip reproduces
each sample file byte for byte.

## Install

```shell
pip install x12sdk
```

From source:

```shell
git clone https://github.com/owgreen-dev/x12sdk
cd x12sdk
python3 -m venv .venv && source .venv/bin/activate
pip install --upgrade pip
pip install -e .
```

## SDK

The `x12sdk.io` module streams either raw segments or validated transaction
models from a file.

Stream segments (each segment becomes its name plus a list of fields):

```python
from x12sdk.io import X12SegmentReader

with X12SegmentReader("/home/edi/270.x12") as r:
    for segment_name, segment_fields in r.segments():
        print(segment_name, segment_fields)
```

Stream models (the payload is validated; one model per transaction set):

```python
from x12sdk.io import X12ModelReader

with X12ModelReader("/home/edi/270.x12") as r:
    for model in r.models():
        print(model.header)   # common attributes: header, footer
        print(model.footer)
        model.x12()           # serialize back to X12
```

## Reaching the claims

The models mirror the X12 loop hierarchy, so a claim is several levels down.
On an 837 it is down one of *two* paths, because a claim sits under the
subscriber when the patient is the subscriber and under a dependent when they
are not:

```
loop_2000a[i].loop_2000b[j].loop_2300[k]                  patient = subscriber
loop_2000a[i].loop_2000b[j].loop_2000c[l].loop_2300[k]    patient = dependent
```

Both are ordinary. Code written against one runs happily on a file that uses
the other and reports no claims at all, so `claims()` walks both and yields a
flat record. It is a generator, so a large file is never materialized.

```python
for claim in model.claims():
    print(claim.patient_control_number, claim.charge, claim.patient_name)
```

Each record carries the claim plus the context you would otherwise re-derive:
`billing_provider`, `subscriber`, `payer`, `patient`, `is_dependent` and
`relationship`. `patient` already points at whoever was treated, so you never
need to know which branch the claim came from. `subscribers()` yields the
subscribers and their dependents.

`claims()` on an 835 yields the claim payments, each with `charge`, `paid`,
`status`, `adjustments`, `service_lines` and the LX `header_number`.

## CLI

```shell
x12sdk --help
usage: x12sdk [-h] [-s | -m] [-x] [-p] [-d] file

The x12sdk CLI parses and validates X12 messages.
Messages are returned in JSON format in either a segment or transactional format.

positional arguments:
  file              The path to a ASC X12 file

options:
  -h, --help        show this help message and exit
  -s, --segment     Returns X12 segments
  -m, --model       Returns X12 models
  -x, --exclude     Exclude fields set to None in model output
  -p, --pretty      Pretty print output
  -d, --delimiters  Include X12 delimiters in output (model mode only)
```

```shell
x12sdk -s -p demo-file/demo.270   # segments
x12sdk -m -p demo-file/demo.270   # models
```

## Writing X12

The transaction models cover ST through SE. `write_transactions` adds the
interchange and functional group envelopes and keeps the control numbers
consistent, so you get a file a trading partner would accept.

```python
from x12sdk.io import X12ModelReader, write_transactions

with X12ModelReader("in.835") as reader:
    transactions = list(reader.models())

out = write_transactions(transactions, sender_id="SENDERID", receiver_id="RECEIVERID")
```

## Generating synthetic files

Real claims and remittances contain PHI, and there is no public X12 corpus to
test against. `x12sdk.generate` builds valid transactions from the same models
the parser produces, so your test data is guaranteed synthetic.

```python
from x12sdk.generate import generate_835

remittance = generate_835(seed=7, claims=25)   # a complete file, envelope included
```

The same seed always produces the same bytes, and generation never touches the
global random state, so it is safe inside someone else's test suite.

To build a specific scenario, describe it:

```python
from x12sdk.generate import ClaimSpec, ServiceLineSpec, denial, generate_835

spec = [
    ClaimSpec(
        charge="900.00",
        lines=[ServiceLineSpec(charge="900.00", procedure="99214",
                               adjustments=[denial("CO", "97", "300.00")])],
    )
]
remittance = generate_835(seed=1, claims=spec, payer_name="EXAMPLE HEALTH PLAN")
```

A claim's payment is derived as charge minus adjustments, so a specification
that would break the 835 balance rule cannot be written down.

Claim submissions work the same way:

```python
from x12sdk.generate import generate_837p

submission = generate_837p(seed=7, claims=25)
```

In an 837 a claim sits under the subscriber when the patient is the
subscriber, and under a dependent when they are not. Code that walks the
hierarchy often handles only the first, so generated files contain both by
default. Set `dependent_rate` to choose the mix, or pass a `SubmissionSpec`
to place each claim yourself:

```python
from x12sdk.generate import (
    ClaimSpec, PatientSpec, ServiceLineSpec, SubmissionSpec, generate_837p
)

spec = SubmissionSpec(
    patients=[
        PatientSpec(
            claims=[ClaimSpec(charge="450.00",
                              lines=[ServiceLineSpec(charge="450.00",
                                                     procedure="99214")])],
            dependent=True,
            relationship="19",   # child
        )
    ]
)
submission = generate_837p(seed=1, claims=spec)
```

## Denial analytics

An 835 tells you what a payer did to a claim, but in a shape built for
transmission: adjustments nested at claim and service line level, up to six
reason/amount pairs per CAS segment, remark codes in a different segment
again. `x12sdk.denials` flattens that to one record per reason code and
aggregates it the way a recovery or program integrity analyst asks the
question.

```python
from x12sdk.io import X12ModelReader
from x12sdk.denials import denial_summary, iter_adjustments

with X12ModelReader("remit.835") as reader:
    for transaction in reader.models():
        rows = list(iter_adjustments(transaction))
        for row in denial_summary(rows):
            print(row.payer_name, row.group_code, row.reason_code,
                  row.category, row.claim_count, row.total_amount)
```

`denial_summary` counts payer-side groups (`CO`, `OA`, `PI`) by default and
leaves out patient cost share (`PR`), because a deductible is not a denial;
pass `include_patient_responsibility=True` to keep it. Amounts stay `Decimal`,
so totals are exact. `claim_count` counts distinct claims, so a reason hitting
three lines of one claim counts once.

For DataFrame work, install the extra and use `to_dataframe`:

```shell
pip install 'x12sdk[pandas]'
```

### Code lists

CARC and RARC **descriptions** are published by X12 and the Washington
Publishing Company and are licensed separately, so **x12sdk ships none of that
text**. What it ships is `categorize()`, x12sdk's own grouping of reason codes
into analysis categories such as `eligibility`, `authorization`, `duplicate`
and `timely_filing`, with anything unmapped resolving to `other`.

If you need the official wording, obtain the list from
[x12.org/codes](https://x12.org/codes) under whatever licence applies to you
and load it yourself:

```python
from x12sdk.denials import describe, load_code_descriptions

descriptions = load_code_descriptions("carc.csv")   # your file, not ours
for row in describe(denial_summary(rows), descriptions):
    print(row["reason_code"], row["description"], row["total_amount"])
```

## Migrating from `linuxforhealth-x12`

| before | after |
|---|---|
| `pip install linuxforhealth-x12` | `pip install x12sdk` |
| `from linuxforhealth.x12.io import X12ModelReader` | `from x12sdk.io import X12ModelReader` |
| `lfhx12 -m -p file.x12` | `x12sdk -m -p file.x12` |
| `lfhx12-api` (FastAPI endpoint) | removed; wrap the SDK in your own service |

See [CHANGELOG.md](CHANGELOG.md) for everything that changed.

## Development

```shell
pip install -e ".[dev]"
ruff check src
pytest --cov
```

Contributions are welcome; see [CONTRIBUTING.md](CONTRIBUTING.md) (Apache-2.0,
DCO sign-off, no copyrighted standards text, no real PHI). To add a
transaction set, see [repo-docs/NEW_TRANSACTION.md](repo-docs/NEW_TRANSACTION.md);
the design is described in [repo-docs/DESIGN.md](repo-docs/DESIGN.md).

## Provenance and related work

x12sdk is a fork of **[LinuxForHealth x12](https://github.com/LinuxForHealth/x12)**
by Dixon Whitmire and the LinuxForHealth contributors (IBM), released under
the Apache License 2.0. The models, parser, readers, and test corpus
originate there; x12sdk exists to keep that work usable on current Python
and Pydantic. The original LICENSE is retained, and attribution and
trademark notes are in [NOTICE](NOTICE) and [TRADEMARK.md](TRADEMARK.md).
x12sdk is not affiliated with or endorsed by IBM, LinuxForHealth, or the
Linux Foundation.

- **[MdClarity/x12](https://github.com/MdClarity/x12)** — an independent
  fork by MD Clarity (Cary Lee) that completed a Pydantic v2 migration and
  added type checking and fuzzing in 2026. x12sdk's port is written
  separately from the 2022 upstream; their work is acknowledged here and
  their fixes are welcome upstream in x12sdk.
- **[pyx12](https://github.com/azoner/pyx12)** — the long-standing Python X12
  validator/converter (XML/dict output, map-driven). Choose pyx12 for
  validation against X12 maps; choose x12sdk for typed Python models.
- **[edi-835-parser](https://github.com/keiron-stoddart/edi-835-parser)** —
  a popular 835-only parser with pandas output.

## License

Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
