Import transactions
Bring bank transactions in from a CSV or QIF file: preview what would happen, choose the accounts, apply the proposal, and let red-ale learn which account a description belongs to.
Most banks let you download your transactions as a file. Importing saves typing them, and red-ale checks each against what is already in the book so that a transaction is not entered twice. Nothing changes until you have seen what would happen: import is always two steps, preview and apply.
Steps
- Create the account the file belongs to, a bank or credit card account in the file's currency (Set up accounts).
- Preview with import.preview.
formatiscsvTransactionsorqif. The file's content goes infilesastext(orbase64); the server never reads a path you name. From the command line,red-ale exec --file bank.csv import.preview '{...}'reads the file on your side and adds it tofiles.- For CSV, give a
layout: the separator, how many lines to skip, the date and number formats, thebaseAccountand what each column is (Date,Description,Amount,Account, and so on). - For QIF, the dates and the accounts the file names are read for you.
- For CSV, give a
- Read the proposal. Its
idnames it.transactionslists what would be done to each row:addit,clearan existing transaction it matches,updateone, orskip. A row that matches an existing transaction lists thematches.errorsare rows that cannot be imported and are left out. A transaction with no account to balance it is markedimbalance. - Apply with import.apply and the proposal
id. Inchangesgive theaccountthat balances a row, or change a row's action.skipErrors: trueapplies the good rows when others have errors. Applying changes the book in one step: everything or nothing. - Or discard the proposal with import.discard. Proposals are kept in memory only, so one made before the book was opened is gone.
- Learn. When you give a transfer account for a description, red-ale remembers it, so that the next import suggests it. importMap.list shows what it has learned and importMap.delete forgets an entry.
- Save with book.save.
The proposal's id counts up from imp-1 in each server process.
Example
A bank's CSV with two rows. The preview adds both and notes that neither is balanced yet. Applying it names Food as the
account that balances the first row; the second is balanced against Imbalance-USD.
Example: with a new, empty book open as book.gnucash.
JSON-RPC
Request:
{"jsonrpc":"2.0","id":1,"method":"account.create","params":{"name":"Checking","type":"BANK","commodity":"USD"}}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "…"
}
Request:
{"jsonrpc":"2.0","id":2,"method":"account.create","params":{"name":"Food","type":"EXPENSE","commodity":"USD"}}
Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": "…"
}
Request:
{"jsonrpc":"2.0","id":3,"method":"import.preview","params":{"format":"csvTransactions","files":[{"name":"bank.csv","text":"Date,Description,Amount\n2024-06-03,Coffee,-3.50\n2024-06-04,Lunch,-12.00\n"}],"layout":{"format":"csv","separators":",","skipStartLines":1,"dateFormat":"y-m-d","currencyFormat":"period","encoding":"utf-8","baseAccount":"Checking","columns":["Date","Description","Amount"]}}}
Response:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"id": "imp-1",
"format": "csvTransactions",
"summary": {
"add": 2,
"imbalanced": 2,
"errors": 0,
"…": "…"
},
"transactions": [
{
"index": 0,
"description": "Coffee",
"datePosted": "2024-06-03",
"action": "add",
"…": "…"
},
{
"index": 1,
"description": "Lunch",
"action": "add",
"…": "…"
}
],
"…": "…"
}
}
Request:
{"jsonrpc":"2.0","id":4,"method":"import.apply","params":{"proposal":"imp-1","changes":[{"index":0,"account":"Food"}]}}
Response:
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"added": [
{
"index": 0,
"transaction": "…"
},
{
"index": 1,
"transaction": "…"
}
],
"skipped": [],
"imbalanceSplits": [
{
"index": 1,
"account": "…",
"value": {
"num": "1200",
"denom": "100"
},
"…": "…"
}
],
"learned": "…",
"warnings": "…",
"…": "…"
}
}
Command line
red-ale exec --book book.gnucash --script - <<'EOF'
account.create {"name":"Checking","type":"BANK","commodity":"USD"}
account.create {"name":"Food","type":"EXPENSE","commodity":"USD"}
import.preview {"format":"csvTransactions","files":[{"name":"bank.csv","text":"Date,Description,Amount\n2024-06-03,Coffee,-3.50\n2024-06-04,Lunch,-12.00\n"}],"layout":{"format":"csv","separators":",","skipStartLines":1,"dateFormat":"y-m-d","currencyFormat":"period","encoding":"utf-8","baseAccount":"Checking","columns":["Date","Description","Amount"]}}
import.apply {"proposal":"imp-1","changes":[{"index":0,"account":"Food"}]}
EOF
It prints:
[
"…",
"…",
{
"id": "imp-1",
"format": "csvTransactions",
"summary": {
"add": 2,
"imbalanced": 2,
"errors": 0,
"…": "…"
},
"transactions": [
{
"index": 0,
"description": "Coffee",
"datePosted": "2024-06-03",
"action": "add",
"…": "…"
},
{
"index": 1,
"description": "Lunch",
"action": "add",
"…": "…"
}
],
"…": "…"
},
{
"added": [
{
"index": 0,
"transaction": "…"
},
{
"index": 1,
"transaction": "…"
}
],
"skipped": [],
"imbalanceSplits": [
{
"index": 1,
"account": "…",
"value": {
"num": "1200",
"denom": "100"
},
"…": "…"
}
],
"learned": "…",
"warnings": "…",
"…": "…"
}
]
MCP tool call
Tool call:
{"name":"account.create","arguments":{"name":"Checking","type":"BANK","commodity":"USD"}}
Result:
{
"content": [
{
"type": "text",
"text": "\"…\""
}
]
}
Tool call:
{"name":"account.create","arguments":{"name":"Food","type":"EXPENSE","commodity":"USD"}}
Result:
{
"content": [
{
"type": "text",
"text": "\"…\""
}
]
}
Tool call:
{"name":"import.preview","arguments":{"format":"csvTransactions","files":[{"name":"bank.csv","text":"Date,Description,Amount\n2024-06-03,Coffee,-3.50\n2024-06-04,Lunch,-12.00\n"}],"layout":{"format":"csv","separators":",","skipStartLines":1,"dateFormat":"y-m-d","currencyFormat":"period","encoding":"utf-8","baseAccount":"Checking","columns":["Date","Description","Amount"]}}}
Result:
{
"structuredContent": {
"id": "imp-1",
"format": "csvTransactions",
"summary": {
"add": 2,
"imbalanced": 2,
"errors": 0,
"…": "…"
},
"transactions": [
{
"index": 0,
"description": "Coffee",
"datePosted": "2024-06-03",
"action": "add",
"…": "…"
},
{
"index": 1,
"description": "Lunch",
"action": "add",
"…": "…"
}
],
"…": "…"
}
}
Tool call:
{"name":"import.apply","arguments":{"proposal":"imp-1","changes":[{"index":0,"account":"Food"}]}}
Result:
{
"structuredContent": {
"added": [
{
"index": 0,
"transaction": "…"
},
{
"index": 1,
"transaction": "…"
}
],
"skipped": [],
"imbalanceSplits": [
{
"index": 1,
"account": "…",
"value": {
"num": "1200",
"denom": "100"
},
"…": "…"
}
],
"learned": "…",
"warnings": "…",
"…": "…"
}
}