Skip to content

Controls & audit

Accounting software is not a database with forms on top. It is a system of controls, and the UI is where most of those controls are actually enforced.


1. The control principles

Principle What it means How the UI enforces it
Segregation of duties The person who prepares is not the person who approves Enforced, never a setting. The approve action is absent, not disabled, for the preparer.
Authorization Spend above a threshold needs a specific role Thresholds shown at entry, not after submission
Completeness Everything that happened is recorded Gapless numbering; unmatched queues that cannot be dismissed
Accuracy Recorded amounts are right Three-way match, tie-outs, balance checks
Validity Recorded things actually happened Required source documents
Cutoff Recorded in the right period Period states, cutoff warnings
Immutability History cannot be rewritten Reverse, never edit or delete
Traceability Every number traces to its source Drill-down at every level

Where the principles come from

The list above is the working form of two frameworks. COSO's Internal Control — Integrated Framework (2013) — five components (control environment, risk assessment, control activities, information and communication, monitoring) and seventeen principles — is the framework almost every US registrant uses for its annual ICFR assessment under SOX §404(a). The auditor's side is PCAOB AS 2201. The assertions a control is designed to address — existence or occurrence, completeness, valuation or allocation, rights and obligations, presentation and disclosure (PCAOB AS 1105), with accuracy, cutoff and classification broken out separately in the AICPA's AU-C 315 — are the auditor's vocabulary for the same rows.

Control failures are graded, and the UI should use the grade rather than a generic "issue":

Grade Meaning Consequence
Control deficiency A control does not allow timely prevention or detection of misstatements Tracked and remediated
Significant deficiency Less severe than a material weakness, yet merits the audit committee's attention Communicated to those charged with governance
Material weakness A reasonable possibility that a material misstatement will not be prevented or detected on a timely basis Disclosed; ICFR is not effective

2. The audit trail

Every state change on every financial object writes an immutable record.

type AuditEvent = {
  id: string;
  at: Date;                       // server time, UTC, never client time
  actor: { id: string; name: string; role: string; onBehalfOf?: string };
  action: string;                 // "posted" | "approved" | "reversed" | "reopened" | …
  objectType: string;
  objectId: string;
  before?: unknown;               // full prior state for changed fields
  after?: unknown;
  reason?: string;                // required for privileged actions
  source: "ui" | "api" | "integration" | "system";
  requestId: string;              // correlates a multi-object action
  ipAddress?: string;
};

UI rules:

  • The audit trail is a first-class view on every object, not a hidden admin log. AuditTrail renders as a timeline with actor, action, and diff.
  • Field-level diffs, not "record updated". "Amount changed from $1,240.00 to $1,420.00" is the useful form.
  • Server time, UTC, timezone displayed. A client-supplied timestamp is not evidence.
  • onBehalfOf is rendered explicitly — support and delegated access must be visibly distinguishable from the user acting themselves.
  • The audit trail is append-only and cannot be filtered into invisibility. Filters narrow the view and say so: "Showing 12 of 148 events."
  • Privileged actions require a reason: period reopen, write-off above threshold, approval override, bank detail change, permission grant.

3. Approvals

type Approval = {
  step: number;
  requiredRole: string;
  approver?: { id: string; name: string; at: Date };
  status: "pending" | "approved" | "rejected" | "voided";
  reason?: string;
  onVersion: string;              // the object hash approved
};

onVersion is the critical field. An approval applies to a specific version of the object. Edit it afterwards and every approval voids and must be re-obtained, with the change shown to the re-approver. Silent post-approval editing is the classic fraud path, and the version binding closes it.

UI rules:

  • The chain is visible before submission — who, in what order, what's done, what's blocking.
  • Rejection routes back with a reason, visible to the preparer.
  • Delegation expires. Permanent delegation is a control failure; the UI requires an end date.
  • The approver sees the evidence inline — the source document, the match, the coding — not a link to it. An approver who has to click through to see what they're approving will stop clicking through.
  • ApprovalInbox is mobile-first. Approvals happen on phones, between meetings, and the document must be readable at 390px.

