Configure alerts
Build alert rules that decide which Elastio events notify whom: pick event kinds and severities, choose delivery channels, pause rules during maintenance, and check which events matched and where they were delivered.
Before you begin
Section titled “Before you begin”- Use a token with the
integrationscategory:readto list alerts and activity,writeto create, update or delete them. See Automate personal access tokens. - This guide uses the v2 alert API (
/gateway/integration/v2/alerts). It isn’t enabled on every tenant yet; where it’s off, v2 operations respond404. - v2 works with labeled alerts, whose
matching_semanticsissystemorpolicy. Older alerts with nomatching_semantics(“unlabeled leftovers”) are only visible through the v1 operations (/gateway/integration/v1/alerts) until cutover.
Two kinds of alert
Section titled “Two kinds of alert”matching_semantics |
Matches by | Typical use |
|---|---|---|
system |
criteria: event kinds, severities, and optional asset/account/tag scope |
Operational events such as hunts, backups and resolutions |
policy |
Recovery-policy bindings in policy_ids; criteria must be omitted or no_events |
Hunt, anomaly, violation, data-classification, custom-hunt and Pursuit findings |
matching_semantics is set on create and can’t be changed by an update.
Discover event kinds and channels
Section titled “Discover event kinds and channels”Fetch the event catalog to see which event_kind values you can use in criteria.event_kinds, and the channel list to find webhook and Splunk IDs for channels.
curl "https://acme.app.elastio.com/gateway/integration/v2/alerts/events" \ -H "Authorization: Bearer $ELASTIO_TOKEN"
curl "https://acme.app.elastio.com/gateway/integration/v1/channels" \ -H "Authorization: Bearer $ELASTIO_TOKEN"import os, requests
BASE = "https://acme.app.elastio.com"HEADERS = {"Authorization": f"Bearer {os.environ['ELASTIO_TOKEN']}"}
events = requests.get(f"{BASE}/gateway/integration/v2/alerts/events", headers=HEADERS, timeout=30).json()for e in events: print(f"{e['category']:<12} {e['event_kind']:<50} {e['description']}")
channels = requests.get(f"{BASE}/gateway/integration/v1/channels", headers=HEADERS, timeout=30).json()webhook_ids = {w["name"]: w["id"] for w in channels["webhooks"]}$Base = "https://acme.app.elastio.com"$Headers = @{ Authorization = "Bearer $env:ELASTIO_TOKEN" }Invoke-RestMethod -Uri "$Base/gateway/integration/v2/alerts/events" -Headers $Headers | Format-Table category, event_kind, description(Invoke-RestMethod -Uri "$Base/gateway/integration/v1/channels" -Headers $Headers).webhooks | Format-Table name, id, used_by_count[ { "event_kind": "elastio:asset:threat:resolved", "description": "Threat resolved", "category": "security" }, { "event_kind": "elastio:asset:violation:resolved", "description": "Violation resolved", "category": "recovery" }, …]category is one of security, operational, identity or recovery. The channels response groups channels as webhooks and splunk, each entry with id, name, type, direction and delivery metadata such as last_triggered and used_by_count.
Create an alert
Section titled “Create an alert”This system alert emails the security team and calls a webhook whenever a finding or violation is resolved, for critical and high severities only.
criteria.event_kinds.kind_tagisno_events,all_eventsorexact_events. Withexact_events, list the kinds incriteria.event_kinds.event_kinds. Finding kinds (elastio:asset:threat:detected,…:anomaly:detected,…:violation:detected,…:data-classification:findings:detected,…:custom-hunt:findings:detected,…:pursuit:findings:detected) can’t be listed here; use apolicyalert for those.criteria.severitieslimits routing tocritical,high,medium,lowand/orinfo. Omit it to route every severity.channelscan combineemails,tenant_members(bycustomer_id),tenant_admins: true,webhooks,splunkandslack.mode.kind_tagisnotifyorsuppress;statusisenabledordisabled.
curl -X POST "https://acme.app.elastio.com/gateway/integration/v2/alerts" \ -H "Authorization: Bearer $ELASTIO_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Resolved findings - prod", "description": "Tell the SOC when findings are resolved", "matching_semantics": "system", "status": "enabled", "mode": { "kind_tag": "notify" }, "criteria": { "event_kinds": { "kind_tag": "exact_events", "event_kinds": ["elastio:asset:threat:resolved", "elastio:asset:violation:resolved"] }, "aws_account_id_regions": [{ "account_id": "123456789012", "region": "us-east-1" }], "severities": ["critical", "high"] }, "channels": { "emails": [{ "email": "soc@example.com" }], "webhooks": [{ "webhook_id": "660e8400-e29b-41d4-a716-446655440000" }] } }'body = { "name": "Resolved findings - prod", "description": "Tell the SOC when findings are resolved", "matching_semantics": "system", "status": "enabled", "mode": {"kind_tag": "notify"}, "criteria": { "event_kinds": { "kind_tag": "exact_events", "event_kinds": ["elastio:asset:threat:resolved", "elastio:asset:violation:resolved"], }, "aws_account_id_regions": [{"account_id": "123456789012", "region": "us-east-1"}], "severities": ["critical", "high"], }, "channels": { "emails": [{"email": "soc@example.com"}], "webhooks": [{"webhook_id": "660e8400-e29b-41d4-a716-446655440000"}], },}resp = requests.post(f"{BASE}/gateway/integration/v2/alerts", headers=HEADERS, json=body, timeout=30)if resp.status_code == 409: print("Conflict:", resp.json()) # e.g. duplicate_nameresp.raise_for_status()alert_id = resp.json()["id"]$Body = @{ name = "Resolved findings - prod" description = "Tell the SOC when findings are resolved" matching_semantics = "system" status = "enabled" mode = @{ kind_tag = "notify" } criteria = @{ event_kinds = @{ kind_tag = "exact_events" event_kinds = @("elastio:asset:threat:resolved", "elastio:asset:violation:resolved") } aws_account_id_regions = @(@{ account_id = "123456789012"; region = "us-east-1" }) severities = @("critical", "high") } channels = @{ emails = @(@{ email = "soc@example.com" }) webhooks = @(@{ webhook_id = "660e8400-e29b-41d4-a716-446655440000" }) }} | ConvertTo-Json -Depth 6$Alert = Invoke-RestMethod -Method Post -Uri "$Base/gateway/integration/v2/alerts" -Headers $Headers ` -ContentType "application/json" -Body $Body$Alert.idThe response is 201 with the stored alert:
{ "id": "4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d", "tenant_name": "acme", "name": "Resolved findings - prod", "description": "Tell the SOC when findings are resolved", "mode": { "kind_tag": "notify" }, "status": "enabled", "criteria": { "event_kinds": { "kind_tag": "exact_events", "event_kinds": ["elastio:asset:threat:resolved", "elastio:asset:violation:resolved"] }, "aws_account_id_regions": [{ "account_id": "123456789012", "region": "us-east-1" }], "severities": ["critical", "high"] }, "channels": { "emails": [{ "email": "soc@example.com" }], "webhooks": [{ "webhook_id": "660e8400-e29b-41d4-a716-446655440000" }] }, "policy_ids": [], "matching_semantics": "system", "created_at": "2026-10-12T09:45:00Z", "updated_at": "2026-10-12T09:45:00Z"}For findings notifications, create a policy alert instead: set "matching_semantics": "policy", omit criteria (or use no_events), and bind recovery policies with policy_ids. A policy alert without bindings doesn’t match events. Sending policy_ids on a system alert returns 400.
List alerts and suppress during maintenance
Section titled “List alerts and suppress during maintenance”List labeled alerts with cursor pagination (pageSize up to 100, default 50, and pageToken). scope selects policy (the default), system or all; severities keeps alerts that route any of the listed severities.
curl "https://acme.app.elastio.com/gateway/integration/v2/alerts?scope=all&pageSize=100" \ -H "Authorization: Bearer $ELASTIO_TOKEN"alerts, token = [], Nonewhile True: params = {"scope": "all", "pageSize": 100, **({"pageToken": token} if token else {})} page = requests.get(f"{BASE}/gateway/integration/v2/alerts", headers=HEADERS, params=params, timeout=30) page.raise_for_status() data = page.json() alerts += data["alerts"] token = data["pagination"].get("next_page_token") if not token: breakprint(f"{len(alerts)} alerts")(Invoke-RestMethod -Uri "$Base/gateway/integration/v2/alerts?scope=all&pageSize=100" -Headers $Headers).alerts | Format-Table name, matching_semantics, status, @{ n = "mode"; e = { $_.mode.kind_tag } }{ "alerts": [ { "id": "4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d", "name": "Resolved findings - prod", "mode": { "kind_tag": "notify" }, "status": "enabled", "matching_semantics": "system", … } ], "pagination": { "next_page_token": null, "prev_page_token": null, "total_count": 1 }}To silence an alert for a maintenance window without deleting it, update its mode to suppress with a reason and an until timestamp (both required for suppression). Fields you omit from the PUT body are left as they are.
curl -X PUT "https://acme.app.elastio.com/gateway/integration/v2/alerts/4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d" \ -H "Authorization: Bearer $ELASTIO_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "mode": { "kind_tag": "suppress", "reason": "Maintenance window", "until": "2026-10-13T06:00:00Z" } }'alert_id = "4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d"resp = requests.put( f"{BASE}/gateway/integration/v2/alerts/{alert_id}", headers=HEADERS, json={"mode": {"kind_tag": "suppress", "reason": "Maintenance window", "until": "2026-10-13T06:00:00Z"}}, timeout=30,)resp.raise_for_status()print(resp.json()["mode"])$AlertId = "4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d"$Body = @{ mode = @{ kind_tag = "suppress"; reason = "Maintenance window"; until = "2026-10-13T06:00:00Z" } } | ConvertTo-Json -Depth 3Invoke-RestMethod -Method Put -Uri "$Base/gateway/integration/v2/alerts/$AlertId" -Headers $Headers ` -ContentType "application/json" -Body $Body{ "id": "4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d", "name": "Resolved findings - prod", "mode": { "kind_tag": "suppress", "reason": "Maintenance window", "until": "2026-10-13T06:00:00Z" }, "status": "enabled", …}Review alert activity
Section titled “Review alert activity”See which events matched an alert and what happened on each channel. period is a rolling window between 1d and 30d (default 7d); filter by event_kind (values from GET …/activity/options). Pagination uses pageSize (default 20, max 100) and pageToken.
curl "https://acme.app.elastio.com/gateway/integration/v2/alerts/4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d/activity?period=7d" \ -H "Authorization: Bearer $ELASTIO_TOKEN"resp = requests.get(f"{BASE}/gateway/integration/v2/alerts/{alert_id}/activity", headers=HEADERS, params={"period": "7d", "pageSize": 100}, timeout=30)resp.raise_for_status()for row in resp.json()["activity"]: failed = [c for c in row["channels"] if c["status"] != "delivered"] print(row["event_time"], row["event_kind"], row.get("severity"), "problems:" if failed else "ok", [(c["kind"], c["status"]) for c in failed])$Activity = Invoke-RestMethod -Uri "$Base/gateway/integration/v2/alerts/$AlertId/activity?period=7d" -Headers $Headers$Activity.activity | Format-Table event_time, event_kind, severity, @{ n = "channels"; e = { ($_.channels | ForEach-Object { "$($_.kind):$($_.status)" }) -join ", " } }{ "activity": [ { "id": "8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f", "alert_id": "4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d", "event_id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a", "event_kind": "elastio:asset:threat:resolved", "event_description": "Threat resolved", "event_time": "2026-10-12T10:27:41Z", "severity": "high", "channels": [ { "kind": "webhook", "delivery_id": "1f2e3d4c-5b6a-4798-8a9b-0c1d2e3f4a5b", "channel_name": "SOAR intake", "status": "delivered", "created_at": "2026-10-12T10:27:43Z" } ] } ], "pagination": { "next_page_token": null, "prev_page_token": null, "total_count": 1 }}For webhook channels, follow the delivery_id into the webhook delivery history described in Send events to webhooks.
All endpoints
Section titled “All endpoints”Alerts
19 operations in the reference →- GET
/gateway/integration/v1/alertsList leftover unlabeled alerts (v1) - POST
/gateway/integration/v1/alertsCreate a new alert - GET
/gateway/integration/v1/alerts/{alert_id}Get leftover unlabeled alert by ID - PUT
/gateway/integration/v1/alerts/{alert_id}Update leftover unlabeled alert - DELETE
/gateway/integration/v1/alerts/{alert_id}Delete an alert - GET
/gateway/integration/v2/alertsList all alerts for a tenant (v2) - POST
/gateway/integration/v2/alertsCreate a policy-aware alert (v2) - GET
/gateway/integration/v2/alerts/{alert_id}Get alert by ID (v2) - PUT
/gateway/integration/v2/alerts/{alert_id}Update alert (v2) - DELETE
/gateway/integration/v2/alerts/{alert_id}Delete leftover unlabeled alert - GET
/gateway/integration/v2/alerts/{alert_id}/activityList alert activity (v2) - GET
/gateway/integration/v2/alerts/{alert_id}/activity/optionsFilter options for alert activity (v2) - GET
/gateway/integration/v1/alerts/{alert_id}/activityList alert activity (matched events and channel deliveries) - GET
/gateway/integration/v1/alerts/{alert_id}/activity/optionsFilter options for alert activity - GET
/gateway/integration/v1/channelsGet available notification channels - GET
/gateway/integration/v1/alerts/emailsGet available email addresses - GET
/gateway/integration/v1/alerts/eventsGet available event types - GET
/gateway/integration/v2/alerts/eventsGet available event types (v2) - GET
/gateway/integration/v1/alerts/accountsGet available accounts by platform