Import statements
A whole statement enters Badlen through one route, POST /api/v1/import, reached with an
API key carrying transactions:write. That is the entire surface. There is no CSV parser,
no OFX or QIF reader, no bank connection, and no screen that takes a statement file: the
web app’s only operation form writes a single line at a time.
In practice the route is driven by the MCP server, where your own Claude does the reading and the extracting. Driving it from a script of your own is the same route and the same key.
What a line has to be
Section titled “What a line has to be”The route takes no raw file. It takes an account id and an array of normalised lines, and turning a broker file into them is the caller’s work.
| Field | Required | Shape |
|---|---|---|
date |
yes | YYYY-MM-DD |
amount |
yes | Signed decimal string; negative is money out |
label |
yes | Non-empty text |
currency |
no | Three letters; EUR when absent |
fee |
no | Non-negative decimal string |
tax |
no | Non-negative decimal string |
Money travels as a decimal string, never as a JSON number: a number is a double by the time the API reads it, and a quantity loses its last digits on the way in. A body that sends one is refused.
The schema also accepts type, counterparty and category, but the import does not
write them onto the line. A batch’s categories are only counted: the dry run below reports
how many lines arrived without one, so you can press Recategorize on
Settings → Categories afterwards.
Duplicates
Section titled “Duplicates”Each line gets a fingerprint built from its date, its amount and its label folded to lower case. A line whose fingerprint the account already holds is skipped, which is what makes re-sending the same statement safe.
Two identical operations on one day (two card payments of the same amount to the same merchant) are not a duplicate of each other. They are ranked apart within the batch, so both land. That ranking is the batch’s alone, which has one consequence worth knowing:
An operation you deleted after an earlier import is remembered. Badlen keeps a gravestone for the fingerprint, and the same line in a later import of the same statement is not written back.
The dry run
Section titled “The dry run”POST /api/v1/import/reconcile takes exactly the same body and writes nothing. It answers
with the split of the batch (how many lines are new, how many are already there, how many
are buried), plus its date range, the inflow and outflow totals, how many operations the
account already holds and up to what date, and the lines that match an existing date and
amount under a different label. It also returns the questions worth putting to you before
committing.
Both calls count through the same function and the import answers with the same figures, so what the dry run announces is what the write does.