← Building a solo company with AI agents

Part 13 of 40 · 7 min read

Procure to pay: an approved need is not an approved payment

Experimental · Work in progress · Internal R&D

Separate an approved need, purchase order, receipt, invoice match and payment instruction; walk through the local purchasing workflow.

Arunank Sharan · Kuzushi Labs ·

In this article

A solo company still buys software, cloud capacity, contractor time and equipment. The administrative work is easy to underestimate: establish the need, approve the supplier commitment, confirm what was received, check the invoice and verify payment. This loop keeps those steps connected.

The prototype already models requisitions, purchase orders, receipts, vendor invoices and payments. Its end-to-end fixtures exercise the state changes and the cost-side handoff to the financial ledger. No bank transfer or live procurement service is connected by those fixtures.

Why three-way matching helps

For a simple purchase, I want the purchase order, receipt and invoice to agree. In the local implementation, matching includes an amount-tolerance check using a 2% prototype threshold. The broader production check would also need supplier identity, line items, quantity, currency, tax and purchase-order scope.

Consider a fixture with a S$1,000 purchase order:

EvidenceExample valueConsequence
Approved purchase order1,000Maximum agreed basis for the example
Full receiptRecordedReceipt prerequisite available
Invoice A1,010Within the local amount tolerance
Invoice B1,080Matching exception; review needed
Different currencyUSD instead of SGDDo not compare raw amounts

The 2% value is an experimental configuration, not permission to overpay every invoice. A zero-value order, partial delivery or a service milestone needs its own handling.

Follow the records

Requisition → approved purchase order → receipt
  → parsed invoice → matching
  → approved invoice → scheduled payment
  → submitted payment → confirmed settlement

The prototype can send a mismatched invoice to review. It also represents payment failure separately from confirmation. That distinction should survive the future live connector: an API accepting a payout request is not evidence that the beneficiary received the money.

Existing local interfaces make both branches inspectable:

from p2p.pipeline import build_p2p_pipeline

buying, dispatcher = build_p2p_pipeline()
seed = buying.seed("studio-demo", amount=12000.0)
approval = buying.run_to_approval(seed, tenant_id="studio-demo")
assert approval.allowed
# Local settlement fixture, not a bank call:
confirmation = buying.pay(seed, tenant_id="studio-demo", settled=True)
assert "p2p.payment.confirmed" in confirmation.emitted

What the agent contributes

An agent can extract invoice fields, identify a likely purchase-order match, explain discrepancies and prepare the payment request. Deterministic checks should perform the actual arithmetic and compare the relevant IDs. A confident extraction with the wrong supplier bank account is still wrong.

For contractor work, proof of receipt might be an accepted milestone rather than a warehouse receipt. For recurring software, it may be the subscription period and approved renewal. Those are domain extensions over the same purchase lifecycle, not reasons to collapse receipt and invoice into one object.

Separate recommendation, approval and execution

A solo founder cannot create genuine independent review by naming two agents “buyer” and “approver.” The founder can approve bounded purchases, use an external accountant or second authorised person where needed, or explicitly retain manual control for higher-risk actions. The permission model should reflect the real organisation.

Before a payment, the operator should see supplier identity, approved account details, invoice hash, received evidence, amount, currency and earlier approvals. A change to beneficiary details should invalidate the prior payment approval. That is a different event from editing the invoice description.

The finance handoff is already demonstrated locally

An approved vendor invoice can produce an expense/payable posting. A confirmed payment can reduce the payable and cash balances. The tests connect those events on the same object runtime and check that the trial balance remains balanced.

The consumers are currently invoked explicitly in the tests. A live implementation must add durable subscription state and deduplication so a repeated approval event does not book the expense twice. The accounting treatment also needs to reflect the actual purchase; a fixed-asset purchase and a routine operating expense cannot always share one posting template.

The first useful founder view

I would start with three queues: needs awaiting approval, invoice mismatches, and payments with unknown or failed outcomes. Each queue should show the specific next decision. That is a better first release than a general agent allowed to browse supplier sites and spend money.

The outcome measures are duplicate payments, unmatched invoices, unresolved settlement age and time spent gathering approval evidence. The goal is controlled buying with less coordination work, not a larger number of automatically approved expenses.

Match identities before amounts

A near-identical amount is not enough to match an invoice. The supplier, currency and purchase-order identity must agree before a tolerance calculation is useful. A document parser may confidently extract the wrong account number from a footer or an older invoice, so the extracted values need provenance and comparison against the approved supplier record.

from company_lab.business import match_invoice

po = {"supplier": "vendor-4", "currency": "SGD", "order": "po-17",
      "amount_minor": 100000}
receipt = {**po, "quantity": 1}
invoice = {**receipt, "amount_minor": 101000}
assert match_invoice(po, receipt, invoice) == "MATCHED"
assert match_invoice(po, receipt, {**invoice, "supplier": "vendor-9"}) == "REVIEW_IDENTITY"

This reference rule is for a simple full-order fixture. Partial receipts, multiple invoice lines, taxes, service milestones and revised purchase orders need more detailed matching. The point is to make those assumptions explicit rather than hide them in a model-generated “match confidence.”

Persist extraction as a proposal with evidence

For each invoice field, retain the original file hash, page and text region, extracted value, parser version and any reviewer correction. The agent can propose a supplier match, but the operator should be able to open the exact source when the match is uncertain.

{
  "invoice": "vendor-invoice-17",
  "field": "amount_due",
  "candidate_minor": 101000,
  "currency": "SGD",
  "source": {"document_hash": "example-hash", "page": 1, "label": "Total due"},
  "review": "MATCHED_TO_PO_WITHIN_EXAMPLE_TOLERANCE"
}

A source hash prevents a later attachment replacement from silently changing the evidence. If the supplier sends a corrected invoice, record a new version and explain which prior proposal it supersedes. Do not overwrite the earlier document and leave an approval pointing to a different file.

The purchasing path matches supplier, receipt and invoice evidence before preparing a separately authorised payment.

Beneficiary changes need a separate workflow

Suppose an otherwise correct invoice includes new bank details. The purchasing agent should flag the difference against the approved supplier profile. It must not update that profile and use the updated details to make its own mismatch disappear.

The payment approval binds the beneficiary version as well as amount and currency. An authorised supplier-change workflow establishes the new details using the business's verification process. Only then can a payment proposal reference that version. This protects against both extraction mistakes and instructions embedded in a received document.

For a solo founder, this does not require pretending that two automated agents form independent financial oversight. The operator can retain manual review for supplier creation and account changes while automating evidence collection and routine matching.

Keep procurement and payment connectors independent

The document connector retrieves the invoice. The project or delivery connector supplies received-service evidence. The accounting connector records the payable. The payment connector submits an approved instruction and later reports settlement. Each returns its own receipt, linked to the same purchasing obligation.

A payment submission can succeed while settlement remains pending. An accounting posting can fail after the provider payment completes. Those cases should create visible reconciliation work, not roll back the story to “nothing happened.” The platform needs enough state to explain which external system is ahead of which local projection.

Test what can cost real money

The first fixture matrix should include a duplicate invoice, changed supplier identity, quantity above the receipt, a 2.01% amount difference against a 2% example tolerance, different currency, partial settlement and a timeout after provider acceptance. A source document containing “ignore the existing approval” must remain ordinary untrusted content.

A useful exercise is to deliver the same invoice through email and document storage with different transport IDs. The economic invoice identity should still be recognised. Deduplicating only the incoming message would let the same payable be created twice. A reviewed supplier invoice number plus supplier identity is one possible business key, with explicit handling for missing or reused numbers.