Obtain a real data-model slug through JSON:API, then send a bounded GraphQL
query. A model with no persisted facts returns an empty results array; that is
a valid query but not proof that a workflow has written records.
What you will do
- List the visible data models and choose one real slug.
- Write a GraphQL query and variables to a temporary JSON request file.
- Request at most ten latest rows.
- Verify the GraphQL data/error shape and version headers.
Prerequisites
- Create a model through the Warehouse product flow described in Data models.
- For a populated result, run a workflow whose harmonizer persists facts for that model.
- Use a service account in the same workspace with
api_readand an active run-as member. - Set
HODOFLOW_BASE_URLandTOKEN; install cURL andjq.
Do not invent a slug from a display name. The stored slug is the GraphQL table identity.
Request
List the visible data models. This collection currently does not expose a pagination parameter, so it is the documented exception to the portal's explicit-limit rule. Use the response only to select a real slug:
MODELS_RESPONSE="$(
curl --silent --show-error --globoff \
--header "Accept: application/vnd.api+json" \
--header "Authorization: Bearer $TOKEN" \
"$HODOFLOW_BASE_URL/api/json/v1/data_models?fields[data_model]=name,slug"
)"
printf '%s\n' "$MODELS_RESPONSE" | jq '.data[] | {name: .attributes.name, slug: .attributes.slug}'
DATA_MODEL_SLUG="$(printf '%s\n' "$MODELS_RESPONSE" | jq -er '.data[0].attributes.slug')"
Create a request file. Query and variables stay separate, which avoids escaping nested values by hand:
QUERY_FILE="$(mktemp)"
jq -n --arg slug "$DATA_MODEL_SLUG" '{
query: "query FirstFacts($slug: String!, $limit: Int!) { factQuery(dataModelSlug: $slug, mode: \"latest\", limit: $limit) { count hasNextPage results { entityId observedAt data } } }",
variables: {slug: $slug, limit: 10}
}' > "$QUERY_FILE"
curl --silent --show-error --include \
--request POST \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $TOKEN" \
--data-binary "@$QUERY_FILE" \
"$HODOFLOW_BASE_URL/api/graphql"
You should see 200, a date-based x-api-version, x-request-id, and either a
data.factQuery object or a GraphQL errors array explaining why resolution
did not succeed.
Expected response
A valid model with no matching facts returns:
{
"data": {
"factQuery": {
"count": 0,
"hasNextPage": false,
"results": []
}
}
}
A populated result contains at most ten Fact rows:
{
"data": {
"factQuery": {
"count": 1,
"hasNextPage": false,
"results": [
{
"entityId": "order-123",
"observedAt": "2026-07-31T18:30:00Z",
"data": { "status": "shipped", "total": 125.5 }
}
]
}
}
}
Field names inside data come from your model; they are not a fixed schema in
this guide.
Troubleshooting
- Empty model list: create a data model in the same workspace first.
- Unknown slug error: re-read
.attributes.slug; names and slugs differ. 401: exchange once and retry once.403:factQueryrequiresapi_readand an active run-as member.- Complexity error: reduce
limitor selected fields. - Empty
results: confirm a workflow persisted facts for this exact model and workspace. An empty result is not an authentication failure. 429: obeyRetry-Afterbefore retrying.
Remove the temporary file and local response values:
rm "$QUERY_FILE"
unset QUERY_FILE MODELS_RESPONSE DATA_MODEL_SLUG
Next steps
- Compare latest and timeline semantics.
- Add variables-based filters and pagination.
- Browse the exact factQuery reference.