# 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](#anything-else-the-generic-endpoint).

## The one URL and the token

```
POST https://<your-platform>/webhook/<source>/<workspace-slug>
```

[Alert intake & tokens](/portal/tokens#webhook) (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](/help/webhook-reference.md).

## 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.

```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.

```json
{
  "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 {#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>
```

```json
{
  "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.

```json
{
  "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`.

```bash
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](/portal/tokens#validation) (in the product: `/portal/tokens#validation`).

1. Choose the source in Send a test alert: alertmanager, generic, datadog or dynatrace. Press Create test alert.
2. Copy the endpoint and the curl command. Send the sample with your webhook token in the `X-Webhook-Token` header.
3. 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

1. **Dedupe.** An alert whose fingerprint is already active is counted, not stored again.
2. **Correlation.** Alerts that share a `service` join one incident, however old it is. This is why the `service` label matters most. See [Dedupe, correlation, suppression](/help/storms-and-unmapped-signals.md#dedupe-correlation-suppression).
3. **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](/help/storms-and-unmapped-signals.md#alert-storm-hold-and-split).
4. **Recovery.** A resolved event closes the incident only when nothing else on it is still firing, and only while it is open or investigating.
5. **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](/help/incidents.md).

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](/help/troubleshooting.md#nothing-happened-after-an-alert).


## Next steps

- Next: [Notifications, paging, on-call and tickets — set it up like the demo](/help/setup-notifications.md)
- [All Connect your environment pages](/help/connect-your-environment/llms.txt)
- [Documentation home](/help/)
