Azure Monitor — Ingest Setup

This guide shows how to route Azure Monitor alerts to Culprit using the Common Alert Schema and an action-group webhook.


Endpoint

POST https://ingest.theculprit.ai/ingest/<tenant_id>/<service_name>/azure-monitor?token=<ingest_token>

Replace <tenant_id> and <service_name> with the values shown on your service's settings page, and <ingest_token> with your ingest token.


Authentication

Azure action-group plain webhooks cannot send custom HTTP headers, so the ingest token rides in the URL query string as ?token=<ingest_token> instead of an Authorization header.

Treat the full webhook URI as a secret. Anyone who can read the URI can post to your endpoint. Keep it out of source control, access logs, and publicly readable dashboards.

Getting your ingest token:

  1. Open the Culprit dashboard → Services → select the service.
  2. In the Ingest token section, click Issue.
  3. Copy the token shown — it is displayed once. After you navigate away, only the token prefix is shown.

To rotate a compromised token, click Rotate. The previous token remains valid for the grace period shown, giving you time to update the action-group webhook URI without an interruption.


Setup in Azure

  1. Create (or open) an action group. In the Azure portal, go to Monitor → Alerts → Action groups and create or edit an action group.
  2. Add a Webhook action:
    • Action type: Webhook
    • URI: your Culprit endpoint, including the ?token= query string shown above.
    • Enable the common alert schema: toggle to Yes. Culprit reads the data.essentials fields from the Common Alert Schema — this toggle is required.
  3. Attach the action group to your alert rules. For each alert rule, set the action group as the target. When the rule fires, Azure calls the webhook with a Common Alert Schema payload.

How Culprit maps Azure Monitor alerts

Culprit reads the stable data.essentials block from the Common Alert Schema. The data.alertContext block varies by signal type and alert rule — its contents are preserved in context so the full payload is tokenized and encrypted.

Field mapping

| Azure field | Culprit event field | Notes | |---|---|---| | data.essentials.alertRule | event_type | The alert rule name | | data.essentials.severity | severity | Sev0–Sev4 mapped (see table below) | | data.essentials.description | message | Falls back to alertRule if absent | | data.essentials.alertId | context.alertId | Preserved for dedup | | data.essentials.signalType | context.signalType | Metric / Log / Activity Log | | data.essentials.monitorCondition | context.monitorCondition | Fired or Resolved | | data.essentials.monitoringService | context.monitoringService | Platform, Log Analytics, etc. | | data.essentials.alertTargetIDs | context.alertTargetIDs | Resource IDs being monitored | | data.essentials.firedDateTime | context.firedDateTime | ISO 8601 | | data.alertContext | context.alertContext | Full context block, varies by signal type | | (entire payload) | context.raw | Full original payload, preserved encrypted |

Severity mapping

| Azure severity | Culprit severity | |---|---| | Sev0 | CRITICAL | | Sev1 | HIGH | | Sev2 | MEDIUM | | Sev3 | LOW | | Sev4 | LOW |

Sev3 and Sev4 both map to LOW — Azure treats them as informational, and LOW is the appropriate on-call posture.

monitorCondition: Fired vs. Resolved

Azure sends a separate webhook when an alert condition clears, with monitorCondition set to Resolved. Culprit vaults both Fired and Resolved events. The Resolved event flows through the standard pipeline; your alert rules and notification destinations can filter on monitorCondition or the LOW severity.

PII in any field (hostnames, IP addresses, email addresses, API keys) is detected and tokenized before any storage or notification. The original values are preserved encrypted and are visible only in the incident detail view after authentication.


Worked example

Azure Monitor fires an alert. The action-group webhook delivers this body:

{
  "schemaId": "azureMonitorCommonAlertSchema",
  "data": {
    "essentials": {
      "alertId": "/subscriptions/x/providers/Microsoft.AlertsManagement/alerts/aaa",
      "alertRule": "WCUS-R2-Gen2",
      "severity": "Sev1",
      "signalType": "Metric",
      "monitorCondition": "Fired",
      "monitoringService": "Platform",
      "alertTargetIDs": ["/subscriptions/x/.../wcus-r2-gen2"],
      "firedDateTime": "2026-03-22T13:58:24.37Z",
      "description": "CPU threshold exceeded"
    },
    "alertContext": {
      "conditionType": "SingleResourceMultipleMetricCriteria"
    }
  }
}

Culprit event (before tokenization):

{
  "event_type": "WCUS-R2-Gen2",
  "severity": "high",
  "source": "azure-monitor",
  "message": "CPU threshold exceeded",
  "context": {
    "alertId": "/subscriptions/x/providers/Microsoft.AlertsManagement/alerts/aaa",
    "signalType": "Metric",
    "monitorCondition": "Fired",
    "monitoringService": "Platform",
    "alertTargetIDs": ["/subscriptions/x/.../wcus-r2-gen2"],
    "firedDateTime": "2026-03-22T13:58:24.37Z",
    "alertContext": { "conditionType": "SingleResourceMultipleMetricCriteria" }
  }
}

This event flows through the standard pipeline: encrypted vault storage → PII tokenization → storm check → embedding → correlation → root-cause analysis.


Successful response

{ "accepted": true, "ids": ["<vault_id>"] }

Error responses

| Status | Body | Meaning | |---|---|---| | 401 | { "error": "missing_auth" } | No ?token= query parameter | | 401 | { "error": "invalid_ingest_token" } | Token is incorrect or has been rotated/revoked | | 400 | { "error": "unrecognized_payload" } | Body is not valid JSON, is not a Common Alert Schema payload, or data.essentials.alertRule is absent | | 403 | { "error": "service_disabled" } | The service exists but monitoring is paused | | 404 | { "error": "service_deleted_or_not_found" } | The service does not exist | | 429 | { "error": "rate_limited" } | Storm protection active (sustained high volume) |