Skip to main content
A bank feed is two things bolted together: the transport that gets transactions out of the bank, and the queue that turns them into coded postings. Only the transport needs a network. On a workstation the transport is the file you already download from the bank — .qfx, .ofx, .qbo, or CSV. This works on a machine with no bank connection and, behind a client’s firewall, no way to have one. A live connection is available as a paid add-on and fills the same queue; nothing after arrival can tell the two apart.

Setting up an account

Once per bank or card account, name the QuickBooks account its lines belong in: “Set up the Amex Platinum feed for Ridgeline. Its lines post to the Amex Platinum credit card account.” Until an account is set up, there is nowhere for its lines to go. qb_feed_accounts lists what is registered and how many lines sit in each state.

Importing a statement

Two steps, on purpose. CSV column detection is a guess about somebody else’s spreadsheet, and a wrong guess quietly misfiles a month:
1

Preview it

qb_preview_statement reads the file and reports which column was read as the date, the description, and the amount, plus how many rows parse and how many it would skip. It stages nothing.
2

Import it

qb_import_statement stages the rows and runs your coding rules over what is new.
Nothing leaves the workstation, and nothing reaches QuickBooks. The statement file is read where it sits.
Importing the same month twice does not double it. Lines are deduplicated three ways — by the bank’s own FITID where the format carries one, by a content hash, and by reference heuristics against what the queue already holds. Overlapping date ranges are recognised, not restaged.
Amounts are handled as exact decimals rather than as floating-point numbers. These lines become postings, and a figure that has been through a binary float is not evidence of anything.

Working the queue

qb_feed_lines returns the queue oldest first, and the console’s Bank feed tab shows the same queue as buckets: The page opens on what is open, because that is the only bucket that is work. Code a line with qb_code_feed_lines — the account, and optionally the payee and class. Nothing is written to QuickBooks by coding; you are filling in what a plan would post. qb_exclude_feed_line takes a line out without posting it and remembers that you did: a personal charge, a duplicate the bank will reverse.

What is already in the file

This is the part a hosted feed cannot do as well, and the mistake it exists to prevent is expensive: posting something the books already hold. It reconciles either way, so nobody notices. Zeno holds a local copy of the company file’s own transactions, so qb_match_feed can recognise a statement line as the check somebody hand-entered last Tuesday rather than posting it a second time. It proposes; it never decides. qb_accept_feed_match records that a line is something already posted, which takes it out of the queue without posting anything.

Posting what is left

qb_plan_feed turns the coded lines into an approval table — exactly the same one qb_plan_batch produces, for the same reason: “Plan the coded Amex lines for Ridgeline.” From there it is the ordinary loop. Read the table, approve it, apply it, and the run id undoes it. See your first batch. A planned line leaves the queue while its plan waits. Once the plan is decided, the line goes where the plan took it: Posted for every line the run put in the file, and back to Ready to plan for the rest — a run that failed a line, or a plan you rejected. A rejected feed plan hands its lines back so you can recode them and plan again; nothing stays stranded “in a plan” behind a decision that was already made. A feed that posted its own entries would be a second path into the company file, and a second path is a path where the gates are not.
A line’s content hash becomes its entry’s sourceRef, so a plan built twice from one statement carries the same idempotency key — and qb_explain_transaction reads that link back to you months later.

Where the queue lives

In the run journal, not in the local copy of the company file. That copy is disposable and gets rebuilt whenever it is wrong. A half-coded month of bank lines is work somebody did, and work does not live in a cache. On a shared journal it also means the person who imports the statement and the person who reviews it need not be the same person.

A live bank connection

Optional, paid, and the one part of this product that needs an account and a network. The direction is the point: transactions come in, and nothing about the company file goes out. The relay never learns a chart of accounts, a vendor name, how a line was coded, or which client it belongs to. It holds your bank connection, not your bank history, and forgets each transaction as soon as the workstation confirms it staged it.
1

Sign the workstation in

qb_relay_signin signs this workstation in to the feed. Every workstation on the same journal shares it.
2

Connect the bank

qb_connect_bank returns a link. The person who holds the banking credentials opens it in whatever browser they like, on whatever device, and signs in to the bank directly. The credentials never reach the workstation.Say where the feed should start. syncFromDate does two things at once: it asks the bank for history back to that date, and it skips anything dated before it, so a login connected in September for books you have kept from statements since June does not queue June again. Banks share up to two years, and they decide that once, when the login is connected — ask for everything the books will ever need. Without a date, the bank shares its last ninety days. The start can be moved later with qb_set_feed_start: earlier re-reads the bank’s history without doubling what is already staged, and reaches back as far as the bank agreed to when the login was made.
3

Choose the accounts

qb_select_bank_accounts picks which accounts sync and where each one’s lines post. One business login routinely carries a savings account and a personal account nobody will ever code — syncing those costs money and fills the queue with noise. Selection is what is billed.
4

Collect what arrives

qb_sync_bank_feed pulls what the bank has sent into the same queue a statement import fills. Once a line has arrived it is indistinguishable from one off a downloaded statement.
Everything is outbound. A bookkeeping PC sits behind a client’s firewall on a network nobody will open a port in, so nothing ever connects to the workstation.
A lapsed bank login fails silently. The sync keeps returning clean, empty pages, so a dead connection reads exactly like a slow month. qb_bank_connections says which have lapsed and when — treat it as work, not as a notice.
qb_disconnect_bank disconnects a login and stops paying for it. Lines already staged are left alone: disconnecting stops the future, it does not rewrite the past.

The bank balance beside the books

qb_bank_balance puts what the bank says each fed account holds — or, for a card, owes — next to the QuickBooks balance, both read when you ask: the bank’s from the feed’s last read of the bank, the books’ from QuickBooks itself. The two agree only in the moment a feed is fully drained and posted, so the point is not the gap but why: how much of it is lines at the bank not yet in QuickBooks (and of those, how many sit in a plan awaiting approval or are still uncoded), and how much the feed cannot account for — a cheque written and not yet cashed, or money at the bank before the feed’s start. A bank account in another currency is shown beside the QuickBooks figure rather than subtracted from it. The console’s feed page shows the bank’s half of this with a prompt that hands the comparison to the chat, since the console never opens a company file.