object field. Create it once, then run every statement through it.
The statement
BANK STATEMENT
→
What comes back
account_holder99%
account_number99%
closing_balance99%
transactions
NodesParse → ExtractCredits60 per pageYou get
The graph
Run and read
One statement is one run. Keep each file in its own call so each statement gets its own result; a call that carries several files makes one document packet and one run.What comes back
One statement with six transactions, trimmed to two header fields and two rows. The run took 21 seconds.Response
verification_status and value_override. Runs and results has the full envelope, section by section.
Read the fields from extractions[0].fields. A scalar field is one object with value, confidence and evidence. An object field is an array of rows, and every cell in a row is that same object. Every value is a string on the wire: numbers as "2983.35", dates as "2026-03-31", booleans as "True" or "False". The SDKs keep them as strings too, so convert before you add them up. A field the model could not find comes back as {"value": null, "confidence": null, "evidence": []}.
Getting the data out
- In the app, open the workflow and export the results as CSV, Excel or JSON. Each statement is a row and
transactionsexpands into its own sheet or nested array. See Outputs. - Over the API, read each run as shown above, or list a workflow’s runs for a date range and flatten them yourself. Daily CSV export is a complete script for that.
When it goes wrong
Rows are missing from a long transaction list. A statement runs to hundreds of rows across several pages, and a single standard pass can drop one in a dense table. Ask for the totals the statement prints (total_deposits, total_withdrawals) as extra fields and compare them with the sum of the rows. A mismatch is the cheapest signal that a row was missed. When the tables stay dense, move the Parse node to the agentic tier with "mode": "agentic" at 100 credits per page. Agentic parse to markdown shows the presets.
Withdrawals come back positive. amount is a float, and nothing in the type says which direction the money went. Put the convention in the description: “positive for deposits, negative for withdrawals”. The sign then holds across banks that print a Debit column instead of a minus sign.
A transaction type comes back empty. type is an enum, so it returns only the names you declared. A statement that prints “standing order” or “card payment” has nothing to match among deposit, withdrawal, fee, transfer and interest. Add the option, or use a string field when the bank’s vocabulary is open.
Your balances do not add up. Every value arrives as a string: "2983.35", "-1650.0", "2026-03-31". Cast each one to the type you declared before you do arithmetic on it. A field the model could not find comes back as {"value": null, "confidence": null, "evidence": []}, so test for null before you cast.
A batch of statements fails halfway through. Upload and run is limited to 60 requests per minute. Submit statements serially with a short pause between them, or spread them across workers. Feed the workflow the XLSX or CSV the bank already offers when you have the choice: the cells are structured and no OCR runs.
Next steps
Extract node
Object fields, modes and smart lookup
Outputs
CSV, Excel and JSON exports from the app
Daily CSV export
Flatten a day of runs into one CSV over the API
Runs and results
The model behind the envelope

