Arron Bennett, Founder of Bennett Financials
At a seed-stage startup, accounts payable usually means one person, an inbox and a shared spreadsheet. That works at 20 bills a month. At 140 it starts to break: bills get paid twice, a new vendor's bank details go unchecked, and nobody knows what's due next Friday until the bank balance says so.
Accounts payable automation doesn't have to start with a platform. It starts with rules. Write the rules down, run every bill through them, and the inbox turns into a short queue of the bills that need a person.
This post builds that rules engine in plain Python, standard library only, for a hypothetical seed-stage agritech startup selling sensor analytics to farms. It has 19 people and about 140 vendor bills a month. Every figure is example data.
Key takeaways
- Write approval rules before you buy an AP tool. The rules are the hard part; the software just runs them.
- Check every bill for duplicates by vendor and invoice number, and flag the same amount from the same vendor within a week.
- Hold every bill from a new vendor until someone verifies the bank details through a known contact.
- Schedule approved bills into weekly pay runs by due date, so you see the cash going out before it goes.
What is accounts payable automation for a startup?
Accounts payable automation means running every vendor bill through the same rules: check it isn't a duplicate, confirm the vendor is known, route it to the right approver by amount, and schedule payment by due date. For a small startup, a script and a shared inbox can do that before any dedicated AP platform is worth paying for.
The point isn't speed for its own sake. Approved bills you haven't paid yet are accounts payable, a current liability on the balance sheet. When AP is a pile of emails, you can't see that liability, so the cash flow statement surprises you every month.
What approval rules should a small startup use?
Three amount bands and three holds. Bills under $2,500 from a known vendor are approved automatically. Bills from $2,500 to $10,000 go to the budget owner. Anything over $10,000 goes to the CEO. Duplicates, repeat amounts within seven days, and any bill from a vendor not yet on file are held for a person.
The thresholds are examples. Set yours so that most bills by count clear automatically and most dollars get a human look. In this sample, five of the twelve bills clear on their own, about $5,137 of the $51,453.
The new-vendor hold is the one that matters most. A changed bank account on an invoice is a classic fraud pattern, and the fix is boring: call the vendor on a number you already have, not the one on the invoice. That's one of the internal controls every growing company needs, and this workflow builds it in.
This short isn't about payables. It's the order I use for automating any admin process: automate first, train second, change roles last, never the reverse. An approval workflow is the automate step. Train the approvers on it before anyone's job changes.
How do you build the approval workflow in Python?
Load the bills, keep a set of vendor and invoice-number pairs you've already seen, and test each bill against the rules in order: duplicate first, new vendor second, repeat amount third, then the amount bands. Record the route for each bill, then group the approvable bills into weekly pay runs on the last Friday before each due date.
The sample below is twelve bills from one week, inline so you can paste it and run it. No dependencies.
from collections import defaultdict from datetime import date, timedelta VENDORS = {"CloudHost", "Northfield Sensors", "FieldTech Freight", "AgriLab Testing", "Harlow Pike Legal", "Prairie Office Supply"} AUTO_LIMIT, CEO_LIMIT = 2_500, 10_000 bills = [ # vendor, invoice number, amount, invoice date, due date ("CloudHost", "CH-88412", 6_840.00, "2026-11-01", "2026-11-30"), ("Northfield Sensors", "NS-2291", 18_400.00, "2026-11-02", "2026-12-02"), ("FieldTech Freight", "FF-7710", 1_265.50, "2026-11-03", "2026-11-17"), ("AgriLab Testing", "AL-0457", 2_150.00, "2026-11-03", "2026-11-18"), ("Prairie Office Supply", "PO-5532", 389.20, "2026-11-04", "2026-11-19"), ("FieldTech Freight", "FF-7710", 1_265.50, "2026-11-05", "2026-11-17"), ("Harlow Pike Legal", "HP-1180", 4_500.00, "2026-11-05", "2026-11-20"), ("Kestrel Design", "KD-001", 3_200.00, "2026-11-06", "2026-11-21"), ("AgriLab Testing", "AL-0461", 2_150.00, "2026-11-06", "2026-11-21"), ("Prairie Office Supply", "PO-5547", 212.75, "2026-11-06", "2026-11-21"), ("Northfield Sensors", "NS-2302", 9_960.00, "2026-11-07", "2026-12-07"), ("CloudHost", "CH-88519", 1_120.00, "2026-11-07", "2026-11-22"), ] ROUTES = ["auto-approve", "budget owner", "CEO", "REVIEW: same amount within 7 days", "HOLD: duplicate invoice number", "HOLD: new vendor, verify bank details"] def route_for(vendor, inv, amount, inv_date, seen, recent): if (vendor, inv) in seen: return ROUTES[4] if vendor not in VENDORS: return ROUTES[5] if any(a == amount and abs((inv_date - d).days) <= 7 for a, d in recent[vendor]): return ROUTES[3] if amount < AUTO_LIMIT: return ROUTES[0] return ROUTES[1] if amount <= CEO_LIMIT else ROUTES[2] def pay_friday(due): # weekly pay run: the last Friday on or before the due date return due - timedelta(days=(due.weekday() - 4) % 7) seen, recent, queue = set(), defaultdict(list), defaultdict(list) for vendor, inv, amount, inv_s, due_s in bills: inv_date, due = date.fromisoformat(inv_s), date.fromisoformat(due_s) route = route_for(vendor, inv, amount, inv_date, seen, recent) seen.add((vendor, inv)) recent[vendor].append((amount, inv_date)) queue[route].append((amount, due)) print(f"{vendor:<22}{inv:<9}{amount:>10,.2f} -> {route}") print(f"\n{len(bills)} bills, {sum(b[2] for b in bills):,.2f} total") for route in ROUTES: if queue[route]: print(f" {route:<38}{len(queue[route]):>3}{sum(a for a, _ in queue[route]):>11,.2f}") runs = defaultdict(float) for route in ROUTES[:3]: for amount, due in queue[route]: runs[pay_friday(due)] += amount print("\npay runs for approvable bills:") for friday in sorted(runs): print(f" {friday:%a %d %b}{runs[friday]:>11,.2f}") Output:
CloudHost CH-88412 6,840.00 -> budget owner Northfield Sensors NS-2291 18,400.00 -> CEO FieldTech Freight FF-7710 1,265.50 -> auto-approve AgriLab Testing AL-0457 2,150.00 -> auto-approve Prairie Office Supply PO-5532 389.20 -> auto-approve FieldTech Freight FF-7710 1,265.50 -> HOLD: duplicate invoice number Harlow Pike Legal HP-1180 4,500.00 -> budget owner Kestrel Design KD-001 3,200.00 -> HOLD: new vendor, verify bank details AgriLab Testing AL-0461 2,150.00 -> REVIEW: same amount within 7 days Prairie Office Supply PO-5547 212.75 -> auto-approve Northfield Sensors NS-2302 9,960.00 -> budget owner CloudHost CH-88519 1,120.00 -> auto-approve 12 bills, 51,452.95 total auto-approve 5 5,137.45 budget owner 3 21,300.00 CEO 1 18,400.00 REVIEW: same amount within 7 days 1 2,150.00 HOLD: duplicate invoice number 1 1,265.50 HOLD: new vendor, verify bank details 1 3,200.00 pay runs for approvable bills: Fri 13 Nov 3,804.70 Fri 20 Nov 5,832.75 Fri 27 Nov 25,240.00 Fri 04 Dec 9,960.00 The order of the rules matters. The duplicate check runs first, so the second FF-7710 is held even though it's small enough to auto-approve. The new-vendor check runs before the amount bands, so Kestrel Design is held at $3,200 instead of going to a budget owner who can't verify bank details.
What does the output tell the finance team?
That only a small share of the week needs real attention. Twelve bills come in. Five clear automatically. Four go to approvers by amount. Three are held: a duplicate FF-7710 for $1,265.50, a second AgriLab bill for the same $2,150 within three days, and $3,200 from a vendor not yet on file. That's the review queue.
The pay runs are the second output. Approvable bills total $44,837.45, spread over four Fridays: $3,804.70 on 13 Nov, $5,832.75 on 20 Nov, $25,240.00 on 27 Nov and $9,960.00 on 4 Dec. The 27 Nov run is the big one because the $18,400 sensor parts bill and the $6,840 cloud bill land together. That's the number to put in the cash forecast now, not the week it's due.
A few things I'd add as the volume grows:
- Match to purchase orders. If you issue POs for hardware, check each bill against an open PO before it reaches an approver. Open purchase orders are cash you've already committed, even before the bill arrives.
- Watch amounts just under a limit. The $9,960 bill sits just below the $10,000 CEO line. Usually that's nothing. A pattern of them is worth a look.
- Store the decisions. Write each route, approver and timestamp to a file or table. That's your audit trail at year end.
The accounting doesn't change. Approved bills still go into accounts payable, and the expense still hits the income statement in the month the cost was incurred, not the month you pay. The workflow just makes the liability visible.
What should you do on Monday?
Export last month's bills from your inbox or accounting system with vendor, invoice number, amount and due date. Write down your approval bands and your list of known vendors. Run the bills through the script and count how many it holds. Then agree the rules with whoever approves spend before you automate anything further.
FAQ
What should an AP approval workflow check first?
Duplicates, then new vendors. Both are cheap to catch before payment and expensive to fix after the money has gone.
When is it worth buying an accounts payable tool?
When the volume or the number of approvers outgrows a script and a shared inbox. By then you'll know your rules, which makes setting up any tool faster.
Should startups pay bills early?
Usually on the due date, not before, unless a vendor offers a discount worth more than keeping the cash. Scheduling by due date keeps cash in the bank longer.
![Path Planning and Autonomous Navigation — Ch.25: Future of Autonomous Navigation [KR]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjs5djnow7ktieu1tmsv0.png)

