The OFX / QBO File Format Explained (Developer Reference)
OFX (Open Financial Exchange) is an SGML-based format that banks use to deliver statements to accounting software. A .QBO file is the same OFX 1.x structure plus two Intuit-specific header lines that tell QuickBooks which bank the data came from. This page documents the parts that matter when you generate or parse these files.
OFX, QFX, QBO, and OFX: one format, four extensions
All four share a common body. The differences are in the headers and which program is allowed to open the result.
- .OFX is the generic Open Financial Exchange file. Many accounting programs accept it.
- .QBO is OFX with Intuit headers (
INTU.BIDandINTU.USERID) so QuickBooks Desktop and QuickBooks Online will import it through Web Connect. - .QFX is OFX with Quicken's
INTU.BIDheader, used by Quicken. - .OFX (again) or no Intuit header is what other tools (Xero, Wave, GnuCash, MoneyWorks) generally expect.
Because the body is identical, the same writer can emit any of them by swapping the header block. For format-specific walkthroughs see CSV to QFX and CSV to OFX.
The header: DATA:OFXSGML and version 102
An OFX 1.x file starts with a plain-text header block, then a blank line, then the SGML body. The header is colon-delimited, not tagged. A standard QuickBooks-compatible header looks like this:
OFXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
Two points trip people up. First, QuickBooks expects DATA:OFXSGML (the SGML form of OFX 1.x). The XML-based OFX 2.x format, which opens with an <?xml?> declaration, is not accepted by QuickBooks import. Second, VERSION should stay in the 1.x range (102 is the safe, widely accepted value). Mixing a 2.x version number into an SGML file is a common reason an import is rejected.
SGML, not XML: why tags often have no closing tag
OFX 1.x is SGML, which permits open tags without matching close tags for leaf (data) elements. So you will see this:
<TRNAMT>-42.50
<DTPOSTED>20260115
and not <TRNAMT>-42.50</TRNAMT>. Container elements (<STMTTRN>, <BANKTRANLIST>) are closed; scalar fields are not. A parser written for strict XML will choke on this. Either treat the file as SGML, or normalize it (close the leaf tags) before handing it to an XML parser. This is also why hand-editing a QBO file in a generic XML editor tends to corrupt it.
The statement body: SONRS, BANKACCTFROM, BANKTRANLIST
The body is a nested set of aggregates. A bank statement download wraps everything in <OFX>, with a sign-on response (<SIGNONMSGSRSV1> containing <SONRS>) followed by a bank statement response (<BANKMSGSRSV1> containing <STMTTRNRS> and then <STMTRS>). Inside the statement you will find:
<CURDEF>the currency, for exampleUSD.<BANKACCTFROM>the source account, holding<BANKID>(routing number),<ACCTID>(account number), and<ACCTTYPE>(CHECKING,SAVINGS, orCREDITLINEfor cards).<BANKTRANLIST>the transaction list, bracketed by<DTSTART>and<DTEND>, then one<STMTTRN>per transaction.<LEDGERBAL>the closing balance with<BALAMT>and<DTASOF>.
Credit card statements use <CREDITCARDMSGSRSV1> and <CCSTMTRS> with <CCACCTFROM> instead of the bank variants, but the transaction list is the same.
STMTTRN: the transaction record
Each transaction is a <STMTTRN> aggregate. The fields you need in practice:
<TRNTYPE>the type. Common values areDEBIT,CREDIT,CHECK,PAYMENT,XFER,FEE,INT, andATM. When in doubt,DEBITfor money out andCREDITfor money in is safe.<DTPOSTED>the posted date asYYYYMMDD(a time and timezone may be appended, for example20260115120000[-5:EST], but the date alone is accepted).<TRNAMT>the amount, signed. Negative for money out, positive for money in. Use a period as the decimal separator, no thousands separators, no currency symbol.<FITID>the financial institution transaction ID. See the next section.<NAME>the payee or short description (OFX limits this to 32 characters).<MEMO>an optional longer description. Put the full memo here when<NAME>is truncated.<CHECKNUM>the check number, when the type isCHECK.
A minimal transaction:
<STMTTRN>
<TRNTYPE>DEBIT
<DTPOSTED>20260115
<TRNAMT>-42.50
<FITID>20260115-001
<NAME>ACME HARDWARE
<MEMO>Store #114 purchase
</STMTTRN>
Escape the five reserved characters in text fields: & becomes &, < becomes <, and > becomes >. An unescaped ampersand in a payee name is a frequent cause of a failed import.
FITID: the unique key that prevents duplicates
<FITID> is the field QuickBooks uses to decide whether it has already seen a transaction. It must be unique within the account. If two transactions carry the same FITID, the second is silently dropped. If a FITID changes between two imports of overlapping data, the transaction is imported twice. Duplicate or unstable FITIDs are the single most common reason imports go wrong.
Bank-issued OFX files include a real FITID from the institution. When you build a file from a CSV (which has no such field), you must generate one. A reliable approach is to derive a deterministic ID from the stable parts of the row, for example a hash of date plus amount plus description plus a per-row counter for that date. Determinism matters: re-converting the same statement should produce the same FITIDs so re-imports do not duplicate. Avoid using a plain sequence number alone (1, 2, 3), because a different export ordering would then collide with a prior import.
INTU.BID and the QuickBooks-specific headers
What turns a generic OFX file into a .QBO file QuickBooks will accept is two extra header lines that sit below the standard OFX header block:
INTU.BID:2430
INTU.USERID:anonymous
INTU.BID (Intuit Bank ID) is a numeric identifier Intuit assigns to each financial institution. QuickBooks reads it to recognize the bank and to match the file to the right account. The value does not have to correspond to your actual bank for a manual Web Connect import to succeed, but using a real, recognized BID makes account matching cleaner. INTU.USERID can be a placeholder such as anonymous for a file you are importing manually rather than fetching through a live bank-feed connection.
Quicken uses the same INTU.BID mechanism in its .QFX files. Strip the Intuit lines and you are back to a plain .OFX that non-Intuit tools accept. That single difference is why one converter can target QuickBooks, Quicken, and everything else from the same transaction body.
You do not need to assemble any of this by hand. QBO Maker reads your bank's CSV or Excel export and writes a valid OFX 1.x body with deterministic FITIDs, the correct SGML header, and the right Intuit lines for whichever target you pick (.QBO, .QFX, or .OFX), entirely in your browser.
Frequently asked questions
Is a QBO file the same as an OFX file?
The body is the same OFX 1.x SGML. A QBO file adds the Intuit header lines (INTU.BID and INTU.USERID) so QuickBooks will import it. Remove those lines and you have a generic OFX file.
Why does QuickBooks reject my OFX file even though it looks valid?
The usual causes are: the file is OFX 2.x (XML) instead of OFX 1.x (DATA:OFXSGML); the FITIDs are not unique; an unescaped &, <, or > appears in a payee or memo; or the VERSION header is set to a 2.x number inside an SGML file. QuickBooks expects SGML with version 102.
What date format does OFX use?
YYYYMMDD, so 15 January 2026 is 20260115. An optional time and timezone may follow, for example 20260115120000[-5:EST], but the eight-digit date by itself is accepted.
Can I parse a QBO file with a standard XML parser?
Not directly. OFX 1.x is SGML and leaves leaf tags unclosed (for example <TRNAMT>-42.50 with no closing tag). Treat it as SGML, or normalize the file by closing leaf elements before passing it to an XML parser.
What value should INTU.BID be?
It is the numeric Intuit Bank ID for a financial institution. A real, recognized value gives the cleanest account matching, but for a manual Web Connect import the file will generally still import with a generic BID. The field tells QuickBooks which bank the data represents.