Build Fathom HQ
KINDAreplaces $41.54/mosaves $498.48/yrback to the verdict
A reporting layer over accounting data whose real problem is that two tools computing the same ratio from the same ledger will disagree unless every definition is written down, and a figure nobody can trace back to transactions is a figure nobody should act on.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the import, computation and reporting.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree locally
Why Balances, metric values and their drill paths with exact integer arithmetic.
Get it Docker or your package manager. open ↗
Verify
psql connects and integer sums are exact - accountfree with an existing subscription
Why Sign conventions, chart of accounts structure and period adjustment behaviour differ by system, and none of it can be guessed from a specification.
Get it Read-only API access to a real ledger with at least two years of history, ideally including a period that was reopened and adjusted.
Verify
An imported trial balance matches the system's own exactly - decidefree, and it takes a conversation with your accountant
Why Which accounts belong in cost of sales is a judgement, and two people computing the same ratio will disagree unless it is written down first.
Get it Agree each metric's formula and account mapping with whoever owns the numbers, and write the definition note that will appear beside the figure.
Verify
Every planned metric has an agreed formula, mapping and note - have readyfree
Why Comparison basis checking and adjustment handling only matter across real history, and a reopened period is the case that breaks naive imports.
Get it Use the real history from your accounting system rather than generated data.
Verify
Two years exist and at least one period was adjusted after close
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- imports(id, source_system, period_from, period_to, imported_at, ledger_version, complete) - accounts(id, code, name, kind, parent_id, normal_balance) · kind is asset, liability, equity, income or expense - balances(account_id, period, amount_minor, currency, adjusted) · adjusted marks a period reopened and changed - metrics(id, name, formula_json, account_mapping_json, definition_note, created_at) - metric_values(metric_id, period, value, numerator_minor, denominator_minor, computed_at) · both sides kept - forecasts(id, metric_id, method, horizon, assumptions_json, produced_at) - variances(metric_id, period, actual, comparison_kind, comparison_value, explanation) - A metric value stores its numerator and denominator, so a disputed ratio can be argued about in terms of the amounts rather than the result.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATABASE_URLsecret | required | postgres://localhost:5432/reporting | Imported financial data for a real business. |
LEDGER_API_KEYsecret | required | a read-only key | Read only. This build never writes to the accounting system. |
METRICS_PATH | required | ./config/metrics.json | Formulas, mappings and definition notes. Agreed before building. |
BASE_CURRENCY | required | GBP | For reporting. Source amounts keep their own currency. |
SHOW_DEFINITION_WITH_FIGURE | required | always | Present as a constant. A ratio without its definition cannot be reconciled. |
MIN_PERIODS_FOR_FORECAST | required | 12 | Below this a forecast is refused rather than produced. |
The build, in order
Importing a ledger without becoming one
Balances match the accounting system exactly.
Normalising breaks reconciliation, which is the whole basis of trust.
Files
src/import.tsExactly. Any difference is a defect, not a rounding matter.
Files
tests/reconcile.test.tsAdjustments after close are normal and change history.
Files
src/periods.ts
done when · tick each as it passeswatch out- Normalising sign conventions on import so everything is positive makes reports read nicely and makes reconciliation against the accounting system impossible, because the numbers no longer match what the accountant sees. Preserve the source conventions and handle presentation at the display layer.
Metrics defined rather than assumed
Every ratio has a written formula and an account mapping.
Formula, mapping and a note. Not code and not convention.
Files
src/metrics.tsThe disagreement should be discussable rather than mysterious.
Files
src/render.tsA changed mapping changes history and should say so.
Files
src/mappings.ts
done when · tick each as it passeswatch out- Two tools computing gross margin from the same ledger will disagree, because the question of which accounts belong in cost of sales is a judgement rather than a fact, and neither tool says which it used. Store the mapping, show the definition beside the number, and make the disagreement discussable instead of mysterious.
Every figure opens to its transactions
A number in a report can be drilled to the entries that make it.
Metric to balances to transactions, in one path.
Files
src/drill.tsFor every metric and period, in the suite.
Files
tests/drill-sums.test.tsA stated result, not an error and not a misleading number.
Files
src/compute.ts
done when · tick each as it passeswatch out- A report computed from a cached aggregate drifts from the ledger the moment an adjustment is posted, and the discrepancy surfaces in a meeting rather than in a test. Assert in the suite that every displayed figure equals the sum of its drilled detail, for every metric and period.
Comparisons that compare like with like
A variance against budget or last year is computed on the same basis.
Definition, mapping and period length. All three.
Files
src/compare.tssrc/compare.test.tsA mapping artefact looks exactly like a business change.
Files
src/compare.tsBudgets get revised. Which one you beat matters.
Files
src/budgets.ts
done when · tick each as it passeswatch out- Comparing this year against last year across a change in account mapping produces a variance that is entirely an artefact of the mapping, and it looks exactly like a real change in the business. Detect that the definition differs between the periods and flag the comparison rather than presenting the number.
Forecasts that show their assumptionsproduct builder
A projection states what it assumed and how far it can be trusted.
So a wrong forecast is diagnosable rather than merely wrong.
Files
src/forecast.tsA single line becomes a plan the moment it is on a slide.
Files
src/render.tsThe method's track record is the only honest confidence signal.
Files
src/accuracy.ts
done when · tick each as it passeswatch out- A forecast drawn as a single confident line gets treated as a plan, and when it is wrong nobody can say whether the method was bad or the assumptions were. Show a range, record the assumptions with the forecast, and compare past forecasts against outcomes so the method's track record is visible rather than assumed.
That is the whole plan for Fathom HQ. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Being the accounting system.
- Tax computation and statutory filing.
- Audit or assurance.
- Multi-entity consolidation with eliminations.
- accounting integrations
- consolidation
- templates
- forecasting
- report builder
- advisor workflows
- reliability
- Cash flow forecasting from receivables and payables ageing, which is the question people actually ask
- Scenario comparison, once assumptions are already structured
- Board pack generation from the reports, which is the recurring manual work
Need the files? The project pack on the verdict page hands your agent the whole brief · more finance & accounting.