Alerts from your observability platform
If you already run Dynatrace, Datadog, Grafana, CloudWatch, Azure Monitor or Robusta, point its alerting at the platform; anything else uses the generic endpoint.
How-to · Updated 2026-10-24 · Describes release 0.1.164
Keep the monitoring you already have
You do not replace your observability platform. It keeps detecting problems; this platform receives what it raises, groups it into one piece of work and investigates it. Each platform posts to one webhook URL, and the source name in that URL picks the adapter that understands its payload. A tool with no adapter uses the generic endpoint.
The one URL and the token
POST https://<your-platform>/webhook/<source>/<workspace-slug>
Alert intake & tokens (in the product: /portal/tokens#webhook) shows a ready-made URL for every source in the workspace you have open. An owner, or a role with pillars.manage, mints the webhook token there. It is shown once and only its hash is kept, so store it in the sending platform at once. A new token stops the old one for every sender, so update them together.
Send the token in a header when the platform lets you set one, because a header never lands in access logs: Authorization: Bearer <webhook-token>, or X-Webhook-Token: <webhook-token>. Where the platform cannot set a header (Amazon SNS and Azure Monitor action groups), add the token to the end of the URL: /webhook/<source>/<workspace-slug>/<webhook-token>. When both are present the header wins. A wrong workspace, a disabled workspace and a wrong token all answer the same 401 invalid webhook credentials, so the endpoint never says which one was wrong.
What to set up on each platform
One row per platform. Native payload means the platform's own webhook body is read as it is. Template means the platform sends nothing useful until you paste one in; the templates follow the table.
| Platform and source key | Configure on their side | Payload | Where service comes from |
How recovery is detected |
|---|---|---|---|---|
Dynatrace dynatrace |
Problem notification, custom integration | Template below | A service: tag, else the impacted service, then the application, then the first impacted entity |
State is RESOLVED, matched to the open alert by PID |
Datadog datadog |
Webhooks integration, custom payload | Template below | The monitor's service: tag, carried in $TAGS |
alert_transition contains recovered |
Grafana grafana |
Contact point of type Webhook | Native payload | The service label on the alert rule |
status per alert; keep resolved messages on |
Amazon CloudWatch cloudwatch-sns |
Alarm action to SNS, HTTPS subscription | Native payload (the SNS envelope) | service=<name> in the alarm description, else a ServiceName or service dimension, else the resource identifier dimension; severity=<level> in the description sets the severity, which is otherwise critical |
Alarm state OK; give the alarm an OK action on the same topic |
Azure Monitor azure-monitor |
Action group webhook, common alert schema | Native payload | The alert's target resource name; a rule scoped to a workspace or subscription has none, so map resource to service with label_map |
monitorCondition is Resolved; keep auto-resolve on |
Robusta robusta |
Webhook sink, token as authorization | Native payload | subject.labels.service, else the workload name |
Not sent: findings are always firing, so close from another source or by hand |
Prometheus Alertmanager alertmanager |
Webhook receiver, send_resolved: true |
Native payload | The service label on the alerting rule |
status per alert |
Ticket systems (ServiceNow, Jira, Azure DevOps) and the posture scanners (Security Hub, Defender for Cloud) use the same URL shape. Their payloads are in the Webhook reference.
Paste-in templates
Dynatrace
In Dynatrace open Settings Classic, Integration, Problem notifications, Add notification, Custom integration. Newer SaaS environments (the *.apps.dynatrace.com console) have no classic Problem notifications: there, build a Workflow instead. In the Workflows app add a workflow with the Davis problem trigger (open and close), one Run JavaScript task that POSTs the JSON below to the workspace URL with the token in the Authorization header, open the app's Authorization settings once and grant the three primary permissions plus automation, and add the workspace host under Settings, Preferences, Limit outbound connections. A platform token (dt0s16…) can drive the Workflow API, but the read connector needs a classic access token (dt0c01…): platform tokens cannot carry the environment-api scopes. Put the workspace URL in Webhook URL, add the token under Additional HTTP headers and mark it secret, and paste this as the custom payload. Keep {ImpactedEntities} and {ProblemDetailsJSONv2} without quotes, because Dynatrace renders them as JSON.
{
"ProblemID": "{ProblemID}",
"PID": "{PID}",
"State": "{State}",
"ProblemTitle": "{ProblemTitle}",
"ProblemSeverity": "{ProblemSeverity}",
"ProblemImpact": "{ProblemImpact}",
"ImpactedEntities": {ImpactedEntities},
"ImpactedEntity": "{ImpactedEntity}",
"Tags": "{Tags}",
"ProblemURL": "{ProblemURL}",
"ProblemDetailsText": "{ProblemDetailsText}",
"ProblemDetailsJSONv2": {ProblemDetailsJSONv2}
}
Tag your services in Dynatrace with service:<name>, using the names in your estate inventory, so a Dynatrace problem on checkout joins the alerts already open for checkout. Dynatrace's Send test notification opens one real test incident; resolve it by hand.
Datadog
In Datadog open Integrations, Webhooks, New. Put the workspace URL in URL, the token in Custom Headers as {"X-Webhook-Token": "<webhook-token>"}, and paste this as the payload. Then mention @webhook-<name> in each monitor's message, for the alert and for the recovery.
{
"alert_id": "$ALERT_ID",
"id": "$ID",
"alert_scope": "$ALERT_SCOPE",
"title": "$ALERT_TITLE",
"body": "$EVENT_MSG",
"alert_type": "$ALERT_TYPE",
"alert_transition": "$ALERT_TRANSITION",
"tags": "$TAGS",
"date": "$DATE"
}
Tag monitors with service:<name> and env:prod; Datadog's unified service tagging already does, so keep $TAGS in the payload. Severity comes only from alert_type, so a severity: tag is ignored. Keep $ALERT_SCOPE: it separates the groups of a multi-alert monitor, so one group recovering does not close the incident for the others.
Anything else: the generic endpoint
New Relic, Splunk, Elastic, Zabbix, PagerDuty and in-house tools need no adapter. The generic endpoint takes one shape, and you choose the labels.
POST https://<your-platform>/webhook/generic/<workspace-slug>
{
"source": "my-monitoring",
"alerts": [
{
"fingerprint": "orders-api:db-pool-exhausted",
"status": "firing",
"labels": {
"alertname": "DBPoolExhausted",
"service": "orders-api",
"severity": "critical",
"env": "prod"
},
"annotations": {
"summary": "orders-api connection pool exhausted",
"message": "200 of 200 connections in use"
}
}
]
}
Four things make it work. The fingerprint is the same for the trigger and the recovery of one condition. The status is exactly firing or resolved. The service label is the service name as it appears in your estate inventory. The severity is critical, error, warning or info. Put values that change on every firing, such as a timestamp or a reading, in annotations, never in labels.
Where the tool lets you edit the webhook body, send this shape directly. A tool whose body is fixed needs a small relay in front, such as a function or a Logic App. When the tool sends its own label names, rename them with label_map instead of changing every rule.
Worked example: a New Relic workflow
A New Relic workflow sends a webhook from a template you write. This one keeps New Relic's own words as labels.
{
"source": "newrelic",
"alerts": [
{
"fingerprint": {{ json issueId }},
"status": {{#eq "CLOSED" state}}"resolved"{{else}}"firing"{{/eq}},
"labels": {
"entity_name": {{ json entitiesData.names.[0] }},
"priority": {{ json priority }},
"condition_name": {{ json accumulations.conditionName.[0] }}
},
"annotations": {
"summary": {{ json annotations.title.[0] }},
"issue_url": {{ json issuePageUrl }}
}
}
]
}
Use New Relic's template preview to confirm it renders valid JSON before you save it. Then map the words onto the platform's. A person with members.manage sets the maps once for the workspace; there is no portal editor for them. Find the tenant id with GET /api/aiops/me.
curl -X PATCH "https://<your-platform>/api/aiops/<tenant-id>" \
-H "Authorization: Bearer $PAT" \
-H "Content-Type: application/json" \
-d '{"settings": {
"label_map": {"entity_name": "service", "priority": "severity", "condition_name": "alertname"},
"severity_map": {"CRITICAL": "critical", "HIGH": "error", "MEDIUM": "warning", "LOW": "info"}
}}'
label_map renames the label keys, so entity_name becomes service, priority becomes severity and condition_name becomes alertname. severity_map then rewrites the values of the severity label for every source in the workspace, not only this one. Each key you send replaces that key whole, so include any entries the workspace already has. The maps run before grouping, and the issueId fingerprint keeps the open and the close of one issue together.
Splunk, Elastic, Zabbix and PagerDuty follow the same recipe: map the field that names the failing thing onto service, the priority onto severity, the rule name onto alertname, and the incident or problem id onto fingerprint.
Send a test alert
Before you touch the real sender, prove the path from Alert intake & tokens (in the product: /portal/tokens#validation).
- Choose the source in Send a test alert: alertmanager, generic, datadog or dynatrace. Press Create test alert.
- Copy the endpoint and the curl command. Send the sample with your webhook token in the
X-Webhook-Tokenheader. - The card reports Received and links the incident it opened. The test runs on a service named
aiops-validation, so it never joins a real incident, and it is safe to ignore. It does open one incident and start one investigation; once the test is created, the card says what sending it will do, including model cost and notifications, before you send it.
For Datadog and Dynatrace the sample is what their template renders to. It proves the URL, the token and the adapter, not your template. Check the template with the Send test notification or test button on the sending platform.
What happens next
- Dedupe. An alert whose fingerprint is already active is counted, not stored again.
- Correlation. Alerts that share a
servicejoin one incident, however old it is. This is why theservicelabel matters most. See Dedupe, correlation, suppression. - Grouping. Services failing together on a recorded dependency are grouped under the one the others depend on; unrelated services stay separate incidents. See Alert storm: grouped by the service graph.
- Recovery. A resolved event closes the incident only when nothing else on it is still firing, and only while it is open or investigating.
- The agent. A new incident starts an investigation at once, unless intake holds it. An alert naming no service while the triage queue is on (the default since 0.1.133,
aiops_unmapped_triage), or a built-in detection warning, waits with no run until someone selects Analyse (for an unmapped alert, choosing its label on the Unidentified service — map this source card keeps the next one from that source out of the queue). An alert with no identity labels while the identity gate is on waits too, with no Analyse on its page: add the missing label at the source. The agent reads your connected systems, writes up the probable cause and, if a fix is possible, proposes it for a person to approve. See Incidents.
If nothing arrives, a 401 is the token, a 404 is the source name, a 400 request body is not JSON means the filled-in template is not valid JSON (usually a string placeholder left unquoted), and a 200 with "received": 0 means the payload arrived without the fields the adapter needs, usually because the template left them out. See Troubleshooting.