Metadata-Version: 2.4
Name: docfig
Version: 0.1.0
Summary: Free offline bank statement reconciliation check (CSV/Excel), plus the DocFig API client: statement PDFs to checked JSON, Excel and CSV.
Author-email: DocFig <hello@docfig.com>
License-Expression: MIT
Project-URL: Homepage, https://docfig.com/api
Project-URL: Documentation, https://docfig.com/api
Project-URL: API reference (OpenAPI), https://docfig.com/v1/openapi.json
Keywords: bank statement,reconciliation,csv,fintech,credit model,data quality,bank statement converter,pdf to excel,bookkeeping,api
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business :: Financial :: Accounting
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25
Provides-Extra: excel
Requires-Dist: openpyxl>=3.0; extra == "excel"
Dynamic: license-file

# docfig

Check bank statement transaction tables for reconciliation errors, offline and free. Convert statement PDFs to checked JSON, Excel and CSV with the [DocFig API](https://docfig.com/api).

```bash
pip install docfig            # add [excel] to read .xlsx files: pip install "docfig[excel]"
```

## Free reconciliation check

Scraped or exported bank statements often have corrupted tables: a debit in the credit column, a dropped row, a row copied twice, a misread amount. `docfig check` finds them. No API key, no network, no cost.

```bash
docfig check statement.csv
docfig check statement.csv --opening 2841.17 --closing 11023.40
docfig check statement.csv --json      # for scripts and CI
```

```
docfig check: statement.csv
  27 rows, 26 balance checks. Passed: 24, flagged: 3.
  Opening 2841.17 (inferred), closing 11023.40 (inferred). Money in 11501.94, money out 3434.66.

  3 problems:
  - [row 9] sign_error: Row 9: 150.88 is in the wrong column; the balance says it is a debit.
    Fix: Move 150.88 to the debit column.
  - [row 15] duplicate: Row 15 repeats row 14: same date, description, amount and balance.
    Fix: Delete row 15 if it was copied or exported twice.
  - [row 20] amount_typo: Row 20: the balance says this amount should be -180.80, not -18.08 (a zero looks dropped or added).
    Fix: Amount 18.08 likely 180.80.

Starting from PDFs? docfig.com/api
```

What it checks, row by row:

- **Running balance**: every balance must equal the previous balance plus the rows in between (in integer cents, so 0.1 + 0.2 never fails). Oldest-first or newest-first order is detected from the balances.
- **Swapped debit/credit columns** for the whole file, and **sign errors** or figures in the wrong column on single rows.
- **Mistyped amounts**: "amount 125.00 likely 1,250.00" (a dropped zero, a misplaced decimal point, two swapped digits, one misread digit).
- **Missing rows**: a gap in the balance chain, with the amount that is missing and where.
- **Duplicated rows**, **dates out of order** (including day and month swapped), **totals** against `--opening` and `--closing`.

It reads what real exports look like: headers such as Date / Narration / Withdrawal Amt. / Deposit Amt. / Closing Balance, a signed Amount column or separate Debit and Credit columns, a Dr/Cr column, `CR`/`DR` suffixes, `(1,234.56)` negatives, `£-12.50`, decimal commas (`1.234,56`), Indian lakh grouping (`1,23,456.78`), `;` or tab delimiters, preamble lines above the header, balance brought forward lines, and dates as `2026-03-31`, `31/03/2026`, `03/31/2026`, `31.03.2026`, `31-Mar-26` or `Mar 31, 2026`. A credit card table, where the balance is the amount owed, takes `--type card`.

Exit codes: `0` clean, `1` problems found, `2` the file could not be read as a transactions table. The same logic powers the checks on [docfig.com](https://docfig.com), and the Node package (`npm install docfig`) gives identical results.

From Python:

```python
import docfig

report = docfig.check_file("statement.csv", opening="2841.17")
print(report["status"], report["flagged"])
for p in report["problems"]:
    print(p["line"], p["kind"], p["message"], p["fix"])
print(docfig.format_report(report))
```

## Convert statement PDFs (DocFig API)

Get a free key at [docfig.com/api](https://docfig.com/api) (email code, no card): 100 pages a month. Text PDFs, scanned PDFs, password-protected PDFs and photos (JPG, PNG, HEIC).

```python
import docfig

client = docfig.Client()                      # reads DOCFIG_API_KEY
st = client.convert("statement.pdf")          # upload, wait, return the result
print(st.bank, st.account["last4"], st.period)
print(st.checks["status"], st.checks["rows_flagged"])
for t in st.transactions:
    print(t["date"], t["description"], t["amount"], t["balance"], t["flags"])
st.download("statement.xlsx")                 # the same workbook as the website, or .csv

locked = client.convert("locked.pdf", password="1234")

combined = client.combine(["jan.pdf", "feb.pdf", "apr.pdf"])
for p in combined.problems:
    print(p["kind"], p["text"])               # "Missing statement: 1 Mar 2026 to 31 Mar 2026 ..."
combined.download("all-statements.xlsx")

print(client.usage())                         # pages used and left this month
```

`client.upload(file)` returns at once; `client.get(id)`, `client.wait(id)`, `client.download(id, "csv")` and `client.delete(id)` work with the id. Results are deleted from DocFig an hour after upload.

From the shell: `docfig convert statement.pdf -o statement.xlsx` (or `.csv`, `.json`).

### Errors

All errors derive from `docfig.DocFigError` and carry `.code` and `.status`:

| Exception | When |
|---|---|
| `AuthenticationError` | missing, invalid or revoked key |
| `PasswordRequiredError`, `WrongPasswordError` | locked PDF (`.attempts_left`) |
| `InvalidFileError` | empty, too large, too many pages, unsupported, unreadable, or refused by the virus scan |
| `LimitReachedError` | the month's pages are used (`.upgrade_url`) |
| `RateLimitError` | too many requests (`.retry_after`); retried automatically first |
| `ConversionFailedError` | the statement could not be read |
| `ServiceUnavailableError` | daily capacity reached or busy; retried first |
| `NotFoundError`, `DocFigTimeoutError`, `APIConnectionError` | unknown or expired id, waited too long, network |

Options: `docfig.Client(api_key=None, base_url="https://docfig.com", timeout=60, max_retries=3, poll_interval=2.0)`.

## Privacy

The offline check never sends anything anywhere. For the API: files are virus scanned, read in memory, account numbers are masked before the AI step, and results are deleted after an hour. See [docfig.com/security](https://docfig.com/security).

## License

MIT
