Recued
Menu
← Back to recipes

Import a bank statement (CSV)

by recued-core v7 111 views

Use “Import a bank statement (CSV)” in Recued. Reads a statement CSV you exported from your bank, lets you match its columns from the file’s own header, and records every line in your own warehouse. Re-importing an overlapping export adds only what is new.

Complete workflow

Bank statement import 3 pinned recipes

Use “Bank statement import” in Recued to bring a statement you exported yourself — CSV from any bank — into your own warehouse, with no aggregator in the middle. Aggregators gate real account data behind an approved business account; a file needs no credentials and no third party sees your transactions. You name your bank's columns once, so nothing is guessed. Re-importing an overlapping export skips lines you already have instead of double-counting them. Feeds ledger-book and expense-ledger.

You are viewing one recipe in this workflow. Installing it opens the complete pack so its other recipes and background automations arrive together.

Use “Import a bank statement (CSV)” in Recued. Reads a statement CSV you exported from your bank, lets you match its columns from the file’s own header, and records every line in your own warehouse. Re-importing an overlapping export adds only what is new.

pack:statement-importbankingcsvimport
by recued-core pinned v7

Use “List statement lines” in Recued. Shows imported statement lines, most recently imported first, for one account or across all of them, with the net for the page.

pack:statement-importbankingrecordsread
by recued-core pinned v1
View pack details, dependencies, and permissions →

Why a file, not a bank login

Every open-banking aggregator gates real account data behind an approved business account, and talking to a bank directly needs PSD2 authorisation. A statement export needs neither, works for banks no aggregator covers, and no third party ever sees the transactions.

Setting it up once

Open it from the pack and Recued guides you through it: choose the file, then match each field to one of the columns your bank actually exported. The choices are read from the file’s own header row, so a column name cannot be misspelled, and each column’s first values sit beside it. Check the first rows it shows you, then import. Tick Remember these columns and next month’s export arrives already matched.

Two layouts are supported:

  • One signed amount column (common in the UK/US): match the signed amount, and mark money out / money in as not in my file.
  • A debit + credit pair (common in Europe and India): mark the signed amount as not in my file and match both halves of the pair. Recued stores each column as its own figure and does not fold them together — which column means money out is a fact about your bank, and a stored line you can check against the paper is worth more than a tidier table. Listing recipes add them up when they show you a total.

Set exactly one of the two layouts.

From the plain run form instead, type the exact column names from your header row into the settings. The summary lists any it could not find in the file — a name it cannot find imports as an empty value on every line.

Re-running is safe

Bank exports overlap by design — the last 90 days, every time. Each line's id is derived from its own content plus your account label, so importing the same export twice reproduces the same ids and adds nothing. The summary says how many you already had.

Give each account its own label. The id is scoped to it, so the same amount on the same day in two accounts stays two lines.

A line records its own content, never which upload carried it — the run's history keeps that. So next month's export, which repeats the last few weeks, lands the repeated lines as already had. ⚠ Lines imported by the first version of this importer were stamped with the file they came from, so a later export that repeats one counts it as kept, not already had — it is already recorded, and nothing is lost or doubled.

When a line you already have looks different

Sometimes a line in the file is one you already have, but with different details — this export has a running balance your earlier one did not, you changed the line's category since, or you picked a different currency this time. Keep what I already have starts ticked: those lines stay exactly as you have them and are counted as kept. Untick it and the file wins instead: its running balance replaces yours, and the line is counted as updated. That is the only thing the file can change on a line you already have — the date, description and amount are what make it the same line, and the category, currency and account label come from you, not the file. The check shows you which it would be before you import.

The account's balance

After it records the lines, the import keeps the account's balance: the running balance your bank printed on the newest line Recued has from this account, and that line's date. Other workflows read it, to know what was in the bank on that day. The summary shows what was kept.

It needs two things:

  • A balance column. Match the column holding the running balance. Without one, the balance stays unknown.
  • Dates stored as dates. Recued finds the newest line by its date, so dates must be stored like 2025-12-31. Set the date format, unless your bank already writes them that way.

⚠ Two or more lines on the newest day leave that day's balance unknown. Each line has its own running balance, and nothing in the file says which came last.

⚠ An older statement never replaces a newer balance, so you can import statements in any order. The balance only moves to a later date.

⚠ It looks for the newest line in this month and the two months before. A statement older than that keeps no balance.

When an AI runs the import for you, you approve the balance as well as the lines: a new or changed balance is its own write, and asks on its own.

Check first

