The BAI2 file format is a plain text bank report made of numbered, comma-separated records. A 03 record gives an account's balances, each 16 record is one transaction, and trailer records carry control totals. Amounts have no decimal point and no sign. To use one in QuickBooks or Xero, rebuild the 16 records as CSV rows.
Who sends a BAI2 file, and why to a small business?
Business and treasury banking services send BAI2 files. The format began as a standard for cash management balance reporting, built so a company's systems could read balances and transactions from its banks each day. A small business usually meets one after moving to a business online banking plan, or when a bookkeeper asks the bank for a "machine-readable" export.
East West Bank's BAI2 reporting guide is a typical example of how banks describe the service. It lists prior day and current day reports of balances and activity, delivered daily or hourly, and lets customers download account details in BAI2 from online banking. The full rules come from the BAI Cash Management Balance Reporting Specifications Version 2 manual, which the bank guide points to.
Two things follow for anyone trying to get one into accounting software:
- It is written for treasury systems, not people. There are no column headings and no friendly dates.
- QuickBooks Online's bank upload does not list it. You convert it, or you ask the bank for another export.
Before converting, check the bank's download screen for a CSV, QBO or OFX option for the same account. If one exists, it is the shorter road. The trade-offs between the two QuickBooks-friendly choices are set out in CSV versus QBO for a bank import.
BAI2 file format record codes from 01 to 99
Every line in a BAI2 file starts with a two-digit record code that says what the line is. The file is nested: a file header, then groups, then accounts inside each group, then transactions inside each account, each level closed by a trailer. Fields are separated by commas, and a slash ends a record that has no text.
| Code | Record | What it holds |
|---|---|---|
| 01 | File header | Sender, receiver, creation date and time, file ID, version number 2 |
| 02 | Group header | Originating bank, group status, the as-of date, currency |
| 03 | Account identifier | Account number, currency, then pairs of balance type codes and amounts |
| 16 | Transaction detail | Type code, amount, funds type, bank reference, customer reference, text |
| 88 | Continuation | More of the record above, usually extra text |
| 49 | Account trailer | Account control total and the number of records in the account |
| 98 | Group trailer | Group control total, number of accounts, number of records |
| 99 | File trailer | File control total, number of groups, number of records |
An annotated sample file
Here is a small example file for one checking account. Every value is made up for illustration.
01,121000999,SMALLCO,260515,0600,1,,,2/
02,SMALLCO,121000999,1,260514,,USD,2/
03,0012345678,USD,010,1250000,,,015,1372550,,/
16,165,150000,0,BR0001,,CLIENT ACH PAYMENT
16,475,7450,0,BR0002,1052,CHECK PAID
88,CITY WATER DEPT
16,455,20000,0,BR0003,,INSURANCE PREMIUM
49,2800000,6/
98,2800000,1,8/
99,2800000,1,10/
Line by line:
- 01: the bank (121000999) sends the file to SMALLCO, created May 15, 2026 at 6:00 AM, file number 1, in version 2.
- 02: a group from the bank, status 1 (an update), as of May 14, 2026, in US dollars. The final 2 marks it as final previous-day data.
- 03: account 0012345678. Type code 010, the opening ledger balance, is 1250000, which is 12,500.00. Type code 015, the closing ledger balance, is 13,725.50.
- 16 to 88: three transactions, the second with a continuation line.
- 49, 98, 99: the trailers that close the account, the group and the file.
The values in the 03 record repeat in pairs. After each type code and amount come an item count and a funds type, and empty fields show as adjacent commas.
Reading a BAI2 16 record line by line
A BAI2 16 record holds one transaction in up to seven fields: the record code, a type code that says what kind of transaction it is, the amount, a funds type, the bank's reference, the customer's reference and free text. Reading one is a matter of splitting at the commas and looking up the type code.
Take the second transaction in the sample:
16,475,7450,0,BR0002,1052,CHECK PAID
88,CITY WATER DEPT
| Field | Value | Meaning |
|---|---|---|
| Record code | 16 | Transaction detail |
| Type code | 475 | Check paid, a debit |
| Amount | 7450 | 74.50, with two implied decimals |
| Funds type | 0 | Immediate availability |
| Bank reference | BR0002 | The bank's own ID for this item |
| Customer reference | 1052 | The check number |
| Text | CHECK PAID | Description, continued on the 88 line |
The BAI manual says the customer reference holds the check number for type codes 474, 475 and 395, which is why 1052 sits there. The 88 line carries on the text field, so the full description is "CHECK PAID CITY WATER DEPT".
Text is the one field allowed to contain commas and slashes. The manual's sample text records have no closing slash, but some banks add one, so strip a trailing slash if it is there rather than assume either way. A careless split at every comma will cut a description such as "SMITH, JONES AND CO" into pieces. Split only the first six commas and treat the rest of the line as text.
The type codes for the other two lines in the sample are 165, a preauthorized ACH credit, and 455, a preauthorized ACH debit. Banks publish their own lists of the codes they use, and some add custom codes of their own.
Why are BAI2 amounts unsigned and missing decimal points?
BAI2 amounts carry no decimal point because the currency sets the decimals; for US dollars the last two digits are cents, so 7450 is 74.50. They carry no sign because the BAI manual describes 16 record amounts as always positive. Whether money came in or went out is told by the type code, not by the number.
The BAI manual sets the type code ranges:
| Type code range | Meaning |
|---|---|
| 001 to 099 | Account status, such as ledger and available balances |
| 100 | Total credits summary |
| 101 to 399 | Credit summary and detail codes: money in |
| 400 | Total debits summary |
| 401 to 699 | Debit summary and detail codes: money out |
| 700 to 799 | Loan summary and detail codes |
The mistake this causes
The common error is to copy BAI2 amounts straight into a CSV amount column. Every transaction then reads as a deposit, the checks and fees included, and the account balance in QuickBooks or Xero grows by the very amounts that should have reduced it. The import itself raises no error, so the problem only appears at reconciliation.
The fix is a rule applied to every 16 record:
- Divide the amount by 100 for US dollars.
- If the type code is from 401 to 699, make the amount negative.
- If it is from 101 to 399, keep it positive.
- Set aside any other code, such as 890, which the manual defines as an information message with no amount, and look at it by hand.
Status amounts in the 03 record follow different rules and may carry a plus or minus sign. Do not apply the 16 record rule to them.
The BAI2 as-of date and transaction dates
A BAI2 16 record has no date field of its own. Each transaction takes the as-of date from the 02 group header above it, written as YYMMDD. In the sample, 260514 means May 14, 2026, so all three transactions are dated May 14. Converting a file therefore means carrying that group date down to every row.
Three details to watch:
- Two-digit years. 260514 is 2026, not 1926. Some spreadsheet tools guess the century wrongly, so write the year in full when you rebuild the date.
- Creation date versus as-of date. The 01 header has the date the file was made, May 15 in the sample. The transactions belong to the as-of date in the 02 record, May 14. Using the wrong one moves every line by a day, which can push month-end lines into the next month.
- Same-day files. The as-of date modifier at the end of the 02 record says whether data is previous-day or same-day, and interim or final. A same-day interim file may list items that later change. For bookkeeping, use final previous-day files.
If one file holds several groups, each group can have its own as-of date. Take the date from the nearest 02 record above each transaction.
Turning a BAI2 file into CSV or QBO for QuickBooks or Xero
To turn a BAI2 file into an import file, rebuild every 16 record as a CSV row with a date, a description and a signed amount, then upload the CSV or convert it to QBO. A spreadsheet can do this for a short file. For a regular monthly job, a small script or a BAI2 converter saves the hand work.
Steps in a spreadsheet
- Copy the file and open the copy in a text editor. Confirm it starts with 01 and ends with 99.
- Keep one account. If the file has several 03 records, copy the lines from one 03 to its 49 into a separate file.
- Join continuation lines. Add each 88 line's text to the end of the 16 line above it.
- Import into Excel with Data, then From Text/CSV, comma separated, every column as text.
- Delete the header and trailer rows, keeping only the 16 rows.
- Build the columns: Date from the 02 as-of date, Description from the text and customer reference, Amount from the amount divided by 100 and signed by the type code range.
- Save as CSV and check the result, as in the next section.
Where the CSV goes next
- QuickBooks Online. Intuit's page on uploading transactions manually to QuickBooks Online names CSV among the accepted types for a transaction list.
- QuickBooks Desktop. Desktop's bank import takes a QBO file, so turn the CSV into QBO. The header change that makes a bank file acceptable to QuickBooks is shown in what a QFX to QBO converter changes.
- Xero. Lay the CSV out the way Xero's statement import expects its CSV columns.
- An older program that reads QIF. The same rows can become a QIF file. What that format loses on the way is described in converting QFX to QIF.
If you are in the middle of moving books between systems, decide first which one will hold the bank history. Switching from Xero to QuickBooks Online covers that choice.
SimpleBankConvert reads PDF bank statements, not BAI2 files. If your bank can give you the same months as PDF statements, it is being built to turn those into a CSV or QBO file, and the homepage shows what to expect.
BAI2 trailer control totals as a conversion check
BAI2 trailer records exist so a reader can prove the file arrived whole. The 49 account trailer carries an account control total, which the BAI manual defines as the algebraic sum of every amount in the 03 record and in the 16 and 88 records for that account, plus a count of records. Recompute both before you trust a conversion.
Checking the sample
Add every amount in the account, using the raw unsigned values from the file:
| Record | Amount field |
|---|---|
| 03, opening ledger (010) | 1250000 |
| 03, closing ledger (015) | 1372550 |
| 16, type 165 | 150000 |
| 16, type 475 | 7450 |
| 16, type 455 | 20000 |
| Total | 2800000 |
The total matches the 49 record. The record count of 6 counts the 03, the three 16s, the 88 and the 49 itself. The 98 and 99 trailers repeat the same total one level up, with their own record counts.
Note that the control total is a checksum, not a balance. Debits are added rather than subtracted. To check the money itself, use the balances:
- Start from the opening ledger balance, 12,500.00.
- Add the converted, signed CSV amounts: 1,500.00, minus 74.50, minus 200.00.
- The result, 13,725.50, should equal the closing ledger balance from type code 015.
If the control total matches but the balance check fails, a sign is wrong in your CSV. If the control total fails, a record was lost when the file was copied or split. Either way, fix the CSV before it reaches your books.