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

# Query Guide API

> Describe the best logs, metrics, traces, AI, monitor, and incident query surfaces for agents and scripts.

Use the Query Guide API when you want an agent or automation to discover the best Squasher endpoints before it starts debugging.

## Get query surfaces

```text theme={null}
GET https://api.squasher.ai/v1/projects/{project_id}/query-guide
```

The response lists the main query surfaces for a project, including:

* logs
* metrics and metric timelines
* traces
* errors and raw events
* AI observability
* monitors
* incidents

Each surface includes a canonical path, a parameter list, and example investigation URLs.

For metric timelines, the guide now points at the same generic metric series path the dashboard uses. That means automations and dashboard widgets can both request `avg`, `count`, `sum`, `p50`, `p90`, `p95`, and `p99` over the same OTLP metric names instead of relying on product-specific endpoints.

## Safe SQL schema

```text theme={null}
GET https://api.squasher.ai/v1/projects/{project_id}/schema
```

The schema response describes Squasher's public observability query model. The safe query API and MCP Code Mode `schema.get` operation use the same model, so agents see the same column map that the validator enforces.

Queryable tables:

* `logs`
* `spans`
* `metric_gauge_points`
* `metric_sum_points`
* `metric_histogram_points`
* `metric_exponential_histogram_points`

Queries that reference an unknown column on these tables are rejected before execution with `UNKNOWN_COLUMN` and a suggested nearby column when one is available.

## Safe SQL queries

```text theme={null}
POST https://api.squasher.ai/v1/projects/{project_id}/query
POST https://api.squasher.ai/v1/projects/{project_id}/query/explain
```

Request body:

```json theme={null}
{
  "sql": "SELECT timestamp, severity, message FROM logs WHERE timestamp > now() - INTERVAL 1 HOUR LIMIT 100",
  "long_range": false
}
```

The `sql` field is required. `long_range` is optional and only extends the allowed time window for projects that need a 30-90 day query. Both endpoints validate the SQL against the public schema, inject the project scope, and reject unsupported fields or shapes with a structured `400` response instead of executing untrusted input directly.

Safe-query responses are limited to 10,000 rows and 8 MiB. If the serialized result exceeds the byte limit, the API returns `400` with `error: "QUERY_RESULT_TOO_LARGE"`. Select fewer fields, aggregate before returning rows, or narrow the time window and retry. The TypeScript client exposes this response through the public `QueryError` type, and the CLI prints the same recovery guidance.

Every physical table scan must have a positive lower timestamp bound in its `WHERE` clause. A timestamp expression in `SELECT`, under `NOT`, or in only one side of an otherwise-unbounded `OR` does not satisfy the guard. Multi-table joins are rejected; issue separate bounded queries and correlate their returned IDs instead. This keeps agent-generated SQL inside predictable ClickHouse Cloud scan limits.

Use the CLI when an agent or script already has a safe SQL statement and needs JSON back from the public API:

```bash theme={null}
squasher query explain --project <project_id> --sql "SELECT timestamp FROM logs WHERE timestamp > now() - INTERVAL 1 HOUR LIMIT 1"
squasher query run --project <project_id> --file query.sql --long-range
```

Pass exactly one of `--sql` or `--file`. The CLI sends the same request body as the API examples above, and `--long-range` only adds `"long_range": true` when the flag is present.

## Monitor queries

```text theme={null}
GET https://api.squasher.ai/v1/projects/{project_id}/monitors
GET https://api.squasher.ai/v1/projects/{project_id}/monitors/{monitor_id}
POST https://api.squasher.ai/v1/projects/{project_id}/monitors
POST https://api.squasher.ai/v1/projects/{project_id}/monitors/upsert
PATCH https://api.squasher.ai/v1/projects/{project_id}/monitors/{monitor_id}
DELETE https://api.squasher.ai/v1/projects/{project_id}/monitors/{monitor_id}
```

Common list parameters:

* `q` - search name, target, linked component, incident id, and runbook URL
* `kind` - monitor kind such as `http`, `ssl`, `dns`, or `heartbeat`
* `status` - `healthy`, `degraded`, `failed`, or `paused`
* `enabled` - `true` or `false`
* `limit`
* `offset`

Create and upsert requests use `{ name, kind, target, config, enabled?, linkedComponent?, runbookUrl? }`. `upsert` matches by monitor name so scripts and agents can safely refresh declarative monitor config without double-creating the same check.

## Incident queries