The guided import always checks before it records. From the plain form, turn on Check first: Recued reads the whole file, puts every line through exactly the checks a real import would, then throws the result away and tells you what would have happened — including the first rows exactly as they would be recorded, and what would happen to each. Nothing is recorded.

Do this the first time you map a bank. A column pointed at the wrong field imports perfectly — every line lands, nothing is refused — and the only sign is that the numbers are wrong. Reading the first few lines back before you commit is the only thing that catches it.

When a number or a date will not read

A number that cannot be read is stored EMPTY, never as zero, and the line still lands. A zero would look like a real transaction for nothing and would quietly move your balance. A date that does not read in the format you typed is kept exactly as written. Either way the summary counts them and shows the first few with their line numbers, so you can open the file at that line.

Decimal marks and date formats

Both are under More options, and are remembered with your columns.

  • Decimal mark. Pick Comma if your bank writes amounts like 1.234,56 or 1 234,56. The thousands mark (a comma, dot, space or apostrophe) is recognised on its own, but only in groups of three digits, or India's 12,34,567. So the wrong choice leaves amounts unreadable rather than wrong: 12.50 read with Comma is not 1250.
  • Date format. Empty keeps dates exactly as your bank writes them. Type the format instead, like dd/mm/yyyy, mm/dd/yyyy, yyyy-mm-dd or dd-mmm-yyyy for 20-Aug-2020, and they are stored as 2025-12-31, so they sort and filter as dates. If a date only reads in another order (12/31/2025 when you typed dd/mm/yyyy), nothing is imported, and Recued names the line and the format it reads in.

⚠ Set the date format before your first import from an account. The date is part of what makes a line the same line, so lines you imported with their dates as written would come in again as new. The check shows them as new before anything is recorded.

How it works 108 steps

Inspect the data fetches, transforms, gates, and output this recipe runs.

