---
title: "Build Fact Grid"
method: POST
path: "/extensions/roboledger/{graph_id}/operations/build-fact-grid"
tags: ["RoboLedger: Analytical Views"]
---

# Build Fact Grid

`POST /extensions/roboledger/{graph_id}/operations/build-fact-grid`

## Request

- Base URL: `https://api.robosystems.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.robosystems.ai/extensions/roboledger/{graph_id}/operations/build-fact-grid`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Queries LadybugDB `Fact` nodes by element qnames, with filters for periods and entities. Returns deduplicated facts plus the aspects they span — arranging them into a table is the consumer's job, since collapsing cells safely requires the full aspect signature. Works on both roboledger tenant graphs (post-materialization) and the SEC shared repository. Canonical concepts and the form and fiscal-period filters exist on shared repositories only: a ledger's graph carries none of them, and a query by them there is refused rather than answered with nothing.

**Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict.

## Path parameters

- `graph_id` string, required

## Headers

- `Idempotency-Key` string, nullable

## Request body

- CreateViewRequest
  - `elements` string[]: Element qnames (e.g., 'us-gaap:Assets'). Can combine with canonical_concepts.
  - `canonical_concepts` string[]: Canonical concept names (e.g., 'revenue', 'net_income'). Matches all mapped qnames. Shared repositories only.
  - `periods` string[]: Period end dates (YYYY-MM-DD format)
  - `entity` string, nullable: Filter by entity ticker, CIK, or name
  - `entities` string[]: Filter by multiple entity tickers (e.g., ['NVDA', 'AAPL'])
  - `form` string, nullable: Filter by SEC filing form type (e.g., '10-K', '10-Q'). Shared repositories only.
  - `fiscal_year` integer, nullable: Filter by fiscal year (e.g., 2024). Shared repositories only.
  - `fiscal_period` string, nullable: Filter by fiscal period (e.g., 'FY', 'Q1', 'Q2', 'Q3'). Shared repositories only.
  - `period_type` string, nullable: Filter by period type: 'annual', 'quarterly', or 'instant'
  - `include_summary` boolean: Include summary statistics per element. An element reported in more than one unit has none.
  - `limit` integer: Maximum facts to return. Applied after deduplication and sorting, so truncation keeps the most recent periods. Check `metadata.truncated` to see whether more facts matched.
  - `view_config` ViewConfig
    - `rows` ViewAxisConfig[]: Row axis configuration
      - `type` string, required: Axis type: 'element', 'period', 'entity'
      - `include_null_dimension` boolean: Include facts where this aspect is absent (default: false)
      - `selected_members` string[], nullable: Specific members to include (e.g., ['2024-12-31', '2023-12-31'])
    - `columns` ViewAxisConfig[]: Column axis configuration
      - `type` string, required: Axis type: 'element', 'period', 'entity'
      - `include_null_dimension` boolean: Include facts where this aspect is absent (default: false)
      - `selected_members` string[], nullable: Specific members to include (e.g., ['2024-12-31', '2023-12-31'])

## Response `200`

Successful Response

- OperationEnvelopeViewResponse
  - `operation` string, required: Kebab-case operation name
  - `operationId` string, required: op_-prefixed ULID for audit and SSE correlation
  - `status` 'completed' | 'pending' | 'failed', required: Operation lifecycle state
  - `result` ViewResponse: Flat, deduplicated facts plus the aspects they span. No server-side pivot: each fact is returned as its own record so the consumer can arrange cells on the full aspect signature. Summing across entities, units, or elements that merely share a local name is a presentation decision this endpoint refuses to make on the caller's behalf.
    - `metadata` ViewMetadata, required
      - `view_id` string, required: Unique view identifier
      - `facts_processed` integer, required: Number of facts processed
      - `construction_time_ms` number, required: Time to build view in milliseconds
      - `source` string, required: Data source type
      - `period_start` string, nullable: Period start date
      - `period_end` string, nullable: Period end date
      - `truncated` boolean: True when more facts matched than `limit` allowed. The returned facts are the most recent by period; narrow the filters or raise `limit` to see the rest.
    - `dimensions` Dimension[]: Aspects spanned by the returned facts
      - `name` string, required: Dimension name (e.g., 'Element', 'Period')
      - `type` 'element' | 'period' | 'entity', required
      - `members` string[]: List of dimension members
    - `facts` FactRecord[]: Deduplicated fact records
      - `element_id` string, required: Element qname (e.g., 'us-gaap:Assets')
      - `element_name` string, nullable: Element local name
      - `period_start` string, nullable: Period start date (YYYY-MM-DD); null for instant facts. A duration fact is identified by (start, end) — two facts for the same element can share an end date and differ only here (e.g. a quarterly and a year-to-date figure from the same 10-Q).
      - `period_end` string, nullable: Period end date (YYYY-MM-DD)
      - `duration_type` string, nullable: Period duration classification (e.g. 'quarterly', 'nine_months', 'annual'); null for instant facts. Use with period_start to tell overlapping windows apart.
      - `value` number, nullable: Numeric fact value
      - `unit` string, nullable: Unit of measure (e.g., 'USD')
      - `entity_ticker` string, nullable: Entity ticker; present only when an entity filter was applied
      - `entity_name` string, nullable: Entity name; present only when an entity filter was applied
    - `summary` object, nullable: Per-element aggregates, only when include_summary=true. `total` and `average` span every returned period, so they are present for duration elements only — instants omit both (a balance summed across periods is not a balance). Overlapping duration windows sharing a period_end (quarter + year-to-date) contribute only the narrowest window, so a quarter is never double-counted inside its own YTD figure.
  - `at` string, required: ISO-8601 UTC timestamp
  - `createdBy` string, nullable: User ID that initiated the operation
  - `idempotentReplay` boolean: True when this envelope came from the idempotency cache — the underlying command did not execute again. False on fresh executions.

## Other responses

- `400`: Invalid request
- `401`: Authentication required
- `403`: Access denied
- `404`: Resource not found
- `409`: Idempotency-Key conflict — key reused with different body
- `422`: Validation error
- `429`: Rate limit exceeded
- `500`: Internal server error

---

[API](https://skmtc.dev/robosystems/apis/robosystems-api.md) · [All operations](https://skmtc.dev/robosystems/apis/robosystems-api/llms.txt) · [OpenAPI document](https://skmtc.dev/robosystems/apis/robosystems-api/revisions/e661e2d5ca6a?raw)
