Statement mapping
The mapping covers profit and loss statements. Reading one requires a plan that
includes statement mapping; other plans get 403 with
statement_mapping_not_in_plan.
What a mapping is
A mapping is one profit and loss statement placed on the workspace’s chart of accounts. Every line the statement prints is filed on an account, every section and account carries its figure per period, the headline measures (gross profit, operating income, income before income taxes, net income) are stated, and every figure says where in the document it comes from. The body is a contract you can check: the tree adds up to the cent by the rules under The arithmetic, and what it adds up to is the bottom line the statement prints.
For every usable profit and loss statement a mapping is prepared after
extraction. It is read by document id, or listed per borrower, in the Statement
Mappings group of the API reference. Both reads need the
extractions:read scope.
The response carries the mapping’s identity and versions on the envelope and
the statement itself in payload:
Every figure is a decimal string with exactly two decimal places, and every
values array, every measure and every entry of foot run in the order of
payload.periods, so position i of each belongs to period i. See the
value conventions.
When a mapping is ready
A mapping is prepared after the statement’s extraction, so it follows
extraction.ready. Three signals say when it is ready:
- The event.
extraction.mappedfires once for every mapping: the first, and each one made again.reasonsays why:firstfor the document’s first mapping,extractionwhen the extraction was revised,chartwhen the workspace’s chart of accounts, caption rules or export map changed,logicwhen the mapping logic changed, andcorrectionwhen none of those moved and the statement was corrected in the workspace. The event is a pointer: the document, its borrower and loan, themapping_id,statusandmapping_state, the versions and the time, never a figure. Read the mapping it names and let it replace the copy you hold. See Webhooks for the envelope, the fields and the delivery rules. - The retrieve. While the statement’s first mapping is being prepared, the
retrieve answers
404with aRetry-Afterheader saying how many seconds to wait before reading again. A statement nobody has asked for is prepared by the read that finds it, so the first read of a document can answer this way. A404withoutRetry-Aftermeans no mapping is being prepared: the document is not a profit and loss statement, or is not one you can see. - The list.
pending_document_idsnames the documents of the borrower, or of the loan whenloanIdis given, whose first mapping is still being prepared. Read the list until it is empty and every statement the borrower holds is indata.
A mapping that reads stale: true is still the latest one stored, served
while a fresh one is prepared: the chart of accounts, the caption rules, the
export map or the mapping logic moved after it was made. Read again to receive
the new one, or wait for the extraction.mapped that announces it.
Mapped, unfooted, unmappable
The arithmetic
The tree is a contract you can recompute from the body, to the cent.
- Every figure on a section or an account is positive in the direction of the
node’s
effect,addsorsubtracts: a positive figure on a cost is a cost, a negative one is a credit against it, and a gain or loss on dispositions is positive for a gain. - A node’s figure is the sum of the nodes under it, measures aside, each added
when its
effectis the node’s own and subtracted otherwise, plus thefiled_valuesof the lines placed on the node itself, plus itsunitemized. A null counts as no figure. unitemizedis the part of a node’s figure that no printed line accounts for: the statement prints a total and no detail for part of it, or the printed figures were rounded. It is0.00where the lines account for all of it.- Gross profit is revenue less cost of goods sold. Operating income is gross profit less operating expenses. Income before income taxes is operating income plus other income, less other expenses, less interest, plus gains on dispositions. The net income row is the bottom line the statement prints.
- The sections at the top of the tree, measures aside, each counted by its
effect, sum tofoot.line_sum, andfoot.residualisprinted_bottom_linelessline_sum. On a mapped statement the residual is withinfoot.tolerance, what the footing allows for the rounding of printed figures: a dollar, plus half a dollar for each printed figure the walk counts below the operating income line. A residual of3.00beside a tolerance of8.50is a statement printed in whole dollars, not a defect. - A line’s
valuesare the figures as the statement prints them, in the statement’s own sign presentation. Itsfiled_valuesare what the tree counts, positive in the direction of the node’seffect.
A worked example
A statement for the year ended December 31, 2025 prints ten lines, in whole dollars:
Its tree, showing the three sections that carry a figure (the other five are listed with null figures), the two measure rows, and the accounts the lines landed on:
Read it with the rules above:
pl.opex.insuranceholds two printed lines. Insurance is filed at14900.00. Insurance refund is printed as(1,200.00), a credit against the cost, so itsvaluesentry is-1200.00and so is itsfiled_valuesentry: a negative figure on asubtractsnode. The account’s figure is14900.00 + (-1200.00) = 13700.00, and itsunitemizedis0.00.pl.opexholds no line of its own. Its accounts sum to196300.00 + 72000.00 + 13700.00 = 282000.00, but the statement prints Total expenses of284000.00and gives no line for the other2000.00, so the section’sunitemizedis2000.00and its figure is284000.00, the total as printed.- Gross profit is revenue less cost of goods sold. This statement prints no
cost of goods sold, so
m:gross_profitis418600.00. - Operating income is
418600.00 - 284000.00 = 134600.00, the Net operating income the statement prints. Income before income taxes is134600.00 - 9800.00 = 124800.00: no other income, no other expenses, no gains on dispositions. m:net_incomeis124801.00, the bottom line the statement prints.
The two lines placed on the insurance account, as lines carries them:
The foot for the period:
line_sum is the three sections by their effect:
418600.00 - 284000.00 - 9800.00 = 124800.00. The statement prints
124801.00, so the residual is 1.00. The tolerance is 1.50: a dollar,
plus half a dollar for the one printed figure the walk counts below the
operating income line, the interest expense. The residual is within it, so the
statement is mapped: it was printed in whole dollars, and a dollar of
rounding sits between its lines and its bottom line. measures state the
tree’s own figures: revenue 418600.00, gross_profit 418600.00,
operating_expenses 284000.00, operating_income 134600.00,
pretax_income 124800.00, net_income 124801.00.
Sources
Every figure states where it comes from. A source on a tree node, on a printed line or on the foot carries:
kind:printedwhen the document prints the figure,derivedwhen the figure is computed from figures the document prints, such as a section total the statement does not print or a measure.document_id: the document the figure comes from.address: a printed figure’s place in the document’s report data, for examplelines.4.0for the figure oflines[4]in the first period column of the profit loss statement read, so a mapped figure joins back to the printed line it came from. Null on a derived figure.pages: the pages the figure is printed on, numbered from 1. A derived figure lists the pages of the figures it is computed from.
A node’s sources and a line’s sources run in the order of periods, one
entry per figure, null where the figure is null.
Marks and unmapped lines
Three lists on a tree node name its printed lines:
member_keys: every printed line the node holds, those on the nodes under it included;direct_member_keys: the lines placed on the node itself. Each is thekeyof a member oflines.flagged_keys: lines marked for a reader’s attention: an expense line whose caption names no expense (uncategorized, suspense), names something that is not an expense (an owner’s draw, a loan payment, an asset purchase), or is a bare catch-all such as other or miscellaneous. A marked line stays where it is placed; the mark says a person should look.reclassified_keys: always empty. The key stays so the shape of a node is unchanged.
unmapped_lines lists the printed lines that carry a figure and that no
account received, each with the section it prints in (revenue, cogs,
other_income, opex, other_expenses, interest, dispositions or
taxes, or null when the line prints outside every section). On a mapped
statement such a line is counted on its section’s own row, so the tree still
foots; suggested_account_id is reserved and null. On an unfooted statement no
line is placed, and unmapped_lines names every line that would have been.
unmapped_export_account_ids lists the account ids the workspace’s export map
gives a code that this mapping’s tree does not list, so a map entry that names
an account outside the profit and loss chart is visible from the mapping
itself.
The chart of accounts
The tree is the workspace’s chart of accounts with this statement’s figures.
GET /api/chart-of-accounts returns the chart document the workspace saved,
its version, the rules_version of its caption rules and its
active_export_system. Until the workspace saves a chart, chart is null,
version is 0 and the platform’s accounts below apply. A workspace’s own
arrangement keeps every platform account it leaves out in force, and the tree
of every mapping lists the accounts in force with their ids, so the tree is
the authority on which accounts exist for that statement. The read needs the
chart_of_accounts:read, chart_of_accounts:write or extractions:read
scope.
A section that has no accounts of its own holds its lines on the section’s
row, whose key is pl. followed by the section (pl.interest) and whose
account_id is null.
The platform’s accounts
The account ids an export map keys on, section by section, in tree order. Sub-accounts are indented under their parent.
Net Revenue
The revenue section, key pl.revenue.
COGS
The cogs section, key pl.cogs.
Other income
The other_income section, key pl.other_income.
No accounts under this section.
Operating expenses
The opex section, key pl.opex.
Other expenses
The other_expenses section, key pl.other_expenses.
No accounts under this section.
Interest expense
The interest section, key pl.interest.
No accounts under this section.
Gain (loss) on dispositions
The dispositions section, key pl.dispositions.
No accounts under this section.
Income taxes
The taxes section, key pl.taxes.
No accounts under this section.
The export map
An export map gives each chart account the code, and the name, it has in your own system, so a mapped statement arrives with your codes on it. A workspace holds one map per export system and one active system.
The body is the whole map. system is the export system’s name: a lowercase
letter followed by up to 31 lowercase letters, digits, underscores or hyphens.
entries holds at most 1,000 entries, each naming an account once: an
account_id from the tree (pl.opex.rent, or t: followed by up to 48
lowercase letters, digits and hyphens for an account the workspace added), an
external_code of 1 to 64 letters, digits, dots, underscores, colons or
hyphens, and an external_label of 200 characters or fewer, or null. An
account left out of the body leaves the map. The response is the map as saved;
a concurrent save of the same map answers 409.
After a save, every mapping read answers stale: true until that mapping has
been prepared again with the new codes, and extraction.mapped announces it
with reason: chart. A mapping then carries the system in
payload.export_system, each account’s code and name in external_code and
external_label on its tree node (null where the map gives none), and, in
unmapped_export_account_ids, the account ids the map names that the tree
does not list.
Scopes and the plan
The extraction.mapped event is sent to a workspace whose plan includes
statement mapping. A document you cannot see answers 404 before any plan
signal. Every reference page states its scope and its rate limit lane; see
Authentication for scopes and Errors for
the envelope.