Process (108 steps)
■ file_id trim
Trim whitespace from setting file
■ has_file compare
Check if file id has data
⚠ file_guard guard
Stop if has file equals
■ acct trim
Trim whitespace from setting account label
■ has_account compare
Check if acct has data
⚠ account_guard guard
Stop if has account equals
◆ file_read core.storage.data-file-read
Data file read storage
■ csv_text decode_base64
Apply decode_base64
■ columns_all default
Apply default
■ columns filter
Filter by condition
■ money_all default
Apply default
■ money_present filter
Filter by condition
■ money_fields pluck
Extract field from each item
■ numeric_all default
Apply default
■ numeric_present filter
Filter by condition
■ balance_fields pluck
Extract field from each item
■ numeric_fields flatten
Flatten nested lists in
■ dedup_on flatten
Flatten nested lists in
■ clash_mode switch
Map setting keep mine to one of: false
■ decimal_mode switch
Map setting decimal mark to one of: Comma: 1.234,56
◆ imported recued-core.statement-import.line.import
Import line on statement import
■ any_unreadable compare
Check if imported unparsed is greater than 0
■ any_refused compare
Check if imported failed is greater than 0
■ stopped default
Apply default
■ was_check compare
Check if imported dry run equals
■ mode ternary
Apply ternary
■ unreadable to_table
Format results as a data table
skip: step.any_unreadable equal false
■ refused to_table
Format results as a data table
skip: step.any_refused equal false
■ missing_known compare
Check if imported columns missing exists
■ missing_list join
Join items with ", "
■ missing_named default
Apply default
■ missing_text ternary
Apply ternary
■ preview_marked map
Extract outcome from each item
skip: step.was_check equal false
■ preview_rows map
Extract from each item
skip: step.was_check equal false
■ preview to_table
Format results as a data table
skip: step.was_check equal false
■ bal_now_iso date_add
Add 0 seconds to
■ bal_month_now date_format
Format as a date
■ bal_mid_now template
Generate text from a template
■ bal_mid_prev date_add
Add -30 days to
■ bal_month_prev date_format
Format as a date
■ bal_mid_prev2 date_add
Add -61 days to
■ bal_month_prev2 date_format
Format as a date
◆ bal_lines_now recued-core.statement-import.line.search
Search line on statement import
skip: step.was_check equal true
■ bal_rows_now default
Apply default
■ bal_n_now count
Count items in bal rows now
■ bal_try_prev all
Check if all conditions are true
◆ bal_lines_prev recued-core.statement-import.line.search
Search line on statement import
skip: step.bal_try_prev equal false
■ bal_rows_prev default
Apply default
■ bal_n_prev count
Count items in bal rows prev
■ bal_try_prev2 all
Check if all conditions are true
◆ bal_lines_prev2 recued-core.statement-import.line.search
Search line on statement import
skip: step.bal_try_prev2 equal false
■ bal_rows_prev2 default
Apply default
■ bal_has_now compare
Check if bal n now is greater than 0
■ bal_has_prev compare
Check if bal n prev is greater than 0
■ bal_rows_older ternary
Apply ternary
■ bal_rows ternary
Apply ternary
■ bal_cursor_older ternary
Apply ternary
■ bal_cursor ternary
Apply ternary
■ bal_partial compare
Check if bal cursor has data
■ bal_sorted sort
Sort by posted_on
■ bal_last_date default
Apply default
■ bal_last_iso date_format
Format as a date
■ bal_dated compare
Check if bal last iso equals bal last date
■ bal_found all
Check if all conditions are true
■ bal_on_last filter
Filter by condition
■ bal_on_last_count count
Count items in bal on last
■ bal_line_value default
Apply default
■ bal_one compare
Check if bal on last count equals 1
■ bal_has_value compare
Check if bal line value exists
■ bal_known all
Check if all conditions are true
■ bal_value ternary
Apply ternary
■ bal_basis_single ternary
Apply ternary
■ bal_basis ternary
Apply ternary
■ bal_currency default
Apply default
◆ bal_stored recued-core.statement-import.balance.search
Search balance on statement import
skip: step.bal_found equal false
■ bal_row default
Apply default
■ bal_has_row compare
Check if bal row exists
■ bal_days_after date_diff
Calculate days between bal row as of and bal last date
■ bal_not_older compare
Check if bal days after is at least 0
■ bal_same_date compare
Check if bal row as of equals bal last date
■ bal_same_value compare
Check if bal row balance equals bal value
■ bal_same_basis compare
Check if bal row basis equals bal basis
■ bal_same_currency compare
Check if bal row currency equals bal currency
■ bal_unchanged all
Check if all conditions are true
■ bal_do_create all
Check if all conditions are true
■ bal_do_update all
Check if all conditions are true
◆ bal_create recued-core.statement-import.balance.create
Create balance on statement import
skip: step.bal_do_create equal false
◆ bal_update recued-core.statement-import.balance.update
Update balance on statement import
skip: step.bal_do_update equal false
■ bal_window_empty compare
Check if bal rows is empty
■ bal_need_why all
Check if all conditions are true
◆ bal_newest recued-core.statement-import.line.search
Search line on statement import
skip: step.bal_need_why equal false
■ bal_newest_date default
Apply default
■ bal_newest_iso date_format
Format as a date
■ bal_newest_dated compare
Check if bal newest iso equals bal newest date
■ bal_no_lines compare
Check if bal newest date is empty
■ bal_older_stored all
Check if all conditions are true
■ bal_code_window_dated ternary
Apply ternary
■ bal_code_window ternary
Apply ternary
■ bal_code_single ternary
Apply ternary
■ bal_code_basis ternary
Apply ternary
■ bal_code_found ternary
Apply ternary
■ bal_code_dated ternary
Apply ternary
■ bal_code_rows ternary
Apply ternary
■ bal_code_whole ternary
Apply ternary
■ bal_code ternary
Apply ternary
■ bal_text switch
Map bal code to one of: old, none, check, dates, known, older, several, too_many, no_balance
■ words switch
Map was check to one of: true
■ card to_summary
Format results as a summary card
▼
Output
summary card
table preview
table unreadable
table refused
Settings 14 configurable

Configurable at install. Defaults shown — change them anytime in Recued.

file setting = [object Object]
currency setting = GBP
delimiter setting = ,
keep mine setting = true
check first setting = false
column date setting = Date
date format setting =
column debit setting =
decimal mark setting = Point: 1,234.56
account label setting =
column amount setting = Amount
column credit setting =
column balance setting =
column description setting = Description

Uses (6 operations)

Files Data file read storage core.storage.data-file-read
Pack operation Import line on statement import recued-core.statement-import.line.import
Pack operation Search line on statement import recued-core.statement-import.line.search
Pack operation Search balance on statement import recued-core.statement-import.balance.search
Pack operation Create balance on statement import recued-core.statement-import.balance.create
Pack operation Update balance on statement import recued-core.statement-import.balance.update

Trust & control

What installing this recipe would let it do. Recued grants these permissions at install — review them there before approving.

What it's allowed to do

Read Files core.storage.data-file-read read-only
Write Pack Bank statement import external change
Write: line.import · Asks approvalRead: line.search · No approvalRead: balance.search · No approvalWrite: balance.create · No approvalWrite: balance.update · No approval

Data it touches

SurfacesFiles

About

Tags

pack:statement-importbankingcsvimportreconciliation

Details

108 steps 14 configurable settings recipe_id: import-bank-statement