> ## Documentation Index
> Fetch the complete documentation index at: https://zenofirm.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# The bank feed

> Work a month of bank and card transactions as a queue instead of typing them in one at a time

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](#a-live-bank-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:

<Steps>
  <Step title="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.**
  </Step>

  <Step title="Import it">
    `qb_import_statement` stages the rows and runs your [coding
    rules](/docs/quickbooks-desktop/memory#coding-rules) over what is new.
  </Step>
</Steps>

Nothing leaves the workstation, and nothing reaches QuickBooks. The statement
file is read where it sits.

<Note>
  **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.
</Note>

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](/docs/quickbooks-desktop/console#the-bank-feed) shows the same queue as
buckets:

| Bucket                     | What is in it                                                        |
| -------------------------- | -------------------------------------------------------------------- |
| **Needs a decision**       | Nothing has said what these are. This is the work.                   |
| **Ready to plan**          | Coded, and eligible for the next plan.                               |
| **Already in the file**    | Matched to a transaction QuickBooks already holds. These never post. |
| **Dropped**                | Excluded, with the reason recorded.                                  |
| **In a plan** / **Posted** | The rest of the lifecycle.                                           |

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](/docs/quickbooks-desktop/questions) 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](/docs/quickbooks-desktop/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.

<Note>
  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.
</Note>

## Where the queue lives

In the [run journal](/docs/quickbooks-desktop/shared-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](/docs/quickbooks-desktop/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.

<Steps>
  <Step title="Sign the workstation in">
    `qb_relay_signin` signs this workstation in to the feed. Every workstation on
    the same journal shares it.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

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.

<Warning>
  **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.
</Warning>

`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.
