---
title: "Generate SQL from alert query parameters"
method: POST
path: "/api/v2/{org_id}/alerts/generate_sql"
tags: ["Alerts"]
---

# Generate SQL from alert query parameters

`POST /api/v2/{org_id}/alerts/generate_sql`

## Request

- Base URL: `https://api.openobserve.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.openobserve.ai/api/v2/{org_id}/alerts/generate_sql`
- Auth: API key in header `Authorization`
- TypeScript: <https://skmtc.dev/openobserve/apis/openobserve-api-http/docs/api/v2/:org_id/alerts/generate_sql/post.ts> (a function with typed inputs; its response is checked against the schema)

Generates a SQL query string based on alert query parameters including stream, aggregations, and conditions. This endpoint is useful for testing alert queries and understanding the SQL that will be executed.

## Path parameters

- `org_id` string, required

## Request body

- object: HTTP request body for `GenerateSql` endpoint.
  - `query_condition` QueryCondition, required
    - `aggregation` Aggregation
      - `function` 'avg' | 'min' | 'max' | 'sum' | 'count' | 'median' | 'p50' | 'p75' | 'p90' | 'p95' | 'p99', required
      - `group_by` string[], nullable
      - `having` Condition, required
        - `column` string, required
        - `ignore_case` boolean
        - `operator` '=' | '!=' | '>' | '>=' | '<' | '<=' | 'contains' | 'not_contains' | 'is_null' | 'is_not_null' | 'is_empty' | 'is_not_empty', required
        - `value` object, required
      - `multi_alert` boolean: Opt-in to per-group evaluation — multi-alerts, `alerts_2.md` M-9/D26. `#[serde(default)]` is the whole backward-compatibility guarantee: an aggregation stored before this field existed cannot contain it, so it deserializes to `false` and the alert keeps its legacy collapsed evaluation byte-for-byte. Nothing is inferred from `group_by` being present — that would silently change paging cadence and reset silence fingerprints for every existing grouped alert. Validated by [`grouping::validate_multi_alert`] (M-10): requires a non-empty `group_by`, an orderable `having.operator`, and "any group" count gates.
      - `warning_value` number, double, nullable: WARNING threshold, sharing `having.operator` and `having.column` with critical (alerts_2.md §4.4). `None` = single-level aggregation alert, i.e. exactly the legacy behaviour. Stored as f64 because aggregate values (averages, percentiles) are not integers. Same lenient deserialization as `promql_warning_value`, for the same reason (D61) — aggregate warnings are the field most likely to be fractional.
    - `conditions` object, nullable
    - `multi_time_range` CompareHistoricData[], nullable
      - `offSet` string, required
    - `promql` string, nullable
    - `promql_condition` Condition
      - `column` string, required
      - `ignore_case` boolean
      - `operator` '=' | '!=' | '>' | '>=' | '<' | '<=' | 'contains' | 'not_contains' | 'is_null' | 'is_not_null' | 'is_empty' | 'is_not_empty', required
      - `value` object, required
    - `promql_multi_alert` boolean: Per-group alerting for a PromQL alert (M-9), where a "group" is one returned SERIES. A sibling of `promql_condition` rather than a member of [`Aggregation::multi_alert`], because a PromQL alert has no `aggregation` at all — its grouping is expressed in the PromQL itself (`sum by (pod) (…)`), not in a `group_by` column list. The group key is the series' FULL label set. That is what a series' identity already is in Prometheus, and it means the expression stays the single place grouping is decided — a separate label picker could only ever disagree with the `by (…)` clause beside it. `#[serde(default)]` carries the same backward-compatibility guarantee as its aggregation counterpart: an alert stored before this field existed deserializes to `false` and keeps its collapsed evaluation byte-for-byte. Nothing is inferred from the query returning several series — that would change paging cadence for every existing PromQL alert that happens to be unaggregated.
    - `promql_rule_mode` boolean
    - `promql_warning_value` number, double, nullable: WARNING value for the PromQL condition (alerts_2.md Feature 1). A sibling field rather than a member of `Condition`, which is shared by every filter in the product and must not grow alert-specific knobs. Shares `promql_condition.operator` with critical. `None` = single-level. Lenient deserialization: this arrives through `CreateAlertRequestBody`'s `#[serde(flatten)]`, which buffers via `Value`, where `arbitrary_precision` makes a number a map (D61). Without it a FRACTIONAL warning is rejected while an integer one works — found by end-to-end testing.
    - `search_event_type` 'ui' | 'dashboards' | 'reports' | 'alerts' | 'values' | 'other' | 'rum' | 'derivedstream' | 'searchjob' | 'download' | 'insights' | 'public_dashboard'
    - `slo_condition` SloCondition: The SLO alert's condition, persisted in `alerts.query_slo_condition` (D42). `slo_id` is additionally written to the indexed `alerts.slo_id` column, which is authoritative for reverse lookups (D60).
      - `critical` number, double, required: Finite and strictly positive (SA-3). Lenient deserialization is required, not cosmetic: this struct arrives nested inside `CreateAlertRequestBody`'s `#[serde(flatten)]`, and a flatten buffers through `Value`, where `arbitrary_precision` represents every number as a map (D61). A plain `f64` fails there with `invalid type: map, expected f64`.
      - `kind` 'error_budget' | 'burn_rate', required: The two SLO-alert shapes we offer.
      - `long_window_secs` integer, nullable: `BurnRate` only. 1h–48h, ≤ the SLO window, an exact multiple of and ≥ 2× the slice interval (SA-8).
      - `multi_alert` boolean: Per-group fan-out — Feature 3's flag, same semantics (SA-13).
      - `operator` '=' | '!=' | '>' | '>=' | '<' | '<=' | 'contains' | 'not_contains' | 'is_null' | 'is_not_null' | 'is_empty' | 'is_not_empty', required
      - `short_window_secs` integer, nullable: `BurnRate` only. Defaults to long ÷ 12 (Google SRE workbook).
      - `slo_id` string, required
      - `warning` number, double, nullable
    - `sql` string, nullable
    - `type` 'custom' | 'sql' | 'promql' | 'slo'
    - `vrl_function` string, nullable
  - `stream_name` string, required: Stream name to query
  - `stream_type` 'logs' | 'metrics' | 'traces' | 'profiles' | 'service_graph' | 'enrichment_tables' | 'file_list' | 'metadata' | 'index', required

## Response `200`

Success

- object: HTTP response body for `GenerateSql` endpoint.
  - `metadata` GenerateSqlMetadata: Metadata about the generated SQL query.
    - `has_aggregation` boolean, required: Whether aggregation is present
    - `has_conditions` boolean, required: Whether WHERE clause is present
    - `has_group_by` boolean, required: Whether GROUP BY is present
  - `sql` string, required: The generated SQL query string

## Other responses

- `400`: Bad request - invalid parameters
- `500`: Internal server error

---

[API](https://skmtc.dev/openobserve/apis/openobserve-api-http.md) - [All operations](https://skmtc.dev/openobserve/apis/openobserve-api-http/llms.txt) - [OpenAPI document](https://skmtc.dev/openobserve/apis/openobserve-api-http/revisions/0529b8a10252?raw)
