Skip to content
SKSuraj Kumar

Accounts payable

Accounts Payable Automation

Matches supplier invoices against purchase orders and receipts, routes only the exceptions, and moves approvals out of email into a policy-driven chain.

  • AI system
  • AI
  • Finance
  • Approvals
  • Next.js
Role
Sole engineer. Matching logic, approval policy engine, exception workflow and the payment run interface.
Work type
Private client project
Timeline
11 weeks build effort · 2025
Status
Case study published
Client
Private client work

Build effort, not elapsed calendar time. Engagements ran alongside study and employment, so this figure is the work itself rather than the span it sat in.

Interface built in code for this case study — not a client screen capture.

01Business problem

What was actually going wrong

Invoice approval lives in email threads, three-way matching is done by eye, and nobody can say what the company owes this month until someone rebuilds the number in a spreadsheet.

Who feels it

  • Finance teams chasing approvals by email
  • Budget holders asked to confirm receipt of goods
  • Directors approving amounts without spend context
  • Auditors reconstructing who approved what

Accounts payable in a mid-sized business is a chain of forwards. An invoice arrives, someone forwards it to the person who ordered the goods, who confirms receipt in a reply, who forwards it to finance, who checks it against a purchase order in another system, who forwards it to a director for sign-off. Every step is a place where the thread stalls and nobody knows whose turn it is.

The specific, expensive failures are consistent across companies: duplicate payments when the same invoice arrives twice through two channels; quantity and price mismatches against the purchase order caught only after the money has left; approvals given by someone without the authority for that amount; and no reliable view of accrued liabilities, because unapproved invoices are sitting in inboxes and not in a system.

This is a workflow problem more than a reading problem. Extraction gets an invoice into structured form, but the value is in what happens next — matching, exception handling, authority, and a payment run someone can defend.

02Product overview

What got built

An accounts payable workflow that captures invoices, matches them against purchase orders and goods receipts within configurable tolerance, detects duplicates before they are paid, routes exceptions to the right owner, and enforces an approval chain derived from spend policy.

Engineering notes

  • Approval chain resolved from policy and frozen per invoice
  • Cumulative-receipt three-way matching with per-category tolerance
  • Duplicate screening before approval, with recorded overrides
  • Segregation of duties as a resolver constraint, not a warning

Invoices are captured and extracted into a structured document, then immediately checked for duplication against existing records using supplier identity, amount, date proximity and invoice number similarity. A suspected duplicate is blocked and surfaced next to the record it resembles. It never enters the approval flow to be caught weeks later by a bank statement.

Matching compares invoice lines to purchase order lines and to goods received notes. Quantity and price tolerances are configurable per supplier category, and the result is not a pass or fail but a typed set of exceptions — over-billed quantity, price variance, no purchase order, partial receipt, unexpected charge line. Each exception type carries an owner role, so routing needs no human triage.

The approval chain is derived from policy; nobody picks it. Amount bands, cost centre, exception presence and segregation-of-duties constraints produce an ordered list of required approvals. A person who raised the purchase order cannot be the sole approver of the invoice against it, and an amount above a band requires the next level regardless of who is available. Cleared invoices collect into payment runs that export in the format the bank or ERP expects.

03Key features

What the software does, feature by feature

10 capabilities, of which 3 are model-backed. The rest are ordinary deterministic software, and the distinction is marked so the AI claim stays honest.

  • 01

    Multi-channel invoice capture

    Supplier email, upload and forwarded attachments enter one register with a processing state, so an invoice cannot sit unrecorded in an inbox.

  • 02

    Structured extractionmodel-backed

    Header fields and line items are extracted with confidence, and low-confidence values are held for review before matching runs.

  • 03

    Duplicate detectionmodel-backed

    Supplier, amount, date window and invoice-number similarity produce a duplicate risk score that blocks a payable before it enters approval.

  • 04

    Three-way matching

    Invoice lines are matched to purchase order lines and goods receipts with per-category quantity and price tolerance.

  • 05

    Typed exceptions

    Mismatches become named exception types with an owning role — never a generic failure that lands back on finance.

  • 06

    Policy approval chain

    Required approvals are derived from amount band, cost centre, exception presence and segregation-of-duties rules.

  • 07

    Delegation and escalation

    Out-of-office delegation is explicit and time-bounded, and an approval that ages past a threshold escalates; it does not wait silently.

  • 08

    Coding suggestionsmodel-backed

    Cost centre and expense account are suggested from supplier history and line description, with the suggestion always editable.

  • 09

    Payment runs

    Approved invoices batch into a run with due-date grouping, early-payment discount flags and an export for the bank or ERP.

  • 10

    Liability view

    Approved, pending and disputed totals by supplier and due period, sourced from the register and never reconstructed in a spreadsheet.

04User flow

