Browse technical docs

Serialization & Export

A published RoboLedger report downloads as a file in three formats: the holon (JSON-LD), the Tavi compiled model, and an XBRL 2.1 package. This page covers what each format contains, what each leaves out, how to download one, and how someone without an account can read it.

Quick Start: Publish a report with create-report, then query the reportDownloadUrl GraphQL field for a short-lived link to the file. With no format, the link is to the holon.

Running your own stack? Every example here works against a local deployment: use http://localhost:8000 and the key from just demo-user. See Local Development.

Table of Contents

Overview

A published report is not a rendered document. It is a structured object: the report's facts, periods, concept declarations, presentation and calculation networks, and the Information Block for each statement. All three formats are projections of that one assembled object, so they cannot disagree with each other.

Formatformat valueWhat it is for
Holon JSON-LDHOLON_JSONLDThe report's anchor file, stored at publish, and the default download: JSON-LD in dataset form, as named graphs, and the one format that carries the whole report; opens in xbrlkit
TaviTAVIA compact compiled model in XBRL International's Project Tavi form, the form the SEC pipeline also publishes beside each filing; opens in xbrlkit
XBRL 2.1XBRL_2_1A standard XBRL 2.1 package for regulators and XBRL tooling

Prerequisites

  • A RoboSystems account and an API key; see Quick Start
  • A graph with the RoboLedger extension, ledger data, a chart-of-accounts mapping, and at least one published report

What a Report Bundle Carries

Every format is built from the same contents:

ContentWhat it holds
EntityThe reporting entity: name, legal name, tax id, country
PeriodsThe report's period columns, and each distinct period its facts use (instant or duration)
Reporting styleThe entity's Reporting Style, by id (a UUID). The four-segment code such as BSC-CORP-IS02-CF1 belongs to the Style itself; resolve the id against it to get the code.
Framework pinsThe framework versions the report was built against. rs-gaap@v1 depends on fac@v1 and cm@v1, so there are three.
ConceptsA declaration for each concept the report uses: qname, label, balance type, period type, abstract or not, monetary or not
NetworksThe presentation, calculation, and definition arcs, grouped by network
UnitsFor example iso4217:USD
FactsEach fact with its concept, period, unit, entity, value, decimals, FactSet, and the structure it is pinned to
Information BlocksOne envelope per statement network, with its display order
Report metadataReport id, generation number, filing status and date, the report it supersedes, and sharing provenance

When a Bundle Is Made

The holon is built and stored as part of publishing. create-report and regenerate-report write it inside the publish itself: if the file cannot be stored, the publish fails, so there is no published report without its file.

Each publish writes a generation. The first publish is generation 1, and each regenerate-report rebuilds the report from the current ledger and writes the next one. file-report also stamps a new generation, so the filed record's downloads carry its filing date. A generation never changes once written, and earlier generations are kept. reportDownloadUrl returns the current generation and tells you its number.

The holon exists from the moment of publish. The Tavi and XBRL files are built from the same generation the first time someone asks for them, then kept, so later downloads of that generation are immediate. Because a generation is immutable, a cached file can never be stale.

export ROBOSYSTEMS_API_KEY=rfs...   # Settings → API keys at robosystems.ai
export GRAPH_ID=kg...               # from GET /v1/graphs or the app's graph selector

curl -X POST "https://api.robosystems.ai/extensions/roboledger/$GRAPH_ID/operations/create-report" \
  -H "X-API-Key: $ROBOSYSTEMS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "FY2025 Annual Report",
    "taxonomy_id": "rs-gaap",
    "mapping_id": "struct_01K8...",
    "period_start": "2025-01-01",
    "period_end": "2025-12-31",
    "period_type": "annual",
    "comparative": true
  }'

mapping_id is the id of the graph's chart-of-accounts mapping structure, a struct_… id; see Chart of Accounts Mapping. The id in the response's result is the report_id you download with.

Downloading a Bundle

Downloads are a GraphQL read field, not a REST resource: a download is a read of stored state, so it lives with the other report reads.

POST /extensions/{graph_id}/graphql
{ reportDownloadUrl(reportId:, format:, expiresIn:) { … } }
ArgumentDefaultNotes
reportIdRequired; the report's id
formatHOLON_JSONLDHOLON_JSONLD, TAVI, or XBRL_2_1
expiresIn300Lifetime of the link in seconds, 60 to 3600

Every format answers with the same envelope: a short-lived signed link that the client follows to fetch the file. The API never streams the bytes itself.

