bank-deposit-lane

Notes

  • Bank deposit lane, shipped 2026-11-02 (entirely-api f8acec0 merge, deployed to api/worker/scheduler).

WHY: Ramp payout PYKMMND2NJ ($13,300, 10/27) was recorded and its six Manassas invoices marked Paid off the payout notice, while the cash was not in the BofA account. Nothing in the stack could tell the difference, because nothing read the bank.

WHAT: three modules in /opt/unlikely-api/services/ — bank_deposit_parser.py (BofA eAlert + transaction CSV + pasted activity rows into one deposit contract), bank_deposits.py (the bank_deposits table in payment_tracking.db, the payout matcher, the alert), bank_deposit_inbox.py (hourly Gmail lane that archives every credit-shaped alert to /data/bank-deposits/ even when it fails to parse). Plus scripts/seed_bank_deposits.py.

Scheduler: bank_deposit_inbox hourly Mon-Fri 7-21 ET; bank_deposit_unlanded_check 09:30 ET weekdays, silent when everything landed, posting to ops with a Dustin DM fallback.

INVARIANT: the lane never writes invoice_status. Reading the bank must not be able to pay an invoice, and failing to read it must not be able to un-pay one.

Match tiers: Ramp Payment ID from the ACH descriptor, then truncated invoice number plus amount agreement, then amount within 8 days of the payout. The weakest tier requires a deposit positively tagged to business account 9299, because the same mailbox carries personal Zelle alerts.

Coverage floor: a payout older than the earliest deposit on file is reported “uncovered”, not missing. Without it the first real run called six September payouts ($119,600) missing when they had in fact landed before the backfill begins.

Live: 14 deposits seeded from the 10/02-10/30 activity paste, 13 of 14 payouts matched (5 by Payment ID, 8 by invoice token), PYKMMND2NJ the only unlanded payout. 67 tests.

OPEN: Dustin must enable the BofA business alert “Direct deposit credited to your account” (currently Off, no threshold). Until then the lane sees only mobile-deposit and Zelle alerts and the ledger is fed by paste or CSV import.