CSV format reference
On this page
This page lists exactly what Pennyweight accepts when it reads a CSV file itself: the headers it looks for, the amount layouts and date orders it understands, and what it leaves out. Use it to check a file before a direct upload, or to understand why one was refused.
Most imports don't need any of this. By default your AI reads your file and sends Pennyweight the rows, converting dates, amounts and columns as it goes. The direct upload is the advanced route. See import a CSV export for the normal steps.
File basics
- CSV only. PDFs, spreadsheets and screenshots aren't read by the upload. Your AI reads those itself.
- Up to 5 MiB. A larger file is refused with a message asking you to split it. One import also takes at most 5,000 rows. See limits and timeouts.
- Your AI sends the file's contents. A link to the file isn't accepted.
- Encoding. UTF-8 is read with or without a byte-order mark. A file that isn't valid UTF-8 is read as Windows-1252 instead, with a
NON_UTF8_SOURCEwarning that non-Latin characters may be wrong. - Column separator. Commas, semicolons, tabs and pipes are detected from the start of the file. If detection can't decide, the most common of the four in the first line wins, else a comma. Your AI can name the separator instead; naming a quote or a line break gives a
BAD_HINTwarning. - The first row is the header. A file that starts with a transaction, or with bank text above the header, isn't read correctly.
- One account per file. Every row goes into the account your AI names for the import. See import years of history across many accounts.
Recognized headers
Before matching, each header is lower-cased, a bracketed note at its end is dropped, punctuation and underscores become spaces, and Amt or Amnt reads as amount. So Amount (USD) reads as amount, Debit Amt. as debit amount and Transaction_Date as transaction date. After that, every match is exact, except for the amount column.
| Field | Headers the upload looks for | If none is found |
|---|---|---|
| Date | transaction date, date, posted date, posting date, booking date, value date | The first column |
| Debit (money out) | debit, debits, withdrawal, withdrawals, debit amount, withdrawal amount, outflow, paid out, money out | — |
| Credit (money in) | credit, credits, deposit, deposits, credit amount, deposit amount, inflow, paid in, money in | — |
| Amount | Any other header containing amount or amt; the first one wins | The second column, when there's no debit or credit column either |
| Direction | transaction type, dr/cr, debit/credit, type | — |
| Merchant | merchant, merchant name, payee, payee name, description, transaction description, narration, narrative, descr, desc, name, particulars, details, transaction details, detail, memo, transaction, reference | The third column, unless it's already used; otherwise blank |
| Category | category, categories | No category |
| Running balance | balance, running balance, closing balance, available balance, ledger balance, account balance | — |
When several headers match:
- Date. Transaction date and date rank first, then posted or posting date, then booking date, then value date. Between equal ranks, the leftmost column wins.
- Merchant. The header listed first above wins, wherever it sits in the file. A column holding only words such as Debit, Credit or Check is skipped while another candidate exists.
- Type. A Type column is only ever read as a direction. It never becomes the category.
Other headers, such as Datum or Betrag, aren't recognized.
How the amount is read
The upload picks one layout for the whole file, and the preview names it:
- One signed amount column. Money out is negative.
- Separate debit and credit columns. A debit is always money out, whatever sign the file gives it, and a credit is added as written, so a positive credit is money in. A row with both cells blank can't be read.
- CR or DR on every amount. DR is money out and CR is money in, before or after the number: 84.22 CR, 84.22DR or DR 84.22. The marker must stand alone, so a code such as IDR doesn't count. A zero amount can go unmarked.
- Unsigned amounts with a direction column. Debit and dr are money out, credit and cr are money in, in any capitals. A blank direction is allowed only next to a zero amount.
A direction column is ignored when none of its values is debit, dr, credit or cr, such as Sale and Payment, or when any amount carries a minus sign. The amounts are then read as signed.
With debit and credit columns beside an amount column, an amount column with no signs is ignored, and a signed one is used only if it agrees with them on every row.
Mixed layouts are refused
The upload never guesses a sign. It refuses the whole file with AMBIGUOUS_SIGN_LAYOUT when:
- some amounts carry CR or DR and others don't;
- the direction column holds other values, or a blank next to a non-zero amount;
- the file uses two layouts at once, such as CR or DR inside debit and credit columns, or a signed amount column that disagrees with them.
The message lists the file's line numbers for up to 20 of those rows, counting the header as line 1. An amount with both a minus sign and CR or DR is a row error, and it blocks the import the same way an unreadable amount does. See import errors.
Dates
Pennyweight saves every date as YYYY-MM-DD. Your AI can name the file's date order when it uploads the file, and the upload converts each date. These orders are supported:
- YYYY-MM-DD
- MM/DD/YYYY
- DD/MM/YYYY
- DD.MM.YYYY
- YYYYMMDD
The order is never guessed. Without one, dates are kept as written and only YYYY-MM-DD passes. With one, single-digit days and months such as 9/3/2026 work, but a two-digit year doesn't, and YYYYMMDD needs all eight digits. A date that fits neither the named order nor YYYY-MM-DD, a date with a time, a date that doesn't exist such as 2026-02-30, and an empty cell are refused. A single refused date blocks the whole file with BAD_DATE, and nothing is imported. See CSV date format errors.
Amounts
| Cell | Read as |
|---|---|
| -84.22 or +84.22 | -84.22 or 84.22 |
| (84.22) | -84.22 |
| 142.10- | -142.10 |
| $1,800.00, €12.50, £3, ¥500 | 1800.00, 12.50, 3, 500 |
| 1.234,56 | 1234.56 |
| 1'234.56 or 1 234,56 | 1234.56 |
- Currency symbols. Only $, €, £ and ¥ are removed. A cell with any other symbol or a currency code, such as USD 12.00 or CHF 12.00, can't be read.
- An unreadable amount blocks the import. The upload's parser warnings report
AMOUNT_PARSE, and the preview refuses the import and quotes up to five of the cells. A row with fewer columns than the date and amount positions need is reported asSHORT_ROWand blocks the import the same way. Blank lines are skipped.
Decimal and thousands separators
The upload works out the decimal point from each cell:
- When a cell has both a dot and a comma, the rightmost one is the decimal point.
- A separator that appears more than once, as in 1.234.567, separates thousands.
- A single separator followed by one, two, or four or more digits is the decimal point.
- Spaces, apostrophes and underscores always separate thousands.
A cell such as 1,234 could be either. If the other amounts in the column agree on the notation, the whole column follows them. If not, 1,234 becomes 1234 and the preview shows an AMBIGUOUS_SEPARATOR warning. A cell such as 0.500 is read as a decimal. Your AI can name the decimal point, which overrides the inference. See bank CSVs with decimal commas.
Money in and money out
When your AI previews the import, it says whether the file raises the account's balance, lowers it, or both. In a one-direction file, every row takes that direction whatever its sign. See understanding balance signs and balance looks wrong after an import.
Categories and running balances
A category column is written as each row's category, unless one of your rules matches the row. Your AI can map the file's labels onto categories you already use, and a row with a blank cell stays uncategorized.
A running balance is read only to tell apart identical lines in the same file. Two non-zero lines with the same date, amount, merchant and balance are one line listed twice, and the second is skipped by default. A balance that's the same on every row is ignored. See reading an import preview.
What the upload doesn't read
These columns are ignored, even when the file has them:
- currency;
- notes;
- tags;
- account name or number.
When your AI reads the file and sends the rows instead, it can carry each row's notes and currency.
Good to know
- You still get a preview. Nothing is saved until you confirm, and a blocked file saves nothing. Every message above is explained in import errors.