4. Roles

A minimal, real set. Products that ship twenty granular permissions end up with everyone as admin.

Role Can
Viewer Read statements and transactions. No export of PII.
Preparer Create drafts, code transactions, prepare schedules. Cannot post or approve.
Accountant Post entries, reconcile, roll schedules. Cannot approve payments or close periods.
Controller Approve entries, close periods, set materiality. Cannot pay.
Treasurer Approve and release payments. Cannot post entries.
Admin Users, roles, integrations. Cannot post, approve, or pay.
Auditor Read everything including the audit trail. Write nothing, ever.

The two structural separations: Controller and Treasurer are separate because the person who records an obligation should not be the person who settles it. Admin cannot transact, because whoever grants permissions should not be able to use them.


5. Evidence

type Evidence = {
  kind: "invoice" | "receipt" | "contract" | "statement" | "memo" | "screenshot";
  file: Document;
  uploadedBy: string; uploadedAt: Date;
  hash: string;                   // content hash — proves it hasn't changed
  linkedTo: { objectType: string; objectId: string }[];
};

UI rules:

  • Required, not optional, on bills, expenses, adjusting entries, and reconciliations. These are the four places an auditor will ask.
  • The content hash is stored and displayed. A document that changed after being attached is a finding, and it should be detectable.
  • Evidence is viewable inline, never download-only. EvidencePanel renders PDFs and images beside the object.
  • Evidence is never deleted, only unlinked, and the unlink is audited.

6. Data integrity checks

Assertions that run continuously and surface as exceptions, not as silent corrections.

Check Assertion
Entry balance Debits = credits, every entry
Accounting equation Assets = Liabilities + Equity, every period
Subledger tie AR aging = AR control; AP aging = AP control
Schedule tie Every schedule closing = its GL account
Roll-forward continuity Opening = prior closing, every schedule
Cash tie Cash flow ending cash = balance sheet cash
Retained earnings Opening RE + NI − distributions = closing RE
Intercompany IC receivables = IC payables across entities
Numbering Invoice and entry numbers gapless
Period integrity No postings in closed periods
Orphan detection No entries referencing deleted accounts or dimensions

A failing check is the primary state of its surface. Not a badge in a corner — the screen's main content becomes the failure, the difference, and the investigation path. A balance sheet that doesn't balance is not a balance sheet with a small problem.


7. Auditor mode

A read-only view built for the annual audit, because the alternative is exporting everything to spreadsheets and emailing them.

  • Read-only, enforced server-side. Not a UI state.
  • Scoped to a period and an entity set, with the scope displayed and immutable during the session.
  • Sampling support — select N transactions by a stated method (random, monetary-unit, judgmental) with the seed recorded so the selection is reproducible.
  • Evidence bundling — for any selection, export the entries, their source documents, their approvals, and their audit trail as one package with a manifest.
  • Auditor activity is itself audited, and visible to the client. Transparency runs both ways.

8. Privacy and retention

  • No monetary amount in any analytics event, log line, or error report. Events carry the shape of an interaction, never the amount.
  • PII in exports is role-gated, and the export is audited with its scope.
  • Retention is jurisdictional — commonly 7 years, sometimes 10. Records inside retention cannot be deleted by any role, including admin.
  • Deletion requests (GDPR and equivalents) conflict with retention obligations. The UI supports pseudonymization of the person while retaining the transaction, and states which was applied.
  • Copy-to-clipboard copies raw values, and clipboard actions on sensitive fields are audited.

Built against this document

The surfaces that implement what this argues — running, computing, or shaped by it.

Told when this spec changes

The docs are the contract the components are built against. When one changes, the component changes with it — and the copy in your repo does not. One email when that happens.

One address, no list rental, unsubscribe in one click.

Works in your QuickBooks Online or Xero file

Get your books closed every month

Lopifi is building AI bookkeeping that keeps your books closed on US GAAP as transactions happen, with a CPA review every quarter, for $200 a month. We’re not open yet.

$200/month when we open. No card now.