curl --request POST \
--url https://telemetry.{region}.basaltic.sh/v1/alerts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"topic": "<string>",
"expression": {
"metric": "<string>",
"scale": 500000000000,
"match": [
"<string>"
],
"by": [
"<string>"
],
"unit": "<string>"
},
"threshold": 123,
"window_seconds": 1830,
"for_seconds": 43200,
"enabled": true,
"silenced_until": "2023-11-07T05:31:56Z"
}
'import requests
url = "https://telemetry.{region}.basaltic.sh/v1/alerts"
payload = {
"name": "<string>",
"topic": "<string>",
"expression": {
"metric": "<string>",
"scale": 500000000000,
"match": ["<string>"],
"by": ["<string>"],
"unit": "<string>"
},
"threshold": 123,
"window_seconds": 1830,
"for_seconds": 43200,
"enabled": True,
"silenced_until": "2023-11-07T05:31:56Z"
}
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({
name: '<string>',
topic: '<string>',
expression: {
metric: '<string>',
scale: 500000000000,
match: ['<string>'],
by: ['<string>'],
unit: '<string>'
},
threshold: 123,
window_seconds: 1830,
for_seconds: 43200,
enabled: true,
silenced_until: '2023-11-07T05:31:56Z'
})
};
fetch('https://telemetry.{region}.basaltic.sh/v1/alerts', 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://telemetry.{region}.basaltic.sh/v1/alerts",
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([
'name' => '<string>',
'topic' => '<string>',
'expression' => [
'metric' => '<string>',
'scale' => 500000000000,
'match' => [
'<string>'
],
'by' => [
'<string>'
],
'unit' => '<string>'
],
'threshold' => 123,
'window_seconds' => 1830,
'for_seconds' => 43200,
'enabled' => true,
'silenced_until' => '2023-11-07T05:31:56Z'
]),
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://telemetry.{region}.basaltic.sh/v1/alerts"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"topic\": \"<string>\",\n \"expression\": {\n \"metric\": \"<string>\",\n \"scale\": 500000000000,\n \"match\": [\n \"<string>\"\n ],\n \"by\": [\n \"<string>\"\n ],\n \"unit\": \"<string>\"\n },\n \"threshold\": 123,\n \"window_seconds\": 1830,\n \"for_seconds\": 43200,\n \"enabled\": true,\n \"silenced_until\": \"2023-11-07T05:31:56Z\"\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://telemetry.{region}.basaltic.sh/v1/alerts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"topic\": \"<string>\",\n \"expression\": {\n \"metric\": \"<string>\",\n \"scale\": 500000000000,\n \"match\": [\n \"<string>\"\n ],\n \"by\": [\n \"<string>\"\n ],\n \"unit\": \"<string>\"\n },\n \"threshold\": 123,\n \"window_seconds\": 1830,\n \"for_seconds\": 43200,\n \"enabled\": true,\n \"silenced_until\": \"2023-11-07T05:31:56Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://telemetry.{region}.basaltic.sh/v1/alerts")
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 \"name\": \"<string>\",\n \"topic\": \"<string>\",\n \"expression\": {\n \"metric\": \"<string>\",\n \"scale\": 500000000000,\n \"match\": [\n \"<string>\"\n ],\n \"by\": [\n \"<string>\"\n ],\n \"unit\": \"<string>\"\n },\n \"threshold\": 123,\n \"window_seconds\": 1830,\n \"for_seconds\": 43200,\n \"enabled\": true,\n \"silenced_until\": \"2023-11-07T05:31:56Z\"\n}"
response = http.request(request)
puts response.read_body{
"alert": {
"expression": {
"metric": "<string>",
"agg": "avg",
"scale": 500000000000,
"match": [
"<string>"
],
"by": [
"<string>"
],
"denominator": {
"metric": "<string>",
"agg": "avg",
"match": [
"<string>"
],
"by": [
"<string>"
]
},
"unit": "<string>"
},
"operator": "gt",
"threshold": 123,
"window_seconds": 1830,
"for_seconds": 43200,
"enabled": true,
"silenced_until": "2023-11-07T05:31:56Z",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"crn": "crn:telemetry:sa-saopaulo-1:my-account:alert/high-cpu",
"name": "<string>",
"topic": {
"id": "288aa1e9-bbf5-5584-bce9-6a688f06d8a1",
"crn": "crn:catalog::platform:country/288aa1e9-bbf5-5584-bce9-6a688f06d8a1",
"name": "Brazil"
},
"state": "ok",
"last_value": 123,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"evaluated_at": "2023-11-07T05:31:56Z",
"state_changed_at": "2023-11-07T05:31:56Z",
"pending_since": "2023-11-07T05:31:56Z",
"evaluation_error": "<string>",
"delivery_error": "<string>"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "CONFLICT",
"message": "Resource with this name already exists",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}Create a metric alert
Creates a durable metric read and notification publish delegation. Requires telemetry:CreateAlert on the named alert, telemetry:ReadMetrics for the account, and notifications:Publish on the selected topic. Firing and resolved transitions use the topic’s confirmed email, webhook and queue subscriptions. Events are stored atomically with state changes and retried with stable message IDs. Missing data and evaluation errors are visible states, not recovery notifications.
curl --request POST \
--url https://telemetry.{region}.basaltic.sh/v1/alerts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"topic": "<string>",
"expression": {
"metric": "<string>",
"scale": 500000000000,
"match": [
"<string>"
],
"by": [
"<string>"
],
"unit": "<string>"
},
"threshold": 123,
"window_seconds": 1830,
"for_seconds": 43200,
"enabled": true,
"silenced_until": "2023-11-07T05:31:56Z"
}
'import requests
url = "https://telemetry.{region}.basaltic.sh/v1/alerts"
payload = {
"name": "<string>",
"topic": "<string>",
"expression": {
"metric": "<string>",
"scale": 500000000000,
"match": ["<string>"],
"by": ["<string>"],
"unit": "<string>"
},
"threshold": 123,
"window_seconds": 1830,
"for_seconds": 43200,
"enabled": True,
"silenced_until": "2023-11-07T05:31:56Z"
}
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({
name: '<string>',
topic: '<string>',
expression: {
metric: '<string>',
scale: 500000000000,
match: ['<string>'],
by: ['<string>'],
unit: '<string>'
},
threshold: 123,
window_seconds: 1830,
for_seconds: 43200,
enabled: true,
silenced_until: '2023-11-07T05:31:56Z'
})
};
fetch('https://telemetry.{region}.basaltic.sh/v1/alerts', 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://telemetry.{region}.basaltic.sh/v1/alerts",
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([
'name' => '<string>',
'topic' => '<string>',
'expression' => [
'metric' => '<string>',
'scale' => 500000000000,
'match' => [
'<string>'
],
'by' => [
'<string>'
],
'unit' => '<string>'
],
'threshold' => 123,
'window_seconds' => 1830,
'for_seconds' => 43200,
'enabled' => true,
'silenced_until' => '2023-11-07T05:31:56Z'
]),
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://telemetry.{region}.basaltic.sh/v1/alerts"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"topic\": \"<string>\",\n \"expression\": {\n \"metric\": \"<string>\",\n \"scale\": 500000000000,\n \"match\": [\n \"<string>\"\n ],\n \"by\": [\n \"<string>\"\n ],\n \"unit\": \"<string>\"\n },\n \"threshold\": 123,\n \"window_seconds\": 1830,\n \"for_seconds\": 43200,\n \"enabled\": true,\n \"silenced_until\": \"2023-11-07T05:31:56Z\"\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://telemetry.{region}.basaltic.sh/v1/alerts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"topic\": \"<string>\",\n \"expression\": {\n \"metric\": \"<string>\",\n \"scale\": 500000000000,\n \"match\": [\n \"<string>\"\n ],\n \"by\": [\n \"<string>\"\n ],\n \"unit\": \"<string>\"\n },\n \"threshold\": 123,\n \"window_seconds\": 1830,\n \"for_seconds\": 43200,\n \"enabled\": true,\n \"silenced_until\": \"2023-11-07T05:31:56Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://telemetry.{region}.basaltic.sh/v1/alerts")
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 \"name\": \"<string>\",\n \"topic\": \"<string>\",\n \"expression\": {\n \"metric\": \"<string>\",\n \"scale\": 500000000000,\n \"match\": [\n \"<string>\"\n ],\n \"by\": [\n \"<string>\"\n ],\n \"unit\": \"<string>\"\n },\n \"threshold\": 123,\n \"window_seconds\": 1830,\n \"for_seconds\": 43200,\n \"enabled\": true,\n \"silenced_until\": \"2023-11-07T05:31:56Z\"\n}"
response = http.request(request)
puts response.read_body{
"alert": {
"expression": {
"metric": "<string>",
"agg": "avg",
"scale": 500000000000,
"match": [
"<string>"
],
"by": [
"<string>"
],
"denominator": {
"metric": "<string>",
"agg": "avg",
"match": [
"<string>"
],
"by": [
"<string>"
]
},
"unit": "<string>"
},
"operator": "gt",
"threshold": 123,
"window_seconds": 1830,
"for_seconds": 43200,
"enabled": true,
"silenced_until": "2023-11-07T05:31:56Z",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"crn": "crn:telemetry:sa-saopaulo-1:my-account:alert/high-cpu",
"name": "<string>",
"topic": {
"id": "288aa1e9-bbf5-5584-bce9-6a688f06d8a1",
"crn": "crn:catalog::platform:country/288aa1e9-bbf5-5584-bce9-6a688f06d8a1",
"name": "Brazil"
},
"state": "ok",
"last_value": 123,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"evaluated_at": "2023-11-07T05:31:56Z",
"state_changed_at": "2023-11-07T05:31:56Z",
"pending_since": "2023-11-07T05:31:56Z",
"evaluation_error": "<string>",
"delivery_error": "<string>"
}
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "CONFLICT",
"message": "Resource with this name already exists",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}Authorizations
An OAuth 2.0 bearer token, sent as Authorization: Bearer <token>.
This is the recommended way to authenticate.
Get one by exchanging a service account's access key pair at
POST /v1/oauth/token with grant_type=client_credentials. It is the
standard client-credentials grant, so any OAuth-aware library will
obtain and refresh it for you.
curl -s -u "$KEY_ID:$SECRET" -d grant_type=client_credentials \
https://iam.basaltic.sh/v1/oauth/token
Tokens last an hour by default. The same access key pair is separately your AWS SigV4 credential for the S3-compatible object endpoint, which speaks nothing else.
Headers
Optional client-generated key that makes a create replay-safe. Retrying a request with the same key returns the original outcome verbatim instead of creating a duplicate resource. Reusing a key with a different request body is rejected (422); a request whose key is still being processed returns 409. Records are honored for 24 hours. Use a UUID or similarly unique token.
255Body
Immutable account-unique name. UUID names and the crn: prefix are reserved.
1 - 128^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$Required same-account, same-region notification topic reference: UUID, canonical CRN or exact name, classified by syntax. Multiple alerts may share a topic. Binding requires notifications:Publish and respects explicit topic-policy denies.
Show child attributes
Show child attributes
gt, gte, lt, lte Whole-minute window. Evaluated every minute against the last complete UTC-aligned window.
60 <= x <= 3600Must be a multiple of 60Continuous breach duration before firing; 0 fires immediately. Missing data, query failures and scheduling gaps reset a pending duration.
0 <= x <= 86400Must be a multiple of 60Mute notifications until this instant (at most 30 days ahead). Evaluation continues; null removes silence. An active alert notifies on the next evaluation after silence expires.
Response
Create a metric alert
Show child attributes
Show child attributes

