Alert rules will soon have a single schema, v2alpha1. The v1 rule schema, the one with evalWindow, frequency, preferredChannels, and a flat condition.target, op, and matchType, will no longer be accepted on any write. Rules already stored in v1 are rewritten to v2alpha1 automatically when SigNoz starts after the upgrade, so nothing changes for anyone who manages alerts from the UI.
The /api/v1/rules endpoints and the deprecated GET /api/v2/rules list will be removed in the same release. Anything that talks to the rules API has to use the replacement endpoints and send the new shape. The complete specification, including request and response schemas for every endpoint, is available in the SigNoz API Reference.
Who needs to act
- Scripts and integrations that call
/api/v1/rules,/api/v1/testRule, orGET /api/v2/rules. Those endpoints are going away, so the calls move to the replacements below. Scripts already on the v2 endpoints are affected by the schema too. After the upgrade, SigNoz rejects a create (POST), replace (PUT), or test body without"schemaVersion": "v2alpha1". APATCHmerges into the stored rule and does not need it. - Terraform users on the
signoz_ruleresource, providerv0.1.0and later, need nothing. It already uses the v2 endpoints and thev2alpha1schema. Configurations still on the oldersignoz_alertresource have to move tosignoz_rule. See Terraform Provider. - Anything that reads rules and expects the v1 fields back. A rule fetched by id after the upgrade comes back in the
v2alpha1shape, migrated or new, and the list returns a summary instead of full rules. - UI-only users need to do nothing.
Endpoints
The v1 endpoints and the v2 list will be removed. Each has a replacement. The full request and response schemas are in the SigNoz API Reference.
| Removed | Replacement |
|---|---|
GET /api/v1/rules | GET /api/v3/rules |
GET /api/v2/rules | GET /api/v3/rules |
GET /api/v1/rules/{id} | GET /api/v2/rules/{id} |
POST /api/v1/rules | POST /api/v2/rules |
PUT /api/v1/rules/{id} | PUT /api/v2/rules/{id} |
PATCH /api/v1/rules/{id} | PATCH /api/v2/rules/{id} |
DELETE /api/v1/rules/{id} | DELETE /api/v2/rules/{id} |
POST /api/v1/testRule | POST /api/v2/rules/test |
GET /api/v3/rules is paginated, with limit up to 200 and offset, and returns a summary per rule: id, state, alert, description, alertType, ruleType, disabled, labels, and the audit fields, under data.rules with data.total. It does not carry the rule definition. Fetch GET /api/v2/rules/{id} for that. The v1 and v2 lists return a bare array of full rules.
What changed in the schema
The v1 schema described a rule with one threshold spread across top-level fields. v2alpha1 groups the same information into three objects, all required:
condition.thresholdsholds one or more named thresholds, each with its own target, comparison, and channels.evaluationholds the window and frequency, and can also describe a cumulative schedule, which v1 could not express.notificationSettingsholds re-notification, grouping, and policy routing, which v1 applied implicitly.
Field mapping
| v1 | v2alpha1 | Notes |
|---|---|---|
schemaVersion (absent or empty) | schemaVersion: "v2alpha1" | Required. A missing or any other value is rejected. |
evalWindow | evaluation.spec.evalWindow | Required, no default. v1 filled 5m when omitted; send it explicitly. |
frequency | evaluation.spec.frequency | Required, no default. v1 filled 1m when omitted; send it explicitly. |
| None (new) | evaluation.kind | rolling is the v1 behavior. cumulative anchors the window to an hourly, daily, weekly, or monthly schedule in a timezone. |
condition.target | condition.thresholds.spec[0].target | Required per threshold. For anomaly rules the sign changes, see below. |
condition.op | condition.thresholds.spec[0].op | Same values. Prefer the names (above, below, and so on) over the numeric codes. |
condition.matchType | condition.thresholds.spec[0].matchType | Same values. Prefer the names (at_least_once, all_the_times, and so on). |
condition.targetUnit | condition.thresholds.spec[0].targetUnit | Unchanged meaning. |
preferredChannels | condition.thresholds.spec[0].channels | Channel names, per threshold. SigNoz rejects an empty list unless usePolicy is true, see below. |
labels.severity | condition.thresholds.spec[0].name | Alerts carry the threshold name as severity, and routing matches on it. Must be non-empty. A static labels.severity overrides the threshold name, so remove it from rules with more than one threshold. |
| None (new) | condition.thresholds.kind | Always basic. |
| None (new) | condition.thresholds.spec[0].recoveryTarget | Optional. A separate value the series must cross back over before the alert resolves. null or omitted keeps the v1 behavior. |
condition.alertOnAbsent | condition.alertOnAbsent and notificationSettings.renotify.alertStates | The field stays. To keep re-notifying on no data, add nodata to alertStates; v1 did this implicitly. |
| None (new) | notificationSettings | Required. renotify and usePolicy are described below. |
version, source | version, source | Still accepted and echoed, marked deprecated. Nothing reads or validates them. |
Everything else is unchanged: alert, alertType, ruleType, description, disabled, labels, annotations, condition.compositeQuery, condition.selectedQueryName, condition.absentFor, condition.requireMinPoints, condition.requiredNumPoints, condition.algorithm, and condition.seasonality.
Operators and match types
v2alpha1 accepts both the codes and the names. Send codes as JSON strings, such as "1". Use the names in new code.
op code | op name | matchType code | matchType name |
|---|---|---|---|
"1" | above | "1" | at_least_once |
"2" | below | "2" | all_the_times |
"3" | equal | "3" | on_average |
"4" | not_equal | "4" | in_total |
"5" | above_or_equal | "5" | last |
"6" | below_or_equal | ||
"7" | outside_bounds |
Behavior to carry over explicitly
v1 derived several values on the server. v2alpha1 stores them, so a script has to write them out.
- Re-notification. Every v1 rule re-notified every 4 hours while firing. To keep that, send
"renotify": {"enabled": true, "interval": "4h", "alertStates": ["firing"]}. Include"nodata"inalertStateswhenalertOnAbsentis true, otherwise a no-data alert notifies once and goes quiet. Omittingrenotify, or sending"enabled": false, means notify once per alert. - Channels. In v1 an empty
preferredChannelsmeant every channel in the organization. Inv2alpha1, SigNoz rejects an emptychannelslist withat least one channel is required. List the channels on each threshold, or set"usePolicy": trueto route through the organization's routing policies instead and leavechannelsempty. A migrated rule that had no channels comes back with an emptychannelslist. Add channels or setusePolicyon that rule. - Anomaly targets. The threshold is compared against the signed anomaly score. A v1 anomaly rule with
"op": "2"(below) and"target": 3fired on a score under -3. Write it as"op": "below", "target": -3. No other rule type or operator changes the number.
Example
The same rule in both schemas. The compositeQuery body is unchanged and omitted here.
v1
{
"alert": "High CPU",
"alertType": "METRIC_BASED_ALERT",
"ruleType": "threshold_rule",
"evalWindow": "10m",
"frequency": "2m",
"labels": { "severity": "warning", "team": "infra" },
"annotations": { "summary": "CPU above 90% on {{$labels.host}}" },
"preferredChannels": ["slack", "pd"],
"condition": {
"compositeQuery": { ... },
"target": 90,
"op": "1",
"matchType": "2",
"targetUnit": "percent",
"alertOnAbsent": true,
"absentFor": 7
}
}v2alpha1
{
"schemaVersion": "v2alpha1",
"alert": "High CPU",
"alertType": "METRIC_BASED_ALERT",
"ruleType": "threshold_rule",
"labels": { "severity": "warning", "team": "infra" },
"annotations": { "summary": "CPU above 90% on {{$labels.host}}" },
"condition": {
"compositeQuery": { ... },
"alertOnAbsent": true,
"absentFor": 7,
"thresholds": {
"kind": "basic",
"spec": [{
"name": "warning",
"target": 90,
"targetUnit": "percent",
"op": "above",
"matchType": "all_the_times",
"channels": ["slack", "pd"]
}]
}
},
"evaluation": {
"kind": "rolling",
"spec": { "evalWindow": "10m", "frequency": "2m" }
},
"notificationSettings": {
"renotify": { "enabled": true, "interval": "4h", "alertStates": ["firing", "nodata"] },
"usePolicy": false
}
}A second threshold is another entry in spec, for example a critical one at 95 with its own channels. Alerts carry the threshold name as severity, and each threshold routes to its own channels. When you add a second threshold, remove severity from labels. Otherwise every alert from the rule carries the static value.
What a v1 payload gets back after the upgrade
SigNoz rejects a create, replace, or test body without "schemaVersion": "v2alpha1" before it stores anything.
POST /api/v2/rulesand the other v2 writes return400witherror.messageofalert rule is not validand the reasons undererror.errors, for exampleschemaVersion: unsupported value ""; must be "v2alpha1". With the version set but the new objects missing, the reasons name them:condition.thresholds: field is required for schemaVersion "v2alpha1", and the same forevaluationandnotificationSettings.- Calls to
/api/v1/rulesand/api/v1/testRulereturn404.GET /api/v2/rulesreturns405, because that path still servesPOST. - A
PATCHthat sends v1 field names does not fail. Fields that no longer exist are ignored, so a patch ofcondition.targetchanges nothing. Patch thev2alpha1paths instead, such ascondition.thresholds.
What reads return after the upgrade
GET /api/v2/rules/{id} returns v2alpha1 for every rule, and the v3 list returns the summary described above. Rules created under v1 come back with schemaVersion, condition.thresholds, evaluation, and notificationSettings filled in from their v1 values as described above, and without evalWindow, frequency, preferredChannels, condition.target, condition.op, condition.matchType, or condition.targetUnit. A reader that looked for those fields finds them under their new paths.
Checklist for a script
- Point the script at the replacement endpoints from the table above. A script that listed rules through v1 or v2 pages through the v3 summary instead and fetches a rule by id when it needs the definition.
- Add
"schemaVersion": "v2alpha1"to every rule body. - Move
evalWindowandfrequencyunderevaluationwith"kind": "rolling", filling5mand1mwhere the script used to omit them. - Move
target,op,matchType, andtargetUnitinto one entry undercondition.thresholds.spec, named afterlabels.severity, with"kind": "basic". - Move
preferredChannelsinto that entry'schannels. If the list was empty, decide between naming the channels and"usePolicy": true. - Add
notificationSettingswith the re-notification the rule relied on:enabled,"interval": "4h", andalertStateswithnodatawhenalertOnAbsentis set. - For anomaly rules with
below, negatetarget. - Update readers to the new paths.
- Optional: send the rule to
POST /api/v2/rules/testbefore you write it. This endpoint sends a test notification to the channels on the rule, so point it at a test channel when you run it from a script.