curl -X POST "https://api.robosystems.ai/extensions/$GRAPH_ID/graphql" \
  -H "X-API-Key: $ROBOSYSTEMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ reportDownloadUrl(reportId: \"'"$REPORT_ID"'\") { downloadUrl expiresAt contentType format generationCount omittedContent } }"}'
{
  "downloadUrl": "https://…",
  "expiresAt": "2026-06-11T19:05:00Z",
  "contentType": "application/ld+json",
  "format": "holon-jsonld",
  "generationCount": 1,
  "omittedContent": []
}
curl -L "<downloadUrl>" -o report.holon.jsonld

Pass format: TAVI (-o report.tavi.json) or format: XBRL_2_1 (-o report.zip) for the other two; the query and the envelope are the same.

Formatformat in the responsecontentType
HOLON_JSONLDholon-jsonldapplication/ld+json
TAVItaviapplication/json
XBRL_2_1xbrl-2.1application/zip

omittedContent names report content the chosen format leaves out. It is empty for the holon, which carries everything. The Tavi omits four items, ib_envelopes, definition_links, framework_pins and filing_lifecycle (see Tavi); XBRL 2.1 omits disclosure_notes. The field returns null for a reportId that does not resolve. Error codes are listed under Troubleshooting.

Downloading from Code

From an AI client. On a graph's MCP connection, the get-report-bundle tool returns the same short-lived link, with the same format choices (holon-jsonld by default, tavi, xbrl-2.1). It is meant for a reader on the same machine that loads a file by URL, such as xbrlkit serve. The link carries its own credential, so pass it straight to the tool that fetches it rather than into a message. The RoboLedger connector address (/v1/mcp/roboledger) does not offer this tool; see AI Operators & MCP.

From Python. The Python SDK resolves the link and follows it in one call:

import os
from robosystems_client import RoboSystemsClientConfig, RoboSystemsClients

clients = RoboSystemsClients(
  RoboSystemsClientConfig(
    base_url="https://api.robosystems.ai",
    headers={"X-API-Key": os.environ["ROBOSYSTEMS_API_KEY"]},
  )
)
bundle = clients.ledger.download_report_bundle(
  os.environ["GRAPH_ID"], os.environ["REPORT_ID"], to="report.holon.jsonld"
)
print(bundle.filename, bundle.generation_count, len(bundle.content))

format defaults to "holon-jsonld"; pass format="tavi" or format="xbrl-2.1" for the others. to is optional: without it the bytes come back on bundle.content. A report with no stored bundle raises a GraphQLError carrying REPORT_BUNDLE_NOT_AVAILABLE.

What's Inside Each Format

Holon JSON-LD

The report as JSON-LD in dataset form: named graphs for the report's scene, boundary, and projection. It is the file stored at publish, and the one format that keeps every platform-specific item: the Information Block envelopes, definition arcs, framework pins, and filing lifecycle that the Tavi leaves out, and the disclosure notes that XBRL 2.1 leaves out.

Tavi

The report as a Project Tavi compiled model: one compact JSON document in the OIM-family form XBRL International is drafting, the same form the SEC pipeline writes beside every filing. A RoboLedger report and an SEC filing come out in one shape, and one reader handles both. The document is a documentInfo header and an xbrlModel holding the concepts, labels, networks, groups, units and facts.

Every aspect of a fact, the concept included, is a dimension of it. A fact:

{
  "name": "rpt:fact_01M2…",
  "factDimensions": {
    "xbrl:concept": "rs-gaap:AccountsPayableCurrent",
    "xbrl:period": "2026-09-01T00:00:00",
    "xbrl:entity": "entity:entity_kg1a…",
    "xbrl:unit": "iso4217:USD"
  },
  "factValues": [{ "value": "0", "decimals": 2 }],
  "properties": [{ "property": "rs:structureId", "value": "struct_…" }]
}

Periods are written as xs:dateTime, where the end is exclusive: the instant above is the close of 31 August 2026, and a fiscal year reads 2025-09-01T00:00:00/2026-09-01T00:00:00. A text fact carries xbrl:language in place of a unit, and its value is the text. Ledger amounts are stated to the cent, so a numeric fact carries decimals: 2.

