> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hiloop.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Query telemetry

> Run SQL over one run or logical lineage subtree.

Telemetry is a single `events` table you query with **SQL**. The gateway forces an organization predicate
from your verified identity, so the SQL you write is organization-agnostic and only ever reads your own
rows. Only a single `SELECT` runs; there's a row cap and a timeout.

## CLI

The pragmatic scoping flags build a `SELECT` over a compact default column set — event id, time,
signal, name, run identity, principal, and payload size; pass `--fields <col,col,…>` to choose
columns, or `--fields '*'` for every column. Return LLM events for a run:

```sh theme={null}
hiloop query --run-id "$RUN_ID" --signal llm
```

For anything richer, pass the SQL yourself with `--sql` (inline, `@file`, or `-` for stdin):

```sh theme={null}
hiloop query --sql "
  SELECT lineage_path,
         COUNT(*) AS calls
  FROM events
  WHERE run_id = '01K6Z…' AND signal = 'llm'
  GROUP BY lineage_path
  ORDER BY calls DESC
  LIMIT 100"
```

A logical lineage subtree is a prefix predicate on `lineage_path` — the path itself, plus
everything below it:

```sh theme={null}
hiloop query --sql "
  SELECT * FROM events
  WHERE signal = 'exec'
    AND (lineage_path = '01H8A.01H8B' OR lineage_path LIKE '01H8A.01H8B.%')
  LIMIT 100"
```

## API

Send a single `SELECT` to `POST /v1/telemetry/sql`:

```sh theme={null}
hiloop api /v1/telemetry/sql -X post -d '{
  "sql": "SELECT * FROM events WHERE run_id = '\''01K6Z…'\'' AND signal = '\''llm'\'' LIMIT 100"
}'
```

The response is `{"rows": [ {col: value, …}, … ], "columns": [ … ]}` — a list of plain JSON object
rows, with `null` columns omitted per row and 64-bit integer columns (`ts_wall_ns`, counts) encoded
as decimal strings, plus the declared column names in projection order (so a column that is `NULL`
in every row is still visible).

## What you can query

`SELECT` over the `events` columns: identity and time (`run_id`, `lineage_path`, `signal`, `name`,
`ts_wall_ns`, …), promoted network columns (`http_status_code`, `http_host`, …), and arbitrary
attributes via `hiloop_json_get(attributes_json, '$.path.to.key')`. Model-shaped fields (model,
token counts, messages) derive from the captured payload bodies through a
[data view](/observability/data-views) like `otel_genai_calls`. Aggregates (`COUNT`, `SUM`, `AVG`,
`MIN`, `MAX`, `approx_percentile_cont`), `GROUP BY`, and `ORDER BY` all work.

Every [signal](/observability/event-model#signals) is queryable the same way — captured workload
events (`llm`, `net`, `log`, `exec`, `span`), available producer observations (`metric` resource
samples), the platform's own records (`runtime` lifecycle),
future runtime-native `egress` decisions, and `annotation`s you wrote.

Unknown columns are rejected with an error that names the offending identifier. DDL, DML, and
multi-statement SQL are rejected — only one `SELECT` runs. See [Event model](/observability/event-model)
for the columns and the [API reference](/api-reference) for the exact request and response schemas.

Save a query you run often as a reusable [data view](/observability/data-views).
