XBRL — namespacing taken to the limit¶
The tour ends with the most namespace-saturated vocabulary in common use. XBRL (eXtensible Business Reporting Language) is how companies file financial statements to regulators — the SEC in the US, ESMA in Europe. It is the natural escalation from e-invoicing: same idea (a public model + your data), turned up to eleven. The defining move is that you do not just use a namespace — you define your own namespace of business concepts (a taxonomy) and then report facts against it.
Two documents, many namespaces¶
XBRL splits into two artifacts that the SOAP / WSDL split foreshadowed:
| Artifact | What it is | Built on |
|---|---|---|
| Taxonomy | An XSD defining concepts (Revenue, Assets…) plus linkbases for labels, calculations, hierarchy | XSD + XLink |
| Instance | A document reporting facts — values for those concepts, in time periods and currencies | the xbrli: namespace |
The instance¶
schemaRefpoints at the taxonomy — the schema this instance reports against. The link usesxlink:(XLink again, the same spec SVG borrowed), so even the act of referencing the schema is namespaced.- A
contextanswers who and when: which entity (by SEC CIK number) and over which period. Every reported fact references a context. This is XBRL's way of avoiding repeating "for Acme Corp, fiscal year 2025" on every single number. - A
unitanswers in what: here US dollars, named by the valueiso4217:USD— a prefixed value, not a prefixed element. Theiso4217:namespace exists so currency codes are globally unambiguous. - The fact itself.
us-gaap:Revenuesis a concept from the US-GAAP taxonomy;contextRef/unitRefwire it to the context and unit above;decimals="-6"says it is accurate to the nearest million. One number, fully self-describing.
Five namespaces in the root, and that is a minimal instance — a real SEC filing
declares dozens. Notice the proportions: the bulk is context/unit scaffolding,
with the actual facts (the us-gaap: lines) a small fraction of the document.
A fuller filing, flattened¶
The minimal instance fits on a screen. A real one does not — and that is exactly
where a flattening view earns its keep. Below is a still-abridged but realistic
10-K excerpt: nine namespaces, four contexts (two reporting periods, one
balance-sheet instant, one dimensional geographic segment), three units
(dollars, shares, and a compound dollars-per-share), cover-page dei: facts, an
income statement with a prior-year comparative, a balance sheet, a company-minted
concept, and a footnote wired to a fact by an XLink arc. As raw markup it is a
wall of xbrli: ceremony; run through unxml the shape surfaces:
xbrli:xbrl(
xmlns:acme="http://acme.example/2025",
xmlns:dei="http://xbrl.sec.gov/dei/2024",
xmlns:iso4217="http://www.xbrl.org/2003/iso4217",
xmlns:link="http://www.xbrl.org/2003/linkbase",
xmlns:us-gaap="http://fasb.org/us-gaap/2024",
xmlns:xbrldi="http://xbrl.org/2006/xbrldi",
xmlns:xbrli="http://www.xbrl.org/2003/instance",
xmlns:xlink="http://www.w3.org/1999/xlink")
link:schemaRef(xlink:href="acme-2025.xsd", xlink:type="simple")
xbrli:context(id="FY2025") # (1)!
xbrli:entity
xbrli:identifier(scheme="http://www.sec.gov/CIK") = 0000320193
xbrli:period
xbrli:startDate = 2025-01-01
xbrli:endDate = 2025-12-31
xbrli:context(id="FY2024") # (2)!
xbrli:entity
xbrli:identifier(scheme="http://www.sec.gov/CIK") = 0000320193
xbrli:period
xbrli:startDate = 2024-01-01
xbrli:endDate = 2024-12-31
xbrli:context(id="AsOf2025-12-31") # (3)!
xbrli:entity
xbrli:identifier(scheme="http://www.sec.gov/CIK") = 0000320193
xbrli:period
xbrli:instant = 2025-12-31
xbrli:context(id="FY2025-Americas")
xbrli:entity
xbrli:identifier(scheme="http://www.sec.gov/CIK") = 0000320193
xbrli:segment
xbrldi:explicitMember(dimension="us-gaap:StatementGeographicalAxis") = acme:AmericasMember # (4)!
xbrli:period
xbrli:startDate = 2025-01-01
xbrli:endDate = 2025-12-31
xbrli:unit(id="USD")
xbrli:measure = iso4217:USD
xbrli:unit(id="shares")
xbrli:measure = xbrli:shares
xbrli:unit(id="USDPerShare") # (5)!
xbrli:divide
xbrli:unitNumerator
xbrli:measure = iso4217:USD
xbrli:unitDenominator
xbrli:measure = xbrli:shares
dei:EntityRegistrantName(contextRef="FY2025") = Acme Corporation # (6)!
dei:EntityCentralIndexKey(contextRef="FY2025") = 0000320193
dei:DocumentType(contextRef="FY2025") = 10-K
dei:DocumentFiscalYearFocus(contextRef="FY2025") = 2025
dei:EntityCommonStockSharesOutstanding(contextRef="AsOf2025-12-31", decimals="0", unitRef="shares") = 15204137000
us-gaap:Revenues(contextRef="FY2025", decimals="-6", unitRef="USD") = 391035000000 # (7)!
us-gaap:Revenues(contextRef="FY2024", decimals="-6", unitRef="USD") = 383285000000
us-gaap:Revenues(contextRef="FY2025-Americas", decimals="-6", unitRef="USD") = 167045000000 # (8)!
us-gaap:CostOfRevenue(contextRef="FY2025", decimals="-6", unitRef="USD") = 210352000000
us-gaap:GrossProfit(contextRef="FY2025", decimals="-6", unitRef="USD") = 180683000000
us-gaap:OperatingExpenses(contextRef="FY2025", decimals="-6", unitRef="USD") = 57467000000
us-gaap:OperatingIncomeLoss(contextRef="FY2025", decimals="-6", unitRef="USD") = 123216000000
us-gaap:NetIncomeLoss(contextRef="FY2025", decimals="-6", unitRef="USD") = 99803000000
us-gaap:EarningsPerShareBasic(contextRef="FY2025", decimals="2", unitRef="USDPerShare") = 6.57
us-gaap:Assets(contextRef="AsOf2025-12-31", decimals="-6", unitRef="USD") = 364980000000
us-gaap:Liabilities(contextRef="AsOf2025-12-31", decimals="-6", unitRef="USD") = 308030000000
us-gaap:StockholdersEquity(contextRef="AsOf2025-12-31", decimals="-6", unitRef="USD") = 56950000000
acme:SubscribersEndOfPeriod(contextRef="AsOf2025-12-31", decimals="0") = 1025000000 # (9)!
link:footnoteLink(xlink:role="http://www.xbrl.org/2003/role/link", xlink:type="extended") # (10)!
link:loc(xlink:href="#fact-rev", xlink:label="rev", xlink:type="locator")
link:footnoteArc(xlink:arcrole="…/fact-footnote", xlink:from="rev", xlink:to="fn1", xlink:type="arc")
link:footnote(xlink:label="fn1", xlink:role="…/footnote", xml:lang="en") = Includes a 53rd week.
- A duration context: a fact tagged
FY2025is a flow measured over the year (revenue, net income). - The prior year, declared once. The comparative column on every financial statement is just facts pointing at a second period context — no duplicated structure.
- An instant context:
periodis a singleinstant, not a start/end. Stocks (assets, equity, shares outstanding) are measured at a point; the period type is part of the concept's definition in the taxonomy, and the instance must use the matching context kind. - A dimensional context. The
segmentcarries anxbrldi:explicitMemberpinning an axis (StatementGeographicalAxis) to a member (acme:AmericasMember). This is how one concept —Revenues— is reported many times, sliced by geography, product, or scenario, without inventing a new element per slice. - A compound unit built with
divide: dollars over shares. Earnings-per-share is dimensionally a ratio, and the unit says so structurally rather than as free text. - The
dei:(Document and Entity Information) block — the cover-page facts every SEC filing carries (registrant name, CIK, form type). A whole separate namespace just for the filing's metadata. - The first real fact, and the same concept as the minimal example.
decimals="-6"= accurate to the nearest million;contextRef/unitRefresolve upward to the scaffolding. - The same
us-gaap:Revenuesconcept, third occurrence — this one bound to the dimensionalFY2025-Americascontext. The fact element is identical; only thecontextRefchanges the meaning. acme:SubscribersEndOfPeriod— a concept the standard taxonomy has no word for, in the company's own namespace, sitting in the same flat list of facts as theus-gaap:ones. The next section is how that element comes to exist.- A footnote linkbase embedded in the instance: an XLink
extendedlink with aloc(points at a fact), anarc(the relationship), and aresource(the note text). The samexlink:machinery that links taxonomies, here annotating a single number. (The long…/fact-footnoteand…/footnoterole URIs are abbreviated above for the page — the real ones are fullxbrl.orgURLs.)
Read flat, the document is plainly three lists: contexts, units, then facts that
reference them by id. The verbosity that makes raw XBRL daunting is almost entirely
the xbrli: scaffolding, and the flattening makes the signal-to-ceremony ratio
visible at a glance — which is the whole argument for a tool like this on
namespace-saturated formats.
Defining your own concepts: the taxonomy¶
This is where XBRL goes beyond every other vocabulary in this section. A company
that needs a concept the standard taxonomy lacks mints its own namespace and
declares concepts in it. Each concept is an xs:element — but a very particular
one:
schema http://acme.example/2025 (elementFormDefault=qualified)
ns acme = http://acme.example/2025
ns xbrli = http://www.xbrl.org/2003/instance
import http://www.xbrl.org/2003/instance from xbrl-instance-2003-12-31.xsd # (1)!
element Revenues : xbrli:monetaryItemType nillable substitutes xbrli:item # (2)!
- The taxonomy
imports the core XBRL instance schema — your concepts are built on XBRL's base types, the sameimportmechanism from the XSD chapter. - The whole game is on this line. A concept is an element whose type is an XBRL
item type (
monetaryItemType,stringItemType, …) and whichsubstitutes xbrli:item— XSD substitution groups. That singlesubstitutionGroupis what makesacme:Revenuesreportable anywhere the XBRL spec allows anxbrli:item, without the spec knowing your concept exists. It is the extension mechanism of Atom, implemented with substitution groups instead of wildcards.
The XBRL-specific attributes live in the XBRL namespace too
Concepts also carry xbrli:periodType (instant vs duration) and
xbrli:balance (debit/credit) attributes. They are attributes from the
XBRL namespace attached to your element — foreign-namespace attributes,
exactly like xlink:href on an SVG <use>. XBRL is built almost
entirely by composing other namespaces this way.
The validation stack, scaled up¶
XBRL closes the loop with the e-invoicing validation pipeline, just with more layers:
flowchart TD
I["Instance"] --> X["XSD<br/>structure & item types"]
X --> C["Calculation linkbase<br/>(sub-totals add up)"]
C --> D["Dimensions / formula rules<br/>(business logic)"]
D --> OK["Accepted filing"]
Where EN16931 used Schematron for business rules, XBRL uses linkbases and a Formula spec — but the principle is identical: XSD checks shape, a higher layer checks that the numbers make sense (e.g. that line items sum to the reported total).
A calculation rule, concretely¶
The second layer is a calculation linkbase — another XLink document, separate from both the instance and the schema. It does not contain numbers; it states relationships between concepts, and a validator then checks the reported facts against them. The rule "GrossProfit = Revenues − CostOfRevenue" is two arcs with weights:
link:linkbase
link:calculationLink(xlink:role="http://acme.example/role/IncomeStatement", xlink:type="extended")
link:loc(xlink:href="acme-2025.xsd#us-gaap_GrossProfit", xlink:label="gp", xlink:type="locator") # (1)!
link:loc(xlink:href="acme-2025.xsd#us-gaap_Revenues", xlink:label="rev", xlink:type="locator")
link:loc(xlink:href="acme-2025.xsd#us-gaap_CostOfRevenue", xlink:label="cor", xlink:type="locator")
link:calculationArc(weight="1.0", xlink:from="gp", xlink:to="rev", xlink:arcrole="…/summation-item") # (2)!
link:calculationArc(weight="-1.0", xlink:from="gp", xlink:to="cor", xlink:arcrole="…/summation-item")
locelements are locators — they give a short locallabelto a concept defined elsewhere (in the taxonomy.xsd). The arcs below wire labels together, not the long element names; this is the XLink indirection from XML Signature and SVG<use>, applied to math.- Each
summation-itemarc says "this child contributes to that parent." Theweightis the multiplier:+1.0adds Revenues into GrossProfit,-1.0subtracts CostOfRevenue. The total is implied by the graph of arcs, never written down.
A validator walks those arcs, fetches the matching facts for each context
(FY2025: 391035 − 210352 = 180683, which equals the reported GrossProfit —
consistent), and flags any context where the arithmetic does not hold:
[ERROR] calc:inconsistentCalculation
concept=us-gaap:GrossProfit context=FY2025
computed=180,683,000,000 (Revenues − CostOfRevenue)
reported=170,000,000,000 difference=10,683,000,000
Two subtleties make this its own layer rather than a <xs:assert>: the check is
per context (the same rule re-runs for FY2025, FY2024, and every dimensional
slice), and tolerance is governed by each fact's decimals — two facts reported to
the nearest million are only compared to that precision, so rounding never trips a
false positive.
Things to note¶
- You can define your own namespace of concepts and have them slot into a standard via substitution groups — XSD's most far-reaching extension mechanism, here doing industrial-scale work.
- Context and unit factoring keeps thousands of facts from repeating their who/when/in-what — a namespaced answer to data normalization.
- Real documents pile up many namespaces; the value of a flattening view like
unxmlgrows with the document. - The shape rhymes with everything before it: a public model, your data, and a layered validation stack — XSD for structure, something higher for business rules.
That is the tour. Across every vocabulary the same handful of namespace moves
kept reappearing — default vs prefixed, borrowing (xlink, fo, ds, Dublin Core),
versioning by URI, and three flavors of extension (wildcard, container, reserved
prefix, substitution group). Once you can spot those, most namespaced XML reads
as familiar shapes — only more or less verbose. And when it is verbose, there's
always unxml.