Your bank hands you a file no program on your computer will open, and the extension tells you almost nothing. It is a text document in a format called OFX, and once you have seen one, the three extensions stop being a mystery — and so do most of the reasons an import gets refused.
OFX — Open Financial Exchange — is a format banks use to hand transactions to accounting software. .qbo is that same document with two extra tags QuickBooks looks for. .qfx is the same again with Quicken's branding. Nothing about the transactions differs between them; a program refusing one of the three is refusing the branding, not the data.
There are two dialects, and the difference matters the moment you try to open one
with a tool of your own. OFX 1.x is SGML: tags are never closed and
the file opens with a block of key:value lines. OFX 2.x
is ordinary XML. Banks hand out the first far more often, which is why a strict XML
parser fails on exactly the files you are most likely to have. Our
QBO to CSV reader treats both as text for that reason.
Everything below is the actual output of the builder behind CSV to QBO — two transactions, nothing trimmed for the page. First the header, which is the part that is not XML at all:
OFXHEADER:100 DATA:OFXSGML VERSION:102 SECURITY:NONE ENCODING:UTF-8 CHARSET:NONE COMPRESSION:NONE OLDFILEUID:NONE NEWFILEUID:NONE
VERSION:102 is the dialect: 1.0.2, the SGML one.
DATA:OFXSGML says the same thing again. A 2.x file has an XML
declaration here instead. Then the envelope — who signed on, which account this is,
and the window the transactions cover:
<OFX> <SIGNONMSGSRSV1> <SONRS> <STATUS> <CODE>0 <SEVERITY>INFO </STATUS> <DTSERVER>20260930120000 <LANGUAGE>ENG <FI> <ORG>ocrsnip.com <FID>021000021 </FI> <INTU.BID>1234 <INTU.USERID>ocrsnip </SONRS> </SIGNONMSGSRSV1> <BANKMSGSRSV1> <STMTTRNRS> <TRNUID>1 <STATUS> <CODE>0 <SEVERITY>INFO </STATUS> <STMTRS> <CURDEF>USD <BANKACCTFROM> <BANKID>021000021
BANKID is the routing number, ACCTID the account.
INTU.BID appears only in a .qbo: it is the institution id QuickBooks
Desktop checks against its own list, and the single most common reason a correct
file is rejected. Inside BANKTRANLIST each transaction is one block:
<STMTTRN> <TRNTYPE>DEBIT <DTPOSTED>20260903 <TRNAMT>-128.40 <FITID>OCRSNIP202609031648410143 <NAME>ACH DEB CO ACME PAYROLL </STMTTRN> <STMTTRN>
Five fields carry everything a ledger needs. The file ends with the balance the statement is claiming as of the last transaction:
<LEDGERBAL> <BALAMT>2271.60 <DTASOF>20260905 </LEDGERBAL> </STMTRS>
That is the whole format. A statement with three hundred transactions is this same
document with three hundred STMTTRN blocks in the middle.
Everything else in the file is packaging.
YYYYMMDD, optionally followed by a time and a bracketed zone:
20260302120000[0:GMT]. The day is the first eight digits and nothing
else in the file contradicts it, which is why a .qbo can never be ambiguous the
way 03/04 in a CSV is.
One signed number. Money out is negative, money in positive, and the sign is the
only thing that says which. TRNTYPE beside it is a label — DEBIT,
CREDIT, CHECK, XFER, POS — and importers trust the sign, not the label.
An id the issuer promises is unique for that account. QuickBooks and Quicken remember it, so re-importing a month you already have corrects rather than doubles. Files built here derive it from the row's own date, amount and description, which is why the same row always produces the same id.
Two fields where a spreadsheet has one column. The specification allows 32 characters for NAME; we write up to 96, because cutting a payee at 32 loses the part the reader came for. If an importer complains about a length, this is the field to shorten.
Only in .qbo. QuickBooks Desktop matches it against its own list of institutions and refuses the file outright if it does not recognise the number — every transaction can be perfect and the import still fails. It cannot be invented, which is why the converter asks you for it rather than guessing.
Stated once, at the end. There is no running balance anywhere in the format, so a CSV made from one of these files has no balance column either — the number simply is not there to carry.
Four failures that look like a broken file and are not.
In 1.x it is not supposed to be. Tags like <TRNAMT>-128.40
are never closed, and a parser that insists on well-formed XML will reject a
perfectly good statement. Read it as text, or convert it first.
Because the file has no commas to split on. Renaming it .csv changes nothing — the tags come through either way. Something has to read the tags and lay the fields out, which is what QBO to CSV and QBO to Excel do.
An investment or brokerage OFX records trades, positions and income in blocks of
its own and carries no STMTTRN at all. Open it in a text editor and
search for that tag: if it is not there, there are no bank transactions in the
file to find.
Nine times in ten it is INTU.BID — the wrong institution id, or
none. QuickBooks Online is far more forgiving than Desktop and also accepts
.csv, .qfx and plain .ofx on its upload screen, so trying the same data there is
a quick way to tell a format problem from a data problem.
Something else? Write to support@ocrsnip.com.
Yes — it is text, and a text editor is enough. Keep the tags exactly as they are, including the unclosed ones, and change only the values after them. Fixing a single wrong amount this way is quicker than converting anything; rewriting a whole statement by hand is not.
No, and the three shared letters cause real confusion. A .qbo is a statement your bank wrote for import. QuickBooks keeps its own books in .qbw and .qbb files, which hold a whole company — customers, invoices, history — and have nothing to do with this format.
QuickBooks Desktop wants .qbo. Quicken expects .qfx and often refuses a plain .ofx. QuickBooks Online accepts .csv, .qbo, .qfx and .ofx on its upload screen. Xero, GnuCash and most other programs read the plain .ofx. The contents are identical in every case.
Many do, and a CSV is perfectly good data — it just leaves the column mapping to you and your accounting program. CSV to QBO writes the file described on this page from that CSV, free and with no row limit, so the import side stops asking questions.
No. Both the reader and the builder are code running in your browser tab — there is no upload step and no server that receives files. The network panel of your developer tools stays empty while you convert, which is the only form of this promise worth anything.