curl --request POST \
--url https://api.cloud.corbado.io/v1/observe/alertRules/simulate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"rule": {
"version": 1,
"window": "<string>",
"conditions": [
{
"threshold": 123
}
],
"groupBy": [
"<string>"
],
"maxInstances": 250,
"settings": {},
"for": "<string>",
"overrides": [
{
"match": {},
"conditions": [
{
"threshold": 123
}
],
"for": "<string>",
"settings": {}
}
]
},
"toMs": 123,
"focus": {}
}
'import requests
url = "https://api.cloud.corbado.io/v1/observe/alertRules/simulate"
payload = {
"rule": {
"version": 1,
"window": "<string>",
"conditions": [{ "threshold": 123 }],
"groupBy": ["<string>"],
"maxInstances": 250,
"settings": {},
"for": "<string>",
"overrides": [
{
"match": {},
"conditions": [{ "threshold": 123 }],
"for": "<string>",
"settings": {}
}
]
},
"toMs": 123,
"focus": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
rule: {
version: 1,
window: '<string>',
conditions: [{threshold: 123}],
groupBy: ['<string>'],
maxInstances: 250,
settings: {},
for: '<string>',
overrides: [{match: {}, conditions: [{threshold: 123}], for: '<string>', settings: {}}]
},
toMs: 123,
focus: {}
})
};
fetch('https://api.cloud.corbado.io/v1/observe/alertRules/simulate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.cloud.corbado.io/v1/observe/alertRules/simulate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'rule' => [
'version' => 1,
'window' => '<string>',
'conditions' => [
[
'threshold' => 123
]
],
'groupBy' => [
'<string>'
],
'maxInstances' => 250,
'settings' => [
],
'for' => '<string>',
'overrides' => [
[
'match' => [
],
'conditions' => [
[
'threshold' => 123
]
],
'for' => '<string>',
'settings' => [
]
]
]
],
'toMs' => 123,
'focus' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.cloud.corbado.io/v1/observe/alertRules/simulate"
payload := strings.NewReader("{\n \"rule\": {\n \"version\": 1,\n \"window\": \"<string>\",\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"groupBy\": [\n \"<string>\"\n ],\n \"maxInstances\": 250,\n \"settings\": {},\n \"for\": \"<string>\",\n \"overrides\": [\n {\n \"match\": {},\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"for\": \"<string>\",\n \"settings\": {}\n }\n ]\n },\n \"toMs\": 123,\n \"focus\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.cloud.corbado.io/v1/observe/alertRules/simulate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"rule\": {\n \"version\": 1,\n \"window\": \"<string>\",\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"groupBy\": [\n \"<string>\"\n ],\n \"maxInstances\": 250,\n \"settings\": {},\n \"for\": \"<string>\",\n \"overrides\": [\n {\n \"match\": {},\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"for\": \"<string>\",\n \"settings\": {}\n }\n ]\n },\n \"toMs\": 123,\n \"focus\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloud.corbado.io/v1/observe/alertRules/simulate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"rule\": {\n \"version\": 1,\n \"window\": \"<string>\",\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"groupBy\": [\n \"<string>\"\n ],\n \"maxInstances\": 250,\n \"settings\": {},\n \"for\": \"<string>\",\n \"overrides\": [\n {\n \"match\": {},\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"for\": \"<string>\",\n \"settings\": {}\n }\n ]\n },\n \"toMs\": 123,\n \"focus\": {}\n}"
response = http.request(request)
puts response.read_body{
"type": "started",
"chunksDone": 123,
"chunksTotal": 123,
"simulation": {
"cached": true,
"fromMs": 123,
"toMs": 123,
"stepMs": 123,
"dataDelayMs": 123,
"fired": 123,
"firedBySeverity": {
"info": 123,
"warning": 123,
"critical": 123
},
"maxGroups": 123,
"failedSteps": 123,
"groups": [
{
"labels": {},
"fired": 123,
"firedBySeverity": {
"info": 123,
"warning": 123,
"critical": 123
},
"firingSteps": 123,
"pendingSteps": 123,
"noDataSteps": 123,
"firedPerHour": [
123
],
"firedPerHourBySeverity": {
"info": [
123
],
"warning": [
123
],
"critical": [
123
]
},
"lowestValue": 123,
"lowestValueAtMs": 123
}
],
"error": {
"atMs": 123,
"reason": "<string>",
"message": "<string>"
},
"focus": {
"labels": {},
"values": [
123
],
"samples": [
123
],
"statuses": [
"<string>"
],
"noDataReasons": [
"<string>"
],
"transitions": [
{
"atMs": 123,
"toStatus": "normal",
"reason": "<string>",
"fromStatus": "normal",
"fromSeverity": "info",
"toSeverity": "info",
"value": 123
}
]
}
},
"message": "<string>"
}{
"error": {
"message": "Validation failed",
"details": [
{
"field": "projectID",
"message": "required"
}
]
}
}Simulate alert rule
Evaluates an alert rule config over the last seven days and reports how often it would have fired, per group and per hour. The rule does not need to be saved. Nothing is stored as an alert, and nobody is notified.
What is simulated: the rule exactly as sent, evaluated every stepMs (as often as live
evaluation runs) with the same evaluation live rules use: the value per group from the window
and baseline, the conditions, the pending period (for), recovery, the no data policy,
minExpected, evaluation hours and overrides. Each evaluation continues from the alerts the
previous one left, so an alert that fired stays firing until it recovers, as live.
Input data: the flows or subflows the rule counts, as they are stored now, in one-minute
buckets. Each evaluation reads the window ending dataDelayMs before it, a fixed delay per
rule type; delays from processing backlogs at that time are not known any more and are not
simulated. Flows classified late are included, so a simulation can see slightly more than the
live evaluation saw at the time.
Not simulated: notifications. The contact point, its integrations and its delivery hours are not taken into account; the result says when alerts would have fired, not who would have been notified when.
The first simulation reads the data the rule needs, which can take a minute; the response
streams its progress as application/x-ndjson and ends with a completed event carrying the
result, or a failed event. Send the toMs of that result with every later simulation of the
same rule: as long as the rule’s type, flow or subflow type and grouping stay the same, the
data is reused and the simulation answers in about a second, so thresholds, windows and
pending periods can be tuned freely.
Validation errors return a regular JSON error before streaming starts. Only flow_volume and
subflow_volume rules can be simulated.
Required API key permission: observe:alerts:read.
curl --request POST \
--url https://api.cloud.corbado.io/v1/observe/alertRules/simulate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"rule": {
"version": 1,
"window": "<string>",
"conditions": [
{
"threshold": 123
}
],
"groupBy": [
"<string>"
],
"maxInstances": 250,
"settings": {},
"for": "<string>",
"overrides": [
{
"match": {},
"conditions": [
{
"threshold": 123
}
],
"for": "<string>",
"settings": {}
}
]
},
"toMs": 123,
"focus": {}
}
'import requests
url = "https://api.cloud.corbado.io/v1/observe/alertRules/simulate"
payload = {
"rule": {
"version": 1,
"window": "<string>",
"conditions": [{ "threshold": 123 }],
"groupBy": ["<string>"],
"maxInstances": 250,
"settings": {},
"for": "<string>",
"overrides": [
{
"match": {},
"conditions": [{ "threshold": 123 }],
"for": "<string>",
"settings": {}
}
]
},
"toMs": 123,
"focus": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
rule: {
version: 1,
window: '<string>',
conditions: [{threshold: 123}],
groupBy: ['<string>'],
maxInstances: 250,
settings: {},
for: '<string>',
overrides: [{match: {}, conditions: [{threshold: 123}], for: '<string>', settings: {}}]
},
toMs: 123,
focus: {}
})
};
fetch('https://api.cloud.corbado.io/v1/observe/alertRules/simulate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.cloud.corbado.io/v1/observe/alertRules/simulate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'rule' => [
'version' => 1,
'window' => '<string>',
'conditions' => [
[
'threshold' => 123
]
],
'groupBy' => [
'<string>'
],
'maxInstances' => 250,
'settings' => [
],
'for' => '<string>',
'overrides' => [
[
'match' => [
],
'conditions' => [
[
'threshold' => 123
]
],
'for' => '<string>',
'settings' => [
]
]
]
],
'toMs' => 123,
'focus' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.cloud.corbado.io/v1/observe/alertRules/simulate"
payload := strings.NewReader("{\n \"rule\": {\n \"version\": 1,\n \"window\": \"<string>\",\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"groupBy\": [\n \"<string>\"\n ],\n \"maxInstances\": 250,\n \"settings\": {},\n \"for\": \"<string>\",\n \"overrides\": [\n {\n \"match\": {},\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"for\": \"<string>\",\n \"settings\": {}\n }\n ]\n },\n \"toMs\": 123,\n \"focus\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.cloud.corbado.io/v1/observe/alertRules/simulate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"rule\": {\n \"version\": 1,\n \"window\": \"<string>\",\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"groupBy\": [\n \"<string>\"\n ],\n \"maxInstances\": 250,\n \"settings\": {},\n \"for\": \"<string>\",\n \"overrides\": [\n {\n \"match\": {},\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"for\": \"<string>\",\n \"settings\": {}\n }\n ]\n },\n \"toMs\": 123,\n \"focus\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloud.corbado.io/v1/observe/alertRules/simulate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"rule\": {\n \"version\": 1,\n \"window\": \"<string>\",\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"groupBy\": [\n \"<string>\"\n ],\n \"maxInstances\": 250,\n \"settings\": {},\n \"for\": \"<string>\",\n \"overrides\": [\n {\n \"match\": {},\n \"conditions\": [\n {\n \"threshold\": 123\n }\n ],\n \"for\": \"<string>\",\n \"settings\": {}\n }\n ]\n },\n \"toMs\": 123,\n \"focus\": {}\n}"
response = http.request(request)
puts response.read_body{
"type": "started",
"chunksDone": 123,
"chunksTotal": 123,
"simulation": {
"cached": true,
"fromMs": 123,
"toMs": 123,
"stepMs": 123,
"dataDelayMs": 123,
"fired": 123,
"firedBySeverity": {
"info": 123,
"warning": 123,
"critical": 123
},
"maxGroups": 123,
"failedSteps": 123,
"groups": [
{
"labels": {},
"fired": 123,
"firedBySeverity": {
"info": 123,
"warning": 123,
"critical": 123
},
"firingSteps": 123,
"pendingSteps": 123,
"noDataSteps": 123,
"firedPerHour": [
123
],
"firedPerHourBySeverity": {
"info": [
123
],
"warning": [
123
],
"critical": [
123
]
},
"lowestValue": 123,
"lowestValueAtMs": 123
}
],
"error": {
"atMs": 123,
"reason": "<string>",
"message": "<string>"
},
"focus": {
"labels": {},
"values": [
123
],
"samples": [
123
],
"statuses": [
"<string>"
],
"noDataReasons": [
"<string>"
],
"transitions": [
{
"atMs": 123,
"toStatus": "normal",
"reason": "<string>",
"fromStatus": "normal",
"fromSeverity": "info",
"toSeverity": "info",
"value": 123
}
]
}
},
"message": "<string>"
}{
"error": {
"message": "Validation failed",
"details": [
{
"field": "projectID",
"message": "required"
}
]
}
}Authorizations
Use an Observe API key from the management console. The key selects the project and must grant the permission listed on the operation. Keep this key on your server.
Body
Type of the rule to simulate.
login_success_rate, flow_volume, subflow_volume The configuration of an alert rule: the generic envelope every rule type shares, plus one settings object belonging to the rule type named by type. Everything outside settings is implemented once, generically - the window is resolved and handed to the rule type as two absolute timestamps, and the conditions are applied to whatever number the rule type returned, so a rule type never sees the thresholds. Shape and vocabulary are validated here. The semantics that span fields - that the bands get stricter as the level does, that recovery sits on the lenient side of them, that the window is a whole number of days, and that groupBy names dimensions this project actually has - are validated when the rule is saved and again when it is evaluated, because only the second catches a project that changed after the rule was written.
Show child attributes
Show child attributes
End of the simulated range in milliseconds since epoch, from the toMs of an earlier
simulation of the same rule. Omit it to simulate the seven days up to now. It must have
ended within the last 24 hours.
Labels of one group, e.g. {"country": "DE"}, whose every step the result carries for a
detailed chart. An ungrouped rule's only group has no labels: send {}.
Show child attributes
Show child attributes
Response
Newline-delimited JSON progress stream; the final line carries the simulation result.
One line of a simulation's progress stream.
started comes first, progress after every chunk of data read (repeated while a chunk
takes long), and either completed with the result or failed last.
started, progress, completed, failed Chunks of data read so far (progress events).
Chunks of data to read in total (progress events).
Show child attributes
Show child attributes
Why the simulation failed (failed events).
Was this page helpful?