DEV Community

Tali Adler
Tali Adler

Posted on

The Missing Layer in Accounting Automation: A Reviewable Evidence Trail

The Missing Layer in Accounting Automation: A Reviewable Evidence Trail

Most accounting automation demos stop at the impressive part: a system reads a document, extracts a value, and proposes an entry. That is useful, but it is not the hard part of operating finance software. The hard part is making the proposal reviewable after the context has disappeared.

A month later, someone needs to answer simple questions: Which source created this number? What did the system infer? Who approved it? What changed between the first suggestion and the final posting? If the answer is a screenshot, a spreadsheet cell, or a vague log message, the workflow is fast only until the first audit question.

This article describes a practical design for an evidence-linked accounting workflow, and why the review layer deserves to be treated as a product surface rather than an afterthought.

Start with an evidence object, not a journal entry

A common implementation starts with a target object: an invoice, a bank transaction, or a journal entry. A more durable implementation starts with an evidence object that records what the system actually saw.

At minimum, an evidence object should include:

  • source_type: invoice, bank feed, contract, email, or manual upload
  • source_id: the provider ID or immutable file reference
  • observed_at: when the source was retrieved
  • content_hash: a fingerprint that detects later replacement
  • extracted_fields: the values read from the source
  • confidence: a signal for routing, never a substitute for review
  • raw_location: where an authorized reviewer can inspect the original

The content hash matters more than it first appears. If a vendor replaces a PDF or a connector replays a transaction with changed metadata, the workflow should create a new observation or raise an exception. Silently overwriting the old evidence breaks the chain of reasoning.

Separate observation, proposal, and decision

These are three different events:

  1. Observation: the system found a $12,400 invoice dated March 31.
  2. Proposal: the system suggests office expense, cost center 410, and an accrual period of one month.
  3. Decision: a named reviewer accepts, edits, or rejects that proposal.

Combining them into one mutable record makes the workflow difficult to explain. Keep them as linked records, or at least as append-only revisions with explicit actors and timestamps. A reviewer should be able to see the original extraction beside the final decision, not just the latest state.

This also improves debugging. If a classification rule is wrong, engineers can distinguish a parsing problem from a policy problem. Those require different fixes.

Build the review queue around exceptions

A review queue should not be a dump of everything the system cannot decide. It should answer why a person is being asked to intervene. Useful exception reasons include:

  • the amount exceeds a policy threshold
  • the supplier is new or unmatched
  • the accounting period is closed
  • the source conflicts with an existing transaction
  • required evidence is missing
  • the model suggested a category outside its allowed policy

Each item needs a next action, not just a red badge. For example, “request a purchase order,” “confirm tax treatment,” and “choose an open period” are operationally different tasks. The queue becomes much more valuable when it gives the reviewer the relevant evidence and the smallest decision required.

This is one area where Portali’s evidence-linked workflow approach is useful: the accounting proposal and the material supporting it can stay together while a person handles the exception. The value is not that a human disappears. The value is that the human spends time on judgment instead of hunting through tabs.

Make approval idempotent

Review systems often fail at the handoff. A user clicks Approve, the network pauses, and the browser retries. If approval directly creates a journal entry without an idempotency key, the same decision can post twice.

Use a key derived from the proposal revision and decision action, such as:

approve:{proposal_id}:{proposal_revision}
Enter fullscreen mode Exit fullscreen mode

The posting service should record that key before performing the side effect, or atomically with it when the storage layer supports transactions. A repeated request should return the existing result instead of creating another entry.

Approval should also expire when its inputs change. If the source evidence, amount, period, or account mapping changes after review, create a new proposal revision and require a new decision. Never carry an approval forward just because the record still has an “approved” status.

Measure trust, not just throughput

A dashboard that reports “10,000 transactions automated” can hide a painful process. Add metrics that describe review quality:

  • percentage approved without edits
  • percentage reopened after approval
  • median age of unresolved exceptions
  • duplicate side effects prevented by idempotency
  • evidence retrieval failures
  • time from observation to final decision

The most useful metric may be the percentage of proposals that reviewers can resolve without leaving the workflow. That measures whether the system provides enough context to support an accounting decision.

The practical takeaway

Automation earns trust when it leaves a trail: source evidence, extracted facts, proposed treatment, human decision, and safe execution. The reviewable trail is not paperwork added after the “real” automation. It is the interface that makes automation usable in a controlled finance environment.

Whether you are building an internal close tool or integrating an accounting product, model the evidence and the decision as first-class objects. Start with one exception type, make the provenance visible, add idempotent approval, and measure where people still need to leave the workflow. That is how a fast demo becomes a system a finance team can actually operate.

Top comments (0)