# GraphQL warehouse API

> Use the read-only factQuery operation to retrieve latest or historical warehouse records for an existing data model.

Hodoflow's GraphQL surface has one application-data job: read warehouse records
through `factQuery`. It is mounted at `POST /api/graphql`, requires `api_read`,
and addresses a data model by slug as a logical table. Its mapped fields are the
columns and each `Fact.data` object is one row.

Configuration and observability remain on [JSON:API](/platform/json-api).
GraphQL exposes no mutations. The schema currently exposes no subscription
fields, so there is no public subscription capability to build against.

## Before your first query

Unlike the JSON:API quickstart, `factQuery` needs product state:

1. create a data model and its fields in Warehouse;
2. run a workflow that persists at least one mapped record if you want a
   populated result;
3. obtain the model's `slug` through JSON:API or the application; and
4. authenticate with an active service account carrying `api_read`.

Follow [Run your first warehouse query](/graphql/first-query) for the exact
requests and checkpoints.

## Query modes

`latest` is the default and returns one current row per entity. Rows without an
entity ID are excluded because no entity identity exists to collapse.

`timeline` returns historical versions inside a time window. When omitted,
`until` defaults to now and `since` defaults to 90 days before it. A requested
span wider than 366 days is clamped to 366 days. Explicit bounds also apply in
latest mode as normal filters.

See [factQuery arguments and results](/graphql/fact-query) for every argument and
response field.

## Pagination and cost

Offset pagination defaults to 50 rows and rejects a limit above 250. Pass a
limit explicitly anyway. The useful limit can be lower because complexity is
based on page size and selected fields.

Default configurable guards are:

| Guard | Default |
|---|---|
| Maximum complexity | 200 |
| Maximum nesting depth | 10 |
| Maximum parseable tokens | 8,000 |

Filters are a JSON array encoded inside a GraphQL string, at most 20 entries.
Use query variables rather than hand-escaping it inline. Continue with
[Filters, pagination, and limits](/graphql/filters-pagination-limits).

## Authentication and errors

Send `Authorization: Bearer $TOKEN` and `Content-Type: application/json`.
`factQuery` requires `api_read` and the run-as member's active workspace access.
The `health` liveness field is public and is the only exception.

Parsed GraphQL failures can return HTTP `200` with an `errors` array. Invalid
tokens and rate limits happen at the transport layer and use non-200 API error
responses. Always inspect both status and GraphQL errors.

## Machine reference

- [factQuery browser reference](/reference/graphql/fact-query)
- [GraphQL SDL](/schema.graphql)
- [SDL usage guide](/tools/graphql-sdl)
- `x-api-version` returns the date-based GraphQL schema identity

The application may expose GraphiQL in development, but integrations must not
depend on a production playground.
