Indicators
Indicators are named, reusable timeseries measurements backed by ConfitSQL queries. They surface across Agents, Issues, and Change Monitors, and live in a shared catalog that Firetiger maintains over time as your telemetry evolves.
Each Indicator is one of two kinds:
INDICATOR_KIND_RATIO— query producestime,good_events,total_eventscolumns. Used for success-rate measurements (e.g. fraction of/checkoutrequests under 450ms).INDICATOR_KIND_GAUGE— query producestime,valuecolumns. Used for raw scalar measurements (e.g. queue depth, active organizations).
The query template references three reserved time-window placeholders that the service substitutes at compile time:
@start_time— inclusive query-window start@end_time— exclusive query-window end@resolution— bucket size
Service: firetiger.goals.v1.IndicatorsService and firetiger.goals.v1.IndicatorRelationsService
Resource name patterns:
indicators/{indicator}— the Indicator definitionindicators/{indicator}/activities/{activity}— append-only maintenance logindicatorRelations/{relation}— attaches an Indicator to an Agent / Issue / Investigation
Access: Read-write
Example flow
Define an Indicator attached to an Agent, attach it to an Issue, then compile its query for charting.
1. Create an Indicator
curl -X POST "https://api.cloud.firetiger.com/firetiger.goals.v1.IndicatorsService/CreateIndicator" \
-u "$USERNAME:$PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"indicator_id": "checkout-latency-p95",
"indicator": {
"display_name": "Checkout p95 latency under 450ms",
"description": "Fraction of /checkout requests completing under 450ms.",
"kind": "INDICATOR_KIND_RATIO",
"unit": "%",
"query": {
"connections": ["connections/spans"],
"confit_sql": "SELECT time_bucket(@resolution, span_start) AS time, count(*) FILTER (WHERE duration_ms < 450) AS good_events, count(*) AS total_events FROM spans WHERE span_start >= @start_time AND span_start < @end_time AND span_name = '\''/checkout'\'' GROUP BY 1",
"description": "Reads from spans where span_name = '\''/checkout'\''. Latency from duration_ms."
}
},
"initial_resource": "agents/checkout-flow"
}'
CreateIndicator is atomic: on success the response carries the Indicator, the initial IndicatorRelation against initial_resource, and an initial IndicatorActivity ("Indicator created."). The server validates that confit_sql references all three reserved placeholders, then asks ConfitService.ValidateQuery to perform a schema-aware dry-run — the engine plans the query against in-memory empty parquet files derived from each touched table’s iceberg-declared schema (no data scan) — and confirms the planned output schema matches the kind contract.
2. Attach to an Issue
curl -X POST "https://api.cloud.firetiger.com/firetiger.goals.v1.IndicatorRelationsService/CreateIndicatorRelation" \
-u "$USERNAME:$PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"indicator_relation": {
"indicator": "indicators/checkout-latency-p95",
"resource": "issues/FT-241"
}
}'
The backing table enforces uniqueness on (indicator, resource) for non-deleted relations. Re-attaching after a soft-delete is permitted.
3. Compile a chart query
curl -X POST "https://api.cloud.firetiger.com/firetiger.goals.v1.IndicatorsService/CompileIndicatorQuery" \
-u "$USERNAME:$PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"name": "indicators/checkout-latency-p95",
"start_time": "2026-04-01T00:00:00Z",
"end_time": "2026-04-29T00:00:00Z",
"resolution": "300s"
}'
The response carries a ready-to-execute firetiger.query.v2.QueryRequest and the current Indicator metadata. Send the request directly to firetiger.query.v2.ConfitService.Query — result data does not proxy through the goals API.
Methods
IndicatorsService
| Method | Description |
|---|---|
| CreateIndicator | Define a new Indicator and its first relation atomically |
| GetIndicator | Retrieve an Indicator by name |
| BatchGetIndicators | Retrieve multiple Indicators in one round trip |
| ListIndicators | Enumerate Indicators in the catalog |
| UpdateIndicator | Edit the Indicator definition (writes a maintenance-log activity) |
| DeleteIndicator | Soft-delete (archive) an Indicator and its IndicatorRelations |
| UndeleteIndicator | Restore a soft-deleted Indicator and its cascade-deleted relations |
| PurgeIndicators | Hard-delete soft-deleted Indicators matching a filter |
| ListIndicatorActivities | Read the maintenance log |
| CompileIndicatorQuery | Compile a stored Indicator into a ready-to-execute Confit query |
| ReportIndicatorQueryError | Flag a runtime query failure observed by a client so the indicator-fixer cron can repair it |
IndicatorRelationsService
| Method | Description |
|---|---|
| CreateIndicatorRelation | Attach an Indicator to a resource |
| ListIndicatorRelations | Enumerate relations matching a filter |
| DeleteIndicatorRelation | Detach (soft-delete) a relation |
CreateIndicator
Define a new Indicator together with its first IndicatorRelation. Both writes plus an initial "Indicator created." activity land in one transaction.
POST /firetiger.goals.v1.IndicatorsService/CreateIndicator
REST alternative:
POST /v1/indicators
| Field | Type | Required | Description |
|---|---|---|---|
indicator_id |
string | No | Caller-chosen kebab-case slug; if empty, the server generates one |
indicator |
Indicator | Yes | The Indicator definition (see below) |
initial_resource |
string | Yes | Resource the Indicator is initially attached to. Must match agents/{agent}, issues/{issue}, investigations/{investigation}, or monitoring-plans/{plan} |
The indicator object accepts:
| Field | Type | Required | Description |
|---|---|---|---|
display_name |
string | Yes | Human-readable label |
description |
string | No | Markdown rendered as “What this measures” in the UI |
kind |
enum | Yes | INDICATOR_KIND_RATIO or INDICATOR_KIND_GAUGE |
query.connections |
string[] | Yes | Connection resource names the query reads from |
query.confit_sql |
string | Yes | ConfitSQL template; must reference @start_time, @end_time, and @resolution |
query.description |
string | No | Markdown rendered as “How this is grounded” |
unit |
string | No | Display unit (%, ms, {jobs}, etc.) |
GetIndicator
Retrieve a single Indicator including its query status (validation timestamp + last error).
POST /firetiger.goals.v1.IndicatorsService/GetIndicator
REST alternative:
GET /v1/{name=indicators/*}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Indicator resource name (e.g. indicators/checkout-latency-p95) |
BatchGetIndicators
Retrieve several Indicators in a single request. Used after listing IndicatorRelations to hydrate Indicator details for each related resource.
POST /firetiger.goals.v1.IndicatorsService/BatchGetIndicators
REST alternative:
POST /v1/indicators:batchGet
| Field | Type | Required | Description |
|---|---|---|---|
names |
string[] | Yes | Indicator resource names |
Returns Indicators in the same order as names. A missing Indicator fails the entire batch with NOT_FOUND.
ListIndicators
Enumerate Indicators in the shared catalog. Supports AIP-160 filtering, AIP-158 pagination, and show_deleted to surface archived Indicators.
POST /firetiger.goals.v1.IndicatorsService/ListIndicators
REST alternative:
GET /v1/indicators
| Field | Type | Required | Description |
|---|---|---|---|
filter |
string | No | AIP-160 filter (e.g. kind = "INDICATOR_KIND_RATIO") |
order_by |
string | No | Sort order |
page_size |
int | No | Maximum number of Indicators per page |
page_token |
string | No | Pagination token from a previous response |
show_deleted |
bool | No | Include soft-deleted Indicators |
UpdateIndicator
Edit the live Indicator definition. There are no Indicator revisions in v1 — the definition mutates in place and an IndicatorActivity entry lands in the same transaction. Pass activity_description for human-authored maintenance notes; otherwise the service auto-generates a generic entry.
POST /firetiger.goals.v1.IndicatorsService/UpdateIndicator
REST alternative:
PATCH /v1/{indicator.name=indicators/*}
| Field | Type | Required | Description |
|---|---|---|---|
indicator |
Indicator | Yes | New field values; only fields named in update_mask are written |
update_mask |
FieldMask | Yes | List of paths to update (e.g. display_name,description) |
activity_description |
string | No | Maintenance-log entry to write atomically with the update |
Server-managed paths (name, etag, create_time, update_time, delete_time, query.status*) are silently filtered from the mask.
DeleteIndicator
Soft-delete the Indicator (sets delete_time) and cascade-soft-delete its IndicatorRelations using a shared delete_time. The shared timestamp lets UndeleteIndicator selectively restore only the relations the cascade touched — relations the user previously detached on their own carry a different delete_time and stay archived. A "Indicator archived." activity is written.
POST /firetiger.goals.v1.IndicatorsService/DeleteIndicator
REST alternative:
DELETE /v1/{name=indicators/*}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Indicator resource name |
UndeleteIndicator
Restore a previously soft-deleted Indicator. Clears delete_time on the Indicator and cascade-restores any IndicatorRelations whose delete_time matches the Indicator’s (i.e. the ones DeleteIndicator archived together with it). Relations the user detached separately stay archived. An "Indicator restored." activity is written.
POST /firetiger.goals.v1.IndicatorsService/UndeleteIndicator
REST alternative:
POST /v1/{name=indicators/*}:undelete
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Indicator resource name |
The response carries the restored Indicator.
PurgeIndicators
Hard-delete soft-deleted Indicators matching an AIP-160 filter, along with their cascade-deleted IndicatorRelations and IndicatorActivities. Irreversible — typically driven by a retention cron rather than interactive use. Pass force=false for a dry-run that returns the count and a sample of names that would be purged without actually deleting anything.
POST /firetiger.goals.v1.IndicatorsService/PurgeIndicators
REST alternative:
POST /v1/indicators:purge
| Field | Type | Required | Description |
|---|---|---|---|
filter |
string | No | AIP-160 filter against soft-deleted Indicators |
force |
bool | No | When false (default), returns the dry-run count + sample without deleting |
The response carries purge_count (number of Indicators that were/would be purged) and purge_sample (sample of Indicator names from that set).
ListIndicatorActivities
Read the maintenance log for an Indicator — creation, edits, archive, and (in future) re-grounding and dispute events. Activities are written by the service itself; there is no public CreateIndicatorActivity.
POST /firetiger.goals.v1.IndicatorsService/ListIndicatorActivities
REST alternative:
GET /v1/{parent=indicators/*}/activities
| Field | Type | Required | Description |
|---|---|---|---|
parent |
string | Yes | Indicator resource name |
filter |
string | No | AIP-160 filter |
order_by |
string | No | Sort order. Default: create_time desc |
page_size |
int | No | Maximum number of activities per page |
page_token |
string | No | Pagination token |
show_deleted |
bool | No | Include purged activities |
CompileIndicatorQuery
Compile the stored Indicator into a ready-to-execute Confit QueryRequest. The service substitutes @start_time / @end_time / @resolution with typed SQL literals from the request and resolves query.connections into ConnectionConfig values. The client sends response.query_request directly to firetiger.query.v2.ConfitService.Query so result data does not proxy through the goals API server.
POST /firetiger.goals.v1.IndicatorsService/CompileIndicatorQuery
REST alternative:
POST /v1/{name=indicators/*}:compileQuery
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Indicator resource name |
start_time |
timestamp | Yes | Inclusive query-window start (substituted for @start_time) |
end_time |
timestamp | Yes | Exclusive query-window end (substituted for @end_time) |
resolution |
duration | Yes | Bucket size (substituted for @resolution) |
ReportIndicatorQueryError
Flag that an Indicator’s query failed at runtime. The server persists the (truncated) error to query.status.error and bumps query.status.validate_time, so the indicator-fixer cron picks the Indicator up on its next pass and either auto-heals it (if a smoke-test against real data succeeds) or queues it for the indicator-curator agent to repair, replace, or delete. Idempotent: re-reporting the same error string still bumps validate_time but skips appending a new IndicatorActivity so a chart that errors on every page-load doesn’t spam the maintenance log.
Used by UI surfaces that execute compiled Indicator queries (e.g. the indicator detail chart, issue indicator cards) to surface live failures to the maintenance loop.
POST /firetiger.goals.v1.IndicatorsService/ReportIndicatorQueryError
REST alternative:
POST /v1/{name=indicators/*}:reportQueryError
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Indicator resource name |
error |
string | Yes | Runtime error observed by the client (e.g. DuckDB binder text, Confit error body). Truncated server-side; only a head slice is persisted to query.status.error. Empty values are rejected — clearing the status is the indicator-fixer cron’s responsibility, not a public client surface. |
CreateIndicatorRelation
Attach an Indicator to a resource (Agent, Issue, Investigation, or Monitoring Plan). Idempotent against soft-deleted relations: re-creating one that was previously detached is permitted.
POST /firetiger.goals.v1.IndicatorRelationsService/CreateIndicatorRelation
REST alternative:
POST /v1/indicatorRelations
| Field | Type | Required | Description |
|---|---|---|---|
indicator_relation.indicator |
string | Yes | Indicator resource name |
indicator_relation.resource |
string | Yes | Target resource name (agents/..., issues/..., investigations/..., or monitoring-plans/...) |
ListIndicatorRelations
Enumerate relations matching a filter. Typical reads filter by a single resource (Issue / Agent / Investigation / Monitoring Plan page) or a single indicator (Indicator dependents tab).
POST /firetiger.goals.v1.IndicatorRelationsService/ListIndicatorRelations
REST alternative:
GET /v1/indicatorRelations
| Field | Type | Required | Description |
|---|---|---|---|
filter |
string | No | AIP-160 filter (e.g. resource = "issues/FT-241" or indicator = "indicators/checkout-latency-p95") |
order_by |
string | No | Sort order |
page_size |
int | No | Maximum number of relations per page |
page_token |
string | No | Pagination token |
show_deleted |
bool | No | Include detached relations |
DeleteIndicatorRelation
Detach an Indicator from a resource (soft-delete). The Indicator itself is unaffected.
POST /firetiger.goals.v1.IndicatorRelationsService/DeleteIndicatorRelation
REST alternative:
DELETE /v1/{name=indicatorRelations/*}
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | IndicatorRelation resource name (e.g. indicatorRelations/rel-abc) |