How a person moves through it

The path from the trigger to the finished record, with each step attributed to whoever performs it — a person or the system.

  1. 01SupplierSends an invoice to the accounts payable address or it is uploaded by finance.
  2. 02SystemExtracts fields and line items, then checks for duplicates against the existing register.
  3. 03SystemMatches lines against the purchase order and goods receipt, producing typed exceptions where they disagree.
  4. 04SystemDerives the required approval chain from policy and notifies the first approver.
  5. 05Budget holderResolves any exception assigned to them — confirms receipt, accepts a price variance, or disputes the invoice.
  6. 06ApproverReviews the invoice with its matching result and spend context, then approves or rejects with a reason.
  7. 07FinanceBuilds a payment run from cleared invoices and exports it for payment.

05Architecture

How it is put together

The processing path first, then the layers it runs on, then the constraints that shaped both.

Path through the system

6 stages

  1. 01

    Capture

    Channel intake, register entry, processing state per invoice.

  2. 02

    Extract

    Header and line extraction with confidence and review hold.

  3. 03

    Screen

    Duplicate detection against supplier, amount, date and number similarity.

  4. 04

    Match

    Three-way match with per-category tolerance producing typed exceptions.

  5. 05

    Approve

    Policy-derived chain with delegation, escalation and segregation of duties.

  6. 06

    Pay

    Payment run batching, export, and posting back to the register.

Layers

Interface
Invoice register with state and exception filtersMatch inspector: invoice vs PO vs receiptApproval queue with spend contextPayment run builderSupplier liability summary
Application state
Invoice + line modelMatch engine (deterministic, tolerance-driven)Approval chain resolverException routing table
Services
Extraction call with invoice schemaDuplicate similarity scoringCoding suggestion from supplier historyExport serializers (bank, ERP)
Records
Invoice register + versionsPurchase orders and goods receiptsApproval policy configurationImmutable approval and payment audit log

Why it is shaped this way

  • Matching and approval are deterministic code. A finance controller has to be able to read the rule that held an invoice, and "the model decided" is not an answer that survives an audit.
  • The model does the parts that are genuinely language and pattern work: reading the document, judging invoice-number similarity for duplicates, and suggesting expense coding from history. Every one of those is presented as a suggestion or a risk score, never as an authority.
  • The approval log is append-only and separate from invoice state. Invoice state is a projection of the log, so an approval can never be quietly rewritten.

06Technical decisions

The choices that mattered, and what each one cost

Every decision here was contested by a reasonable alternative. The trade-off column is the part usually left out.

01

Derive the approval chain from policy at submission time and freeze it on the invoice.

Why

If the chain were recomputed on every render, a policy edit mid-approval would change who was required after some approvals were already given. Freezing the resolved chain keeps an in-flight approval coherent.

Trade-off

A genuine policy correction needs an explicit re-resolve action with its own audit entry. That is the correct amount of friction for changing who must sign off.

02

Model match failures as typed exceptions with owning roles, not as a boolean.

Why

A price variance and a missing purchase order need different people and different resolutions. Typing the exception makes routing automatic; without the type, everything falls back to finance.

Trade-off

The exception taxonomy has to be maintained as new failure modes appear, and an unrecognized mismatch needs a catch-all type that still routes somewhere.

03

Block suspected duplicates before approval, not flag them during it.

Why

A duplicate that enters the approval flow accumulates real approvals, and unwinding those is worse than a false block. Screening first makes the failure recoverable.

Trade-off

Legitimate similar invoices — recurring monthly charges of the same amount — get blocked and need a one-click override with a reason, which is recorded.

04

Enforce segregation of duties as a constraint on the resolver, not a warning in the interface.

Why

A warning gets clicked through. Making "requester cannot be sole approver" a constraint that the resolver must satisfy means the invalid state cannot be reached at all.

Trade-off

In a small team the constraint can be unsatisfiable, so the resolver returns an explicit blocked result naming the constraint that failed. It never degrades quietly.

05

Keep invoice state as a projection of an append-only event log.

Why

Accounts payable is the part of a business most likely to be audited and disputed. Reconstructing state from events means the history is the source of truth, not a side effect of it.

Trade-off

Reads need a fold or a maintained projection. The fold is centralized so no component reimplements it.

07Challenges

What was genuinely difficult

Not the setup work. These are the problems where the first implementation was wrong and had to be reconsidered.

01

Recurring invoices from the same supplier for identical amounts are indistinguishable from duplicates on the obvious signals.

Approach

Duplicate scoring combines supplier identity, amount equality, date proximity and normalized invoice-number similarity, and it treats a matching invoice number as decisive while treating amount-and-supplier alone as suspicious rather than conclusive. A known recurring-charge pattern per supplier further lowers the score.

Outcome

