Quick start — financial statement to ACRA-filed XBRL
A narrative walkthrough of the XBRL filing flow from the staff side. By the end you'll have moved one client through one full filing — source document ingested, every line item tag-mapped to an ACRA element and human-confirmed, the 144-rule validation run, BizFinx workbook exported, optional client sign-off captured, ACRA filing reference recorded. Every section links into the matching how-to runbook for click-by-click detail and into the troubleshooting page that covers the most common stumble at that step.
The story below follows one client end to end. We'll use Meridian Engineering Pte Ltd — a hypothetical non-listed Singapore company with a December year-end, filing under FULL_XBRL against ACRA Taxonomy 2026 v1.0, accounting standard SFRS. Anchor the steps on whichever client you're actually running; the surfaces are identical.
IMPORTANT
The export gate has no override path. Two of the five non-negotiable rules from cursorrules — no XBRL output without passing validation and every tag mapping must be human-confirmed — are enforced by assertExportAllowed() and live in packages/xbrl/src/export-gate.ts. If you find yourself wanting to "just get the export out", the right answer is fix the mapping or fix the rule failure. There is no escape hatch.
NOTE
If you're new to the dashboard, start with the dashboard tour first. If you're new to the codebase, walk the manual testing playbook — it exercises every layer (schema, constitution, gate, FSM) in the current Phase 0 / Phase 2-foundation state.
Cast of characters
| Role | Plays |
|---|---|
| Platform Admin | Sets up the client record once — xbrlConfig, entity type, default entry point, taxonomy version, accounting standard, UEN |
| XBRL Preparer | Creates the filing, ingests the source document, walks every line item, confirms or overrides tag mappings |
| XBRL Reviewer | Runs validation; exports BizFinx XLSX + XBRL XML; marks filed against ACRA; appends synonyms to the mapping library when warranted |
| Senior Accountant | Senior judgment on persistent validation failures; cross-product hand-off when the source is an FS engagement |
| Client approver | Optional pre-submission sign-off via portal — confirms UEN and the period being filed |
0. Confirm the client is configured
An XBRL filing client needs:
- Enabled products including XBRL Filing
- An
xbrlConfigwithentityType(e.g.NON_LISTED_COMPANY_FULL),defaultEntryPoint(e.g.FULL_XBRL),taxonomyVersion(e.g.acra_2026_v1),accountingStandard(SFRS/SFRS_SE/IFRS), and the entity'suen - One approver contact for the optional portal sign-off
Open Clients in the staff dashboard sidebar, click into Meridian. The setup health panel goes green when the essentials are in place.
→ Onboard an XBRL client → Configure xbrlConfig for a client
1. Create the filing
Open Meridian; click New XBRL filing. Fill in:
- Period start —
2024-01-01 - Period end —
2024-12-31 - Entry point — defaults from
xbrlConfig.defaultEntryPoint; override per filing (e.g. an FSH variant) - Entity type — defaults from
xbrlConfig.entityType - Accounting standard — defaults from
xbrlConfig.accountingStandard - Taxonomy version — defaults from
xbrlConfig.taxonomyVersion. This is pinned at create time and immutable thereafter. A future ACRA taxonomy release lands as a side-by-side constitution file; in-flight filings continue against their pinned version. - (Optional) FS engagement — when set, the cross-product Phase 3 hand-off is available at step 2.
Submit. Filing created at DRAFT. URL settles to /dashboard/xbrl/<filingId>.
2. Ingest the source document
Two paths. Pick one.
Path A — DOCX or PDF upload (independent path)
Open the Documents tab on the filing. Click Upload source document and drop in Meridian's financial-statements DOCX or PDF.
The handlers run in sequence:
ingest-fs-document— persists the bytes via theFilemodel with key prefixxbrl/<clientId>/<filingId>/source.{docx,pdf}; computes SHA-256; insertsXbrlSourceDocument.extract-line-items— reads the file; parses tables (DOCX viamammoth; PDF viapdf-parse+ table heuristics); normalises amounts (strip commas, parentheses → negative); insertsXbrlLineItemrows with(statement, label, valueCurrent, valuePrior, sourcePage, sourceLine). Filing transitionsDRAFT → DOCUMENTS_INGESTED.suggest-tag-mappings— for each line item: exact-match againstmapping_library.jsonsynonyms (case-insensitive); on miss, fuzzy-match against taxonomy element labels filtered by entry point and statement section; persistsXbrlTagMappingrows withconfirmedById = NULLandalternativespopulated. Filing transitionsDOCUMENTS_INGESTED → MAPPING_IN_PROGRESS.
For Meridian a typical run on a 14-page statement DOCX yields ~85 line items across balance_sheet, income_statement, cash_flow, equity_changes, and notes statements.
Path B — From an FS engagement (cross-product, Phase 3)
If Meridian's FY2024 financial statements were already built in the platform's Financial Statements module, click Pull from FS engagement on the Documents tab. The handler reads the latest FsGeneratedStatement.statementsJson and expands it directly into XbrlLineItem rows — skipping the DOCX parse entirely. Where the SFRS taxonomy key has a known ACRA element correspondence in mapping_library.json with HIGH confidence, the mapping is auto-confirmed; MEDIUM / LOW rows land unconfirmed for manual review.
NOTE
Path B requires the FS engagement to be at NOTES_COMPLETE and the client must have the FS module enabled. The two products share the same clientId.
→ Upload a source document → Pull from an FS engagement → Source PDF yielded zero line items — usually a rasterised PDF → DOCX has tables but they didn't parse — table layout edge case in mammoth
3. Confirm tag mappings
Open the Mappings tab. Master-detail layout: line-items list on the left (filterable by statement and by confirmation status), element-confirmation panel on the right.
For Meridian's 85 line items, the suggestion engine produced:
- 54 with
HIGHconfidence (exact-match against the synonym library) - 22 with
MEDIUM(fuzzy-match, top suggestion confident) - 6 with
LOW(fuzzy-match, multiple plausible candidates inalternatives[]) - 3 with no suggestion (label too generic; manual search required)
Walk every row. Three actions per line item:
- Confirm — accepts the top suggestion. Sets
confirmedByIdandconfirmedAt. - Pick alternative — opens the
alternatives[]list (top-N candidates from the suggestion engine). Choose; confirm. - Search and pick — opens the taxonomy search box, filtered by the filing's entry point and the line item's statement section. Pick; confirm.
For one row — "Software development costs capitalised" — there's no good match. Click Search, type software, find sg-as_IntangibleAssetsRelatedToComputerSoftware, confirm. The label is canonical enough to be worth saving: click Save as synonym. The handler writes {label: "Software development costs capitalised", target_element_id: "sg-as_IntangibleAssetsRelatedToComputerSoftware", confidence: "high"} into mapping_library.json and emits an AuditEvent xbrl.mapping.synonym_added. Future filings for any client will pick this up.
IMPORTANT
Save-as-synonym affects every future filing across every client. ADD_MAPPING_SYNONYM is restricted to XBRL_REVIEWER, SENIOR_ACCOUNTANT, PLATFORM_ADMIN for exactly that reason. Use it for labels that are likely to recur across the firm's portfolio, not for one-off client wording. The Phase 4 backlog includes per-client override files to limit cross-client blast radius — until then, the global library is shared.
When every line item has confirmedById != NULL, the filing is ready for validation.
→ Confirm tag mappings → Override a tag mapping → Save a new synonym to the mapping library — reviewer / senior-only → Suggestion confidence is all LOW — usually a label-normalisation surprise → Element search returns nothing for an obvious concept — check the entry-point filter
4. Run validation
Open the Validation tab; click Run validation.
The handler enqueues run-xbrl-validation:
- Loads every confirmed
XbrlTagMapping - Loads the validation rules for the pinned
taxonomyVersionvialoadValidationRules(version) - Filters by entry point via
rulesForEntryPoint(version, entryPoint)— forFULL_XBRLtypically ~100 of the 144 - Evaluates each rule's
expressionagainst the line-item values - Writes one
XbrlValidationResultper rule withseverity(ERROR/WARNING/INFO),passed(boolean),message,relatedElements
The UI splits into three panes:
| Pane | Effect on export |
|---|---|
| Errors | Blocks export. The export gate raises VALIDATION_ERROR_PRESENT until every ERROR-severity row is passed = true or remediated |
| Warnings | Does NOT block export. Surfaces in the UI and on the filing log; ACRA can still reject the filing for warning-only issues, so don't ignore |
| Info | Informational only. Pass / fail recorded for completeness |
For Meridian a typical first-pass run lands VALIDATION_FAILED with two errors and four warnings:
- ERROR
BR_totalling_001— Total Equity + Total Liabilities does not equal Total Assets. Difference: SGD 12,400. Cause: a Deferred Tax Liability line item was mapped tosg-as_TradeAndOtherPayablesinstead ofsg-as_DeferredTaxLiabilities. - ERROR
BR_mandatory_011—sg-dei_PrincipalActivitieselement is mandatory for non-listed companies but not present. Cause: the source DOCX had Principal Activities in the Directors Report only; the line-item extractor didn't surface a fact. Fix: search the taxonomy, find the element, and the mapping panel offers to create a synthetic line item. - WARNING
BR_arithmetical_005— Profit Before Tax disclosed but no income-tax expense element provided. Cause: legitimate — Meridian had a tax loss carryforward and no current-year tax expense. - WARNING
BR_cross_statement_003— Retained-earnings closing in SOFP does not match SOCE closing. Difference: SGD 200 (rounding). Acceptable on inspection.
Fix the errors: revisit the mappings; revise. Re-run validation. The handler upserts the same (filingId, ruleId) rows; the prior failures are overwritten.
Now the filing transitions VALIDATION_FAILED → MAPPING_IN_PROGRESS → VALIDATION_PASSED.
→ Run validation → Resolve a validation error → Same error keeps coming back after 3 iterations — escalate to Senior Accountant; the rule expression may need scrutiny → Warning I disagree with — document the rationale on the filing log; warnings do not block
5. Export — BizFinx XLSX and XBRL XML
XBRL_REVIEWER, PLATFORM_ADMIN (and Senior Accountant for everything short of MARK_XBRL_FILED) can export.
Open the Exports tab. Two buttons:
- Generate BizFinx — produces a multi-sheet Excel workbook (Income Statement / Balance Sheet / Cash Flow / Equity Changes / Filing Information) shaped for upload to ACRA's BizFinx Preparation Tool.
- Generate XBRL XML — produces an XBRL instance document with contexts (
instantfor balance-sheet,<startDate>/<endDate>for flow), units (iso4217:SGD), and one fact per confirmed mapping.
Click one. The handler enqueues generate-bizfinx-xlsx or generate-xbrl-xml. Before any byte is written, assertExportAllowed() runs:
| Reason raised | Condition | What to fix |
|---|---|---|
NO_LINE_ITEMS | Filing has no XbrlLineItem rows | Re-upload the source document |
NO_VALIDATION_RUN | No XbrlValidationResult rows exist | Run validation |
VALIDATION_ERROR_PRESENT | At least one row with severity = ERROR AND passed = false | Fix the underlying mapping / data; re-run validation |
UNCONFIRMED_TAG_MAPPING | At least one XbrlTagMapping.confirmedById IS NULL | Open the mappings tab; filter for unconfirmed; confirm each one |
The HTTP layer translates XbrlExportBlockedError to HTTP 422 with the typed reason. The UI shows the offending line items / rule ids inline.
On pass:
- The artifact is built
- Persisted via
Filemodel with key prefixxbrl/<clientId>/<filingId>/<artifact>-v<n>.{xlsx,xml} - An
XbrlOutputArtifactrow is inserted withkind,versionNo, signed URL - Filing transitions
VALIDATION_PASSED → EXPORTED(first time only; subsequent exports add new artifact versions but keep the status)
Both export paths run the gate. Defence in depth: the service-layer and the worker both check, so a future code change can't bypass.
→ Generate BizFinx XLSX → Generate XBRL XML → Export refused with UNCONFIRMED_TAG_MAPPING — filter mappings for unconfirmed; the UI will tell you exactly which rows → Export refused with VALIDATION_ERROR_PRESENT — open the Validation tab; address each error
6. (Optional) Client portal sign-off
If Meridian wants to sign off before submission to ACRA, click Send to client for sign-off on the filing. The handler creates a PortalInvitation with resourceType = XBRL_FILING, 30-day expiry; the approver gets an email:
- Subject: XBRL filing ready for sign-off: Meridian Engineering Pte Ltd (2024-01-01 → 2024-12-31)
- Body: Filing period, UEN, entry point, validation-passed badge, and a single Open the sign-off portal button.
The portal shows:
- Filing summary (UEN, period, entry point, taxonomy version)
- Statement-level previews (Balance Sheet totals, P&L totals)
- A UEN confirmation input — the approver must echo Meridian's UEN to confirm they're signing the right entity
- Two CTAs: Approve and Request revision
| If they pick… | Effect | Your next step |
|---|---|---|
| Approve | Decision captured with timestamp; staff notified | Submit to ACRA; click Mark filed |
| Request revision | Comment captured; staff notified; filing back-routes to MAPPING_IN_PROGRESS | Address the comment; re-run validation; re-export; re-send portal link |
→ Dispatch the XBRL portal link → Handle a client revision request
7. Submit to ACRA and Mark Filed
This step is out-of-band — the platform does not submit to ACRA automatically.
- Open the Exports tab; download the BizFinx XLSX (latest version).
- Open ACRA's BizFinx Preparation Tool. Upload the workbook. Resolve any tool-side prompts.
- Submit through BizFinx. Capture the ACRA filing reference from the acknowledgement.
- Back in the platform, open the filing detail; click Mark filed. Provide the ACRA filing reference. Filing transitions
EXPORTED → FILED.filedAtandfiledByIdare recorded.
IMPORTANT
Only XBRL_REVIEWER and PLATFORM_ADMIN can Mark filed. The boundary deliberately matches human accountability with the regulator — whoever marks the filing is the one taking responsibility with ACRA. Even Senior Accountants who can do everything else short of filing cannot do this.
→ Mark a filing as filed with ACRA → ACRA rejected the filing after submission — back-route to MAPPING_IN_PROGRESS; fix; re-export; re-submit
8. Archive
When the audit cycle closes, click Archive. The filing transitions FILED → ARCHIVED and becomes read-only. The retention policy (default 7 years from period end; configurable per jurisdiction) applies.
What you've just done
You've shipped Meridian through one complete XBRL filing: source document ingested, every line item tag-mapped to an ACRA element and human-confirmed, the 144-rule validation passed, BizFinx workbook and XBRL XML produced through the gate, optional client sign-off captured, ACRA filing reference recorded, filing archived. The five non-negotiable rules — Rule 1 (no taxonomy literals), Rule 2 (no output without passing validation), Rule 3 (every mapping human-confirmed), Rule 4 (taxonomy version is first-class), Rule 5 (regression suite green) — were all enforced in code, in tests, and in your work.
Where to go next
- File the next one — open XBRL Filings in the sidebar, pick the next client whose period-end is approaching, and follow this same arc.
- Build the upstream financial statements — Quick start — financial statements.
- Understand the status machine — XBRL filing states.
- Read the SOP this flow implements — XBRL Filing SOP.
- Walk the architecture and the five rules — XBRL & FS integration plan.
Cheat sheet — what to do when X
| If… | Open… | Do… |
|---|---|---|
| Source PDF yielded zero line items | Filing → Documents | Ask the client for a DOCX or a text-extractable PDF; or use the cross-product from-fs-engagement path |
| Suggestion confidence is all LOW | Filing → Mappings | Check the entry-point filter on element search; verify the source statement headings — see All low confidence |
Export refused: UNCONFIRMED_TAG_MAPPING | Filing → Mappings | Filter for unconfirmed rows; confirm each — see Export blocked: unconfirmed |
Export refused: VALIDATION_ERROR_PRESENT | Filing → Validation | Open each error row; trace relatedElements; fix at source — see Export blocked: validation |
| Save-as-synonym is disabled | Filing → Mappings | You don't hold ADD_MAPPING_SYNONYM — escalate to Reviewer or Senior Accountant |
| Mark Filed button is disabled | Filing detail | Only XBRL_REVIEWER or PLATFORM_ADMIN may mark filed |
| Validation rule keeps failing across iterations | Filing → Validation | Escalate to Senior Accountant; the rule expression may need scrutiny — see Validation loops |
| Need to file the same period for a different entity type | Don't reuse the filing | Create a new filing with the correct entity type; the taxonomy version is per filing |
ACRA rejected after Mark filed | Filing detail | Back-route to MAPPING_IN_PROGRESS (EXPORTED → MAPPING_IN_PROGRESS is allowed); fix; re-export — see ACRA rejection |
| You took a wrong action | Activity log | Every state change is logged with the actor — see Recover from a mistake |