Platform content that Tavi has no built-in slot for rides as model-defined properties in the rs: namespace (https://robosystems.ai/vocab/), each declared in propertyTypes:

PropertyOnHolds
rs:reportingStylethe modelThe Reporting Style id
rs:legalNamethe entityThe legal name, when it differs from the name
rs:blockTypea networkThe statement or note kind, such as balance_sheet
rs:structureIda network, a factThe structure the network presents, and the one a fact is pinned to
rs:factSetIda networkThe FactSet the structure's facts belong to
rs:structureOrdera networkThe structure's display order

Four items still have no place in the file: the Information Block envelopes, the definition arcs, the framework pins, and the filing lifecycle. omittedContent lists them (ib_envelopes, definition_links, framework_pins, filing_lifecycle), and the holon carries all of them.

The Tavi is built from the same generation as the holon on its first download. The format follows XBRL International's working draft, PWD-2026-09-01, and will track it as the draft moves.

XBRL 2.1

A zip of standard XBRL 2.1 files. Two are always present; the linkbases appear only when they have content, so a package has between two and six files:

FileAlways presentContents
instance.xmlYesFacts, with contexts and units derived from each fact's aspects
report.xsdYesThe schema declaring the report's concepts
report-pre.xmlWhen non-emptyPresentation linkbase
report-cal.xmlWhen non-emptyCalculation linkbase
report-def.xmlWhen non-emptyDefinition linkbase
report-lab.xmlWhen non-emptyLabel linkbase

The package carries the statements only. Tenant-authored disclosure notes render in the app and ride the holon and Tavi formats, and the download names them in omittedContent as disclosure_notes. Like the Tavi, the package is built on its first download.

What each format keeps

XBRL 2.1 has no standard place for the platform-specific content, so the package leaves it out; only the disclosure notes are named in its omittedContent.

ContentHolonTaviXBRL 2.1
Statement facts and networksYesYesYes
Disclosure notesYesYesNo
Information Block envelopesYesNoNo
Definition arcsYesNoYes, when present
Reporting Style, FactSetsYesYesNo
Framework pins, filing lifecycleYesNoNo

Reading an Export Without the Platform

Someone who receives a report file needs no RoboSystems account and no graph. The holon and Tavi formats open in xbrlkit, the open-source XBRL toolkit RoboSystems publishes under the MIT license. xbrlkit reads the one report it is given; questions across reports, periods held in other reports, or other entities need the graph.

  • In a browser: xbrlkit.com/file renders the statements, notes, and facts of a .holon.jsonld or .tavi.json file. The file stays on the reader's machine.
  • In an AI client: xbrlkit serve is a local MCP server. Ask the client to load the file by path or URL, then ask about it. Setup for each client is at xbrlkit.com/mcp.
uvx --from "xbrlkit[mcp]@latest" xbrlkit serve

The holon also reads with any JSON-LD or RDF library, and the XBRL package with any XBRL 2.1 processor, such as Arelle. The RoboLedger guide Reports and sharing covers the same hand-off from the app.

Validation

The claim made about the output is that the XBRL package is valid XBRL 2.1. The holon and the Tavi are checked by reading them back: xbrlkit loads either into the same in-memory model the tools run on.

Checking a download yourself. The repository includes a small harness that runs Arelle over a downloaded package; it needs a checkout of the repository but no running stack:

# Arelle: is the zip valid XBRL 2.1?
uv run python -m examples._common.validate --zip report.zip --label fy2025

Changing What a Report Contains

A report's content is the ledger's facts, mapped through the chart of accounts to framework concepts. To change it, change those: post or correct events (Event-Driven Ledger), adjust the mapping (Chart of Accounts Mapping), then run regenerate-report. That writes a new generation, and the next download returns it.

A filed report is fixed. regenerate-report on a report whose filing status is filed or archived is refused with 422; to replace a filed report, archive it and create a new report for the period.

curl -X POST "https://api.robosystems.ai/extensions/roboledger/$GRAPH_ID/operations/regenerate-report" \
  -H "X-API-Key: $ROBOSYSTEMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"report_id": "'"$REPORT_ID"'"}'

Troubleshooting

REPORT_BUNDLE_NOT_AVAILABLE

The report has no stored bundle: it was never published, or its generation did not finish. There is no automatic backfill.

Solution: Run regenerate-report to write a current generation.

INVALID_EXPIRES_IN

expiresIn is outside 60 to 3600 seconds.

REPORT_BUNDLE_SIGNING_FAILED

The link could not be signed, or a format could not be built. Retry; if it persists, report it.

An Unrecognized Format

format is a GraphQL enum, so an unknown name fails validation before the request runs.

Solution: Use HOLON_JSONLD, TAVI, or XBRL_2_1. The flat JSONLD format was retired.

The First Download of a Format Is Slow

The first request for the Tavi or XBRL file of a generation builds it. Later requests for that generation return immediately. A regenerate-report or file-report starts a new generation, so the first download after it builds again. The holon needs no build: it is stored at publish.

Publish Fails on Bundle Upload

The holon is stored as part of the publish; if it cannot be stored, the publish fails with 502 rather than leave a report without its file. file-report stamps a generation the same way and fails the same way, and nothing is filed.

Solution: Re-run the publish or the filing. On a self-hosted deployment, check the object-storage configuration first.

Self-hosted deployments

Demos

The RoboLedger demos exercise the full publish-to-download path: each downloads its report's holon, Tavi and XBRL package through the published SDK and runs Arelle over the package.

just demo-roboledger

Wiki Guides:

API & Codebase:

Support