eqlx-ebelge
Read Turkish e-Fatura (UBL-TR) and e-Defter (XBRL GL) files: clean data, a trial balance (mizan), and consistency checks that catch wrong totals, invalid tax ids, duplicate invoices and unbalanced journal entries. An optional MCP server lets AI agents such as Claude use the same functions, read-only.
Built by eqlx. Part of eqlx.org, open research and tools.
Status: alpha (0.1.1). Tested on synthetic documents and checked against GİB's public sample packages (e-Fatura, e-Arşiv, e-Defter): on those samples it raises no false alarms, and the findings it does raise are real inconsistencies in the samples. Please report any real-world file it reads wrongly (without sending the file itself).
Why
Lenders, factoring companies and finance teams receive invoices and ledgers as GİB XML and still read them by hand or through paid tools. This library turns them into data you can check and analyse, locally, with no network access.
Install
pip install "eqlx-ebelge[mcp] @ git+https://github.com/eqlx-ai/eqlx-ebelge"
Python 3.10+. The core needs only defusedxml; [mcp] adds the MCP server.
Command line
ebelge sample demo # write 10 synthetic files to try it on
ebelge check demo # every check, on files, folders or ZIP archives
ebelge invoices demo --csv # one row per invoice
ebelge mizan demo # trial balance from e-Defter journals (add --level sub)
$ ebelge check demo
10 documents: 9 invoices, 1 ledger files (7 entries). 2 errors, 1 warnings, 0 info.
severity code file message
error payable fatura_08_toplam_hatali.xml TaxInclusiveAmount - withholding + rounding = 4682.88, PayableAmount is 99999.99 (gap 95317.11)
error tax_id_check_digit fatura_09_vkn_hatali.xml supplier VKN 4477127821 fails its check digit
warning possible_duplicate fatura_02.xml supplier 4477127820, same customer, 2026-10-07, payable 10140.12: EQX2026000000002 (fatura_02.xml), EQX2026000000007 (fatura_07.xml)
$ ebelge mizan demo
account name debit credit debit_bal credit_bal
100 KASA 10000.00 8500.00 1500.00 0
102 BANKALAR 560000.00 10000.00 550000.00 0
120 ALICILAR 90000.00 60000.00 30000.00 0
...
ebelge check exits with code 1 when it finds an error, so it can gate a pipeline.
Python
from pathlib import Path
import eqlx_ebelge as eb
inv = eb.read_invoice(Path("fatura.xml").read_bytes(), "fatura.xml")
inv.supplier.tax_id, inv.totals.payable, [line.name for line in inv.lines]
eb.check_invoice(inv) # list of Finding(code, severity, message, ...)
report = eb.scan(["inbox/", "archive.zip"]) # every document, every check, duplicates across all
report.summary()
ledgers = [eb.read_ledger(p.read_bytes(), p.name) for p in sorted(Path("edefter").glob("*.xml"))]
rows = eb.trial_balance(ledgers) # e.g. twelve monthly journals -> one year's mizan
Amounts are Decimal, never floats.
Checks
| Code | Severity | What it catches |
|---|---|---|
tax_id_check_digit, tax_id_length, tax_id_missing |
error | VKN or TCKN that fails its check digit, has the wrong length, or is missing |
lines_total |
error | invoice lines do not add up to LineExtensionAmount |
tax_total, tax_inclusive |
error | tax subtotals or the tax-inclusive total do not add up |
payable |
error above 1 TRY, else warning | payable amount differs from tax-inclusive total minus withholding (tevkifat) |
line_amount, tax_rate |
warning | quantity x price - discount, or base x rate, differs from what the line or tax says |
number_format, number_year, uuid_format |
warning | invoice number is not 3 characters + year + 9 digits, its year differs from the date, malformed ETTN |
duplicate_uuid, duplicate_number |
error | the same ETTN, or the same supplier and number, appears more than once |
possible_duplicate |
warning | same supplier, customer, date and amount under different numbers |
entry_unbalanced, entry_total, ledger_unbalanced |
error | a journal entry, its header totals, or the whole file do not balance |
counter_repeat / counter_gap |
error / warning | journal entry counters repeat or skip |
date_outside_period |
warning | an entry dated outside the period the file covers |
placeholder_tckn, same_party, account_code, unsupported |
info | context worth knowing |
unreadable |
error | a file that is malformed or unsafe to parse |
Tolerances: one kuruş per comparison, plus half a kuruş per line where per-line rounding adds up.
MCP server (for Claude and other agents)
The server exposes four read-only tools: list_documents, check, read_invoices and
trial_balance. It reads only inside the folder you give it; .., absolute paths and symlinks that
point outside are refused.
Claude Code:
claude mcp add ebelge -- ebelge mcp --root /path/to/invoices
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"ebelge": { "command": "ebelge", "args": ["mcp", "--root", "/path/to/invoices"] }
}
}
Then ask, for example: "Bu klasördeki faturaları kontrol et, sorunlu olanları ve mükerrerleri göster."
Safety and privacy
- Runs locally. No network access, no telemetry.
- XML with a DOCTYPE is refused, which blocks entity-expansion and external-entity (XXE) attacks.
- ZIP archives are read in memory with limits on size, file count and compression ratio.
- From e-Defter it keeps only what the checks and the trial balance need; entry comments, document numbers and people's names are not read.
- Tests use synthetic documents only.
ebelge samplewrites the same kind of made-up files.
See LIMITS.md for what it does not do.
Development
uv sync --all-extras
uv run pytest
uv run ruff check . && uv run ruff format --check . && uv run mypy src
Contributions are welcome; see CONTRIBUTING.md.
License
Apache-2.0. See LICENSE and NOTICE. GİB schemas are not included.
Metadata
Release files for eqlx-ebelge 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| eqlx_ebelge-0.1.1.tar.gz | 31.0 kB | With provenance | Details for eqlx_ebelge-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| eqlx_ebelge-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for eqlx_ebelge-0.1.1-py3-none-any.whl |
Total release size: 66.4 kB
Release files / eqlx_ebelge-0.1.1.tar.gz
| Download URL | eqlx_ebelge-0.1.1.tar.gz |
|---|---|
| Size | 31.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
882a8f52220b7acc4e9875c66e329e825dbfb2f50ef0e8d198ebc9d57a385010
|
|
BLAKE2b-256 checksum How to use checksums |
920a1f7e99cc5fe68a2910ecc4575fa07e9b36827749d1c8dbb254dd2bd9fb70
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logRelease files / eqlx_ebelge-0.1.1-py3-none-any.whl
| Download URL | eqlx_ebelge-0.1.1-py3-none-any.whl |
|---|---|
| Size | 35.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e92306427b824ef161b29f841b946b146de7aa83cfff3ec032d7b6cf74536216
|
|
BLAKE2b-256 checksum How to use checksums |
efbe4e4d8f924dde9a9eed9c68d5b4f707967735b0bfc34a59b1e168bac56d94
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logSecurity for eqlx-ebelge 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for eqlx_ebelge-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.