Genuine repeats pass through while true duplicates — the same invoice arriving by email and by upload — are caught before an approval is given.

02

Partial deliveries mean an invoice can be legitimately correct while not matching the purchase order.

Approach

Matching runs against cumulative received quantity across goods receipts rather than a single receipt, and tracks the remaining balance on each purchase order line. An invoice for a delivered part is a clean match; an invoice exceeding cumulative receipt is an over-billing exception.

Outcome

Staged deliveries stop generating exceptions that finance had to dismiss by hand. An exception queue is only worth reading while nobody has learned to ignore it.

03

Making the approval screen give an approver enough context to decide in seconds.

Approach

The approval view shows the invoice, the matching result, the supplier’s recent invoice history, the purchase order it draws against with its remaining balance, and the position of this approval in the chain. Everything is server-rendered in one pass, so the screen arrives complete and does not fill in piece by piece.

Outcome

Approvers stopped replying to ask what they were approving, which was the real bottleneck in the email version of this process.

08Business value

What it changes for the business

Stated qualitatively on purpose. Invented percentages are the easiest thing to put on a portfolio and the easiest thing to see through.

Operational effect

  • Moves approvals out of email into a queue where the current owner of every invoice is unambiguous.
  • Catches duplicate invoices before an approval is given, not after the payment has left.
  • Applies the company’s own spend authority consistently, including when someone is away, through explicit delegation.
  • Routes the specific mismatch to the person who can resolve it instead of returning everything to finance.
  • Produces a defensible record of who approved what and on what basis — the record an audit actually asks for.
  • Makes accrued liability a live view rather than a month-end reconstruction.

What would change at scale

  • Move matching and duplicate screening into a queue-backed pipeline so a month-end intake spike does not depend on synchronous processing.
  • Add supplier-specific extraction templates layered over the general model, since a high-volume supplier’s layout is stable and a template is cheaper and more accurate than inference.
  • Introduce continuous controls monitoring over the approval log — approvals outside band, overrides trending up per user, unusual supplier creation — reported and not merely logged.
  • Support multi-entity and multi-currency with per-entity policy, because the second legal entity is where a single-tenant approval model breaks.
  • Version the exception taxonomy so reporting across periods stays comparable as new types are added.

09Interface

The screens where the work happens

Dense operational views rather than dashboards. These are used for hours at a time, so the priorities are legibility, keyboard flow and state that is never ambiguous.

Invoice register

Every payable with supplier, amount, due date, match state and current approver — filterable by exception type.

Match inspector

Invoice lines beside purchase order lines and cumulative receipts, with variance and tolerance shown per line.

Approval chain

The resolved chain with the policy rule that required each step, completed approvals, and the current owner.

Payment run

Cleared invoices grouped by due date with discount flags and a total, ready to export.

The screen above is built in HTML and CSS for this case study. It reproduces the layout, states and vocabulary of the real build without exposing client data, which is why it exists rather than a screenshot. It is evidence of design and of the decisions behind it. It is not a photograph of a deployed system, and no part of it is a capture of anyone else’s product.

10Technologies

What it is built with

Chosen for the shape of the problem, not for novelty. Anything unusual is justified in the decisions section above.

Frontend
  • Next.js App Router
  • TypeScript
  • Tailwind CSS
  • React Server Components
AI layer
  • Invoice extraction schemas
  • Duplicate similarity scoring
  • Coding suggestions from history
Application
  • Tolerance-based match engine
  • Policy chain resolver
  • Event-sourced invoice state
  • Exception routing table
Data
  • PostgreSQL
  • Append-only approval log
  • Document object storage

11Technical preview

Structure, models and annotated excerpts

There is no repository link on this site. What is available instead is the module structure, the data model, the interface contracts and annotated excerpts written for this case study — enough for a technical reviewer to judge the engineering.

Technical preview

Implementation detail for this build

Selected implementation details are available for technical review: component structure, data models, architecture notes and sanitized code excerpts. Some client-specific source material stays private because of confidentiality. Enter the project access keyword if you have been given one — the same one works across every case study.

A presentation convenience, not authentication. This is a static site, so the keyword ships to your browser with the page and anyone reading the bundle can find it — worth saying plainly rather than dressing it up. Nothing confidential is stored behind it.

Inside this section

  • Module and folder structure for the build
  • Data models and the interface contracts between layers
  • 2 annotated implementation excerpts
  • Design notes covering the decisions the excerpts imply
  • The same keyword opens every case study on the site

For private client work, contact me for a walkthrough.

Next step

Need something along these lines?

Send the process, the constraints and the deadline. You will get an honest scope, an architecture sketch and a timeline before any commitment.

Email
surajk86808@gmail.com
Based in
Bengaluru, India
Working hours
IST (UTC+5:30)
Availability
Taking new engagements