```text theme={null}
GET https://api.squasher.ai/v1/projects/{project_id}/incidents
GET https://api.squasher.ai/v1/projects/{project_id}/incidents/{incident_id}
GET https://api.squasher.ai/v1/projects/{project_id}/incidents/{incident_id}/comments
POST https://api.squasher.ai/v1/projects/{project_id}/incidents/{incident_id}/comments
DELETE https://api.squasher.ai/v1/projects/{project_id}/incidents/{incident_id}/comments/{comment_id}
PATCH https://api.squasher.ai/v1/projects/{project_id}/incidents/{incident_id}/assignee
POST https://api.squasher.ai/v1/projects/{project_id}/service-attribution/resolve
GET https://api.squasher.ai/v1/projects/{project_id}/service-ownership/debt
GET https://api.squasher.ai/v1/projects/{project_id}/service-ownership/import-candidates
GET https://api.squasher.ai/v1/projects/{project_id}/service-ownership/teams
POST https://api.squasher.ai/v1/projects/{project_id}/service-ownership/teams
PATCH https://api.squasher.ai/v1/projects/{project_id}/service-ownership/teams/{team_id}
DELETE https://api.squasher.ai/v1/projects/{project_id}/service-ownership/teams/{team_id}
GET https://api.squasher.ai/v1/projects/{project_id}/service-ownership/services
POST https://api.squasher.ai/v1/projects/{project_id}/service-ownership/services
PATCH https://api.squasher.ai/v1/projects/{project_id}/service-ownership/services/{service_id}
PUT https://api.squasher.ai/v1/projects/{project_id}/service-ownership/services/{service_id}/links
DELETE https://api.squasher.ai/v1/projects/{project_id}/service-ownership/services/{service_id}
```

Common list parameters:

* `{incident_id}` accepts the project-scoped incident ID returned by the API, for example `inc_7`. Existing UUID incident links remain accepted for compatibility.
* `q` - search title, summary, assignee, root cause, and monitor metadata
* `status` - `investigating`, `identified`, `monitoring`, or `resolved`
* `severity` - `low`, `medium`, `high`, or `critical`
* `monitor_id` - limit results to one linked monitor
* `limit`
* `offset`

The incident detail response includes timeline events, evidence records, recent monitor checks, and service attribution with owner-team metadata so agents can build investigation context without scraping the dashboard.

Use comments and assignee updates for responder coordination. Comment create and delete calls need a user-authenticated actor, such as OAuth, because comments are attributed to a team member.

Use the service attribution resolver when an agent has a monitor, trace, alert, repository, status component, or runbook signal but no incident row yet. The request body accepts `monitorId`, `monitorName`, `traceServiceName`, `explicitServiceName`, `statusComponentName`, `repository`, `runbookUrl`, and `alertFields` entries such as `{ "path": "service.name", "value": "checkout-api" }`. The response uses the same service attribution object returned by incident detail.

Use service ownership import candidates to preview operational services and link payloads from monitors and status components that are not assigned to a service yet. Each candidate includes a deterministic `suggestedSlug`, proposed monitor/status component IDs, source records, and `existingServiceId` when the slug already matches a service that should be linked instead of duplicated. Apply a candidate with `POST /v1/projects/{project_id}/service-ownership/import-candidates/{suggested_slug}/apply` to create the suggested service or merge its aliases and source links into the matching existing service.

Use the service ownership debt endpoint to find only ownership gaps Squasher can prove from current product data: services missing owner teams, services missing runbooks, monitors not linked to an operational service, and status components not linked to a service.

Use the service ownership teams endpoint to list owner teams available for service assignment. Create owner teams with `{ name, slug, description? }`. If a retry hits an existing team with the same slug, the API returns that existing team. Patch the same fields on a team by id to keep ownership metadata current. Delete a team by id when it should no longer own operational services; existing service owner references are cleared.

Use the service ownership services endpoint to list the current operational service graph, including aliases, repositories, monitor links, status component links, dependency service ids, runbooks, dashboards, and owner-team metadata.

Create service ownership records with `{ name, slug, description?, ownerTeamId?, runbookUrl?, dashboardUrl? }`. If a retry hits an existing service with the same slug, the API returns that existing service. Patch the same fields on a service by id to update ownership metadata without replacing links. Replace service links with `{ aliases, repositories, monitorIds, statusComponentIds, dependencyServiceIds }`; the link update is a full replacement so pass empty arrays for link sets you want to clear. Delete a service by id when an operational service should no longer participate in incident ownership or routing.

## Agent handoff

This should be the first API an agent calls when it is unsure which observability surface to use:

```text theme={null}
Use Squasher Query Guide for project <project_id>. Read the returned query surfaces, choose the narrowest next API or CLI command, and explain why that surface fits the user's question before running it.
```

For MCP, call `search("queryGuide")`, `search("schema.get")`, `search("query.run")`, or `search("query.explain")` before executing Code Mode snippets. Keep safe SQL bounded by time and project scope.
