Part 4 of 7 · Supplier bill matcher series ~5 min read

How a mismatch reaches the right person

The matcher picked an outcome — matched, price variance, quantity variance, or no PO. Now the approval-desk Lambda has to get the bill in front of the right person, at a sensible time, with enough detail that they can decide in seconds. Get any of that wrong and the message is worse than none: a bill sent to someone who left, a flag with no explanation, a sign-off request that skips the second approver a big payment needs. Four small guardrails sit between the outcome and the bill landing with a person.

Key takeaways

  • Approver resolution: per-supplier override beats per-category default beats fallback to the finance admin.
  • Big-spend bills require a second approver; the rules doc holds the threshold.
  • Quiet hours defer non-urgent notifications to the next business hour.
  • Every bill ships with the exact failing line, the billed-vs-agreed numbers, and Approve, Query, Reject.
  • A no-PO bill always goes to a human and never clears on its own.

Four guardrails on every bill

Four guardrails between the matcher's outcome and the bill landing A horizontal flow diagram. On the far left, an "Outcome chosen" box: the matcher emitted an event with the bill, the supplier, and one of four outcomes — matched, price variance, quantity variance, or no PO. Four guardrail gates sit in a row to the right, each drawn as a vertical bar. Gate 1: Resolve approver — looks up the per-supplier approver first; if blank, falls back to the per-category approver from the rules doc; if still blank, falls back to the finance admin. Returns an email address. Gate 2: Spend threshold — checks the bill total against the rules-doc threshold; if it's above the limit, a second approver is added so a large payment needs two sign-offs. Gate 3: Quiet hours — checks the local time against the quiet-hours window; a clean matched bill that isn't urgent is deferred to the next business hour via an EventBridge Scheduler one-off, while a no-PO or large variance can be marked urgent and sent now. Gate 4: Final compose — formats the email from the template for the outcome, attaches the exact failing line with the billed-versus-agreed numbers and the dollar gap, links the bill PDF and the purchase order, and adds Approve, Query, and Reject buttons. After all four gates pass, the bill ships via SES outbound to the resolved approver, with the action buttons backed by a Function URL. A note at the bottom: every send is logged to DynamoDB so the approval desk knows the bill is waiting and the daily sweep can chase it if it ages. Outcome matched, price, quantity, or no PO Gate 1 Resolve approver supplier override? category default? admin fallback? returns the approver email Gate 2 Spend threshold total above the limit? if so, add a second sign-off approver Gate 3 Quiet hours local time in business window? if not, defer via Scheduler Gate 4 Compose + context email template for outcome + failing line, gap, PO link, 3 buttons Ship the bill — SES outbound email to the resolved approver Approve / Query / Reject buttons backed by a Function URL every send logged to DDB bm-queue — the daily sweep chases it if it ages Every gate is a deterministic check — no model calls, no surprise behavior on a Tuesday in April.
Fig 4. Four guardrails between the outcome and the bill landing with a person. Resolve the approver. Add a second sign-off for big spend. Honor quiet hours. Compose with the exact line and gap. Then ship via email and log it so nothing ages out unseen.

Gate 1: resolve the approver

Three places the approval desk looks for the right person, in order. First, the rules doc’s per-supplier approver — if a supplier is assigned to a specific buyer, that buyer owns its bills regardless of category. Second, the per-category default (“all packaging bills go to the operations manager”). Third, the finance admin fallback — whoever set the matcher up and catches anything unrouted. The fallback should rarely fire in steady state; when it does, the monthly summary names every bill that hit it so the rules doc can be tidied.

Approvers are reached by email. The matcher deliberately uses email rather than a chat tool here, because a supplier-payment decision belongs in a place with a durable record and a clear from-address — and because the people who approve supplier bills (a finance manager, an owner) live in their inbox, not a chat channel.

Gate 2: a second sign-off for big spend

Not every bill should be one person’s decision. A $200 stationery bill is fine for one approver; a $40,000 equipment bill is not. The rules doc sets a spend threshold (say $10,000). Above it, Gate 2 adds a second approver, and the bill isn’t cleared for payment until both have approved. This is the one place the matcher quietly enforces a control that’s easy for a busy team to skip: large payments get two sets of eyes, automatically, without anyone having to remember the policy.

The second approver is named per category in the rules doc — usually the owner or the finance lead. If a bill needs two sign-offs and only one comes back, the bill stays in a waiting state and the daily sweep keeps it visible until the second arrives.

Gate 3: quiet hours

Most bills aren’t urgent. A clean matched bill can wait until morning; a small price variance can too. Gate 3 reads the quiet-hours setting from the rules doc (default 6pm to 8am) and, if a non-urgent bill would land outside business hours, defers it with a one-off EventBridge Scheduler rule that re-sends at the next business minute. The exceptions are bills the rules mark urgent — a no-PO bill, or a variance above a large-dollar line — which go out immediately so a genuine problem isn’t sitting unseen overnight.

Gate 4: compose with the exact gap, then ship

The template doc has one email layout per outcome. A matched bill gets a short “ready to approve” note with the supplier, total, and PO. A flagged bill gets the specifics: the exact line, the billed value, the agreed value, and the gap — “Line 3, item BOLT-12: billed 100 units at $0.40, dock received 84; you’re being charged for 16 units that didn’t arrive ($6.40).” The point is that the approver never has to open three documents and do the arithmetic themselves; the matcher already did it and shows its work.

Each email carries three buttons — Approve, Query, Reject — that hit the ack-handler Function URL when clicked. The bill PDF and the matching purchase order are linked inline so the approver can check the source in one click if they want to. Then the bill ships via SES outbound, and a row is written to bm-queue in DynamoDB marking it as waiting on this approver. The next post covers what each of those three buttons actually does.

Why the guardrails exist

None of these gates are exotic. They’re the care a thoughtful finance person would take by hand — send it to whoever owns this supplier, get a second pair of eyes on the big ones, don’t email at 11pm unless it’s actually urgent, and spell out the problem so the approver can decide without digging. Putting them in code as four small sequential gates makes them part of the design, not something you’re trusting a busy week to remember.

Next post: the three things an approver can do — approve, query, reject — and how each one updates the bill, the supplier, and the audit trail without ever paying a bill on its own.

All posts