# GraphQL SDL contract

> Download Hodoflow's versioned GraphQL schema and use it for validation and type generation with the runtime rules kept in view.

The public [GraphQL schema definition](/schema.graphql) is the
machine-readable type contract for `POST /api/graphql`. Use it to validate
operations, power editor completion, or generate types for queries your
integration owns. It is generated from the running schema and contains no
tenant data or credentials.

SDL describes GraphQL names and types. It does not describe every runtime rule
that decides whether a query is authorized, affordable, or valid for a
particular data model. Use it together with the [GraphQL guide](/graphql), not
as a replacement for that guide.

## Download the schema

Download the schema from the release-matched developer portal:

```bash
export HODOFLOW_DEVELOPER_URL=https://developer.hodoflow.com
curl -fsS "$HODOFLOW_DEVELOPER_URL/schema.graphql" -o hodoflow-schema.graphql
```

The first comment line identifies the contract version:

```graphql
# Hodoflow GraphQL API — schema version YYYY-MM-DD
```

The next comment records that the file is generated from Hodoflow's running
schema and must not be edited by hand.

The date is the GraphQL schema version. It is independent of JSON:API `v1` and
of the `x-api-version` header returned by the HTTP API. There is no request
parameter that selects an older SDL: pin the file used by your integration and
review a newer schema before upgrading.

The endpoint serves SDL as `text/plain`, so it can be read directly in a
browser or fetched by build tooling without authentication. Fetching the
schema does not grant access to warehouse records.

## Read the public type system

The schema currently has one root type:

```graphql
schema {
  query: RootQueryType
}
```

`RootQueryType` exposes two fields:

| Field | Purpose | Authentication |
|---|---|---|
| `health` | Lightweight liveness result | Public |
| `factQuery` | Read current or historical warehouse records for one data-model slug | Bearer token with `api_read` |

There is no mutation root and no subscription root. The application may expose
a GraphQL socket transport, but an SDL without a `subscription` root has no
subscription field to call. Check the schema rather than generating a
subscription client from the existence of a socket URL.

`factQuery` returns `PageOfFact`. Generate against the nullability written in
the SDL: the page itself and its `results` list are nullable, each returned
`Fact` is non-null, and `count` is optional. A `Fact` includes identifiers and
timestamps plus a `Json` `data` value whose properties come from the selected
data model.

Map the custom scalars deliberately:

| Scalar | Wire value | Suggested client representation |
|---|---|---|
| `DateTime` | ISO-8601 timestamp normalized to UTC | Your language's offset-aware timestamp type, or a validated string |
| `Json` | Arbitrary JSON value | A JSON value/object type rather than an unparsed GraphQL string |

The `data` object is intentionally not expanded into a static GraphQL object
type. Its keys and value shapes are defined by the data model in the workspace,
so validate or decode them with the model contract your integration expects.

## Know what SDL cannot express here

Several `factQuery` arguments are currently `String` or `Int` in SDL even
though the resolver applies narrower rules. The SDL therefore cannot tell a
generic generator all of the following:

- `mode` accepts `latest` or `timeline` and defaults to `latest`;
- `timeAxis` accepts `observed_at` or `inserted_at` and defaults to
  `observed_at`;
- `sort` and `sortDirection` accept a defined set of values;
- `limit` defaults to 50 and is capped at 250 before query-cost limits apply;
- `offset` defaults to 0;
- `filters` is a JSON array encoded inside a GraphQL string, with field-type
  specific operators and a maximum of 20 entries;
- omitted timeline bounds create a 90-day window, capped at 366 days; and
- complexity, nesting-depth, token, and per-credential rate limits apply before
  or during execution.

The exact values, paging rules, cost formula, and error examples are maintained
in [GraphQL](/graphql). Authentication is also outside GraphQL's type system:
send `Authorization: Bearer $TOKEN` for `factQuery`, and give that service
account `api_read`. A schema-aware client cannot infer those requirements from
the field signature.

## Generate types from operations you own

Prefer document-based generation: keep each named query in source control and
generate its variables and result type against the pinned SDL. This produces a
smaller, more useful contract than generating a general-purpose client for the
entire schema.

For example, a bounded operation can live in `current-orders.graphql`:

```graphql
query CurrentOrders($slug: String!, $limit: Int!) {
  factQuery(dataModelSlug: $slug, mode: "latest", limit: $limit) {
    hasNextPage
    results {
      entityId
      observedAt
      data
    }
  }
}
```

Configure the generator's `DateTime` and `Json` mappings, then make the HTTP
request yourself or through a standard GraphQL client. Generated types do not
turn the result into a Hodoflow SDK, refresh access tokens, add the `api_read`
scope, choose safe query limits, or decode the model-specific `data` object for
you.

Keep variables separate from the query text. This is especially important for
`filters`, which is JSON nested inside a GraphQL string; passing it as a
variable avoids another layer of manual escaping.

## Detect schema changes

Store the exact SDL used by a released integration. Before adopting a newer
release, compare schemas with a GraphQL schema-diff tool or a plain text diff:

```bash
diff -u hodoflow-schema.graphql hodoflow-schema.next.graphql
```

Treat removed fields or arguments, tightened nullability, and changed scalar or
field types as breaking. Additive fields remain safe only when your decoder
ignores response keys it did not request—which ordinary GraphQL clients do,
because a response follows the operation's selection set.

A changed date comment tells you the schema artifact changed; it does not by
itself say whether the change affects your operations. Revalidate every stored
query against the new SDL, regenerate types, and run the integration's own
query-level checks before deployment.

For a complete request and operational behavior, continue to the
[GraphQL guide](/graphql). For the other generated machine contract, see the
[OpenAPI contract](/tools/openapi).
