---
updatedAt: 2025-11-19T14:01:25.000Z
agentTools:
  projectIndex: https://docs.getmontecarlo.com/llms.txt
---

# Webhooks

Integrate Monte Carlo with a Webhook

This guide explains how to receive webhook notifications from Monte Carlo alerts and alert updates. When you receive webhooks, a signature will optionally be added so you can verify the authenticity of the calls you receive.

## Setting up webhook notifications

To receive notifications from Monte Carlo via webhooks, please visit the [notification settings page](https://getmontecarlo.com/settings/notifications), click Create audience and select Webhook from the Channel drop down.

When setting up webhooks, you can optionally specify a secret that will allow you to authenticate incoming calls on your webhook endpoint. Using Python 3.6, you may generate a secret like so:

```python
import secrets
secrets.token_urlsafe(42)
```

## Receiving and verifying a webhook

Webhooks will be sent as a POST request of type *application/json* to the endpoint you specify during setup.

If you provide a secret during setup, Monte Carlo uses it to generate a hash signature (HMAC SHA-512) for every call it makes to your endpoint. The signature will be sent along in the *x-mcd-signature* header.

Using AWS Lambda (with an API Gateway), you may process and verify a message like so:

```python
"""
Example MCD Webhook Signature Verification
"""

import hashlib
import hmac
import json
import urllib.parse
from typing import Dict

SHARED_SIGNING_SECRET = b'<SECRET>'  # This should be an environment variable


def lambda_handler(event: Dict, context: Dict) -> Dict:
    mcd_signature = event['headers'].get('x-mcd-signature')
    body = json.loads(event['body'])

    if verify_signature(mcd_signature=mcd_signature, body=body):
        print('Success!')
        # business logic
        return {'statusCode': 200}
    return {'statusCode': 403}


def verify_signature(mcd_signature: str, body: Dict) -> bool:
    body_as_byes = urllib.parse.urlencode(body).encode('utf8')
    computed_signature = hmac.new(SHARED_SIGNING_SECRET, body_as_byes, hashlib.sha512).hexdigest()
    return hmac.compare_digest(computed_signature, mcd_signature)
```

> 🚧 SHARED\_SIGNING\_SECRET should not be embedded in your code
>
> We recommend reading it from an environment variable.

## IP Allowlisting

To use IP allowlisting with your webhook endpoint, please ensure the **External webhook public IP addresses** provided in the [Account information](https://getmontecarlo.com/account-info#networking) page are allowlisted. All IP addresses must be allowlisted, as requests from the Monte Carlo platform can originate from any one of them.

## Webhook events

The following events receive a webhook:

1. Alert is created
2. Alert is acknowledged
3. Alert status is updated
4. Alert owner is changed
5. External ticket is attached to an alert (Jira, ServiceNow, etc.)
6. Alert is marked as incident
7. Alert is unmarked as incident
8. Alert is resolved

The following are the key fields that are changed by alert updates.

| Webhook event                           | alert\_feedback                                                                                             | declared\_alert\_severity          | owner                   |
| :-------------------------------------- | :---------------------------------------------------------------------------------------------------------- | :--------------------------------- | :---------------------- |
| Alert is created                        | `null`                                                                                                      | `null`                             | not included in payload |
| Alert is acknowledged                   | `investigating`                                                                                             | --                                 | --                      |
| Alert status is updated                 | `investigating`, `no_status`, `work_in_progress`, `fixed`, `expected`, `no_action_needed`, `false_positive` | --                                 | --                      |
| Alert owner is changed                  | --                                                                                                          | --                                 | email of assigned owner |
| External ticket is attached to an alert | --                                                                                                          | --                                 | --                      |
| Alert is marked as incident             | `investigating` (only if current is `null` or `no_status`)                                                  | `SEV-1`, `SEV-2`, `SEV-3`, `SEV-4` | --                      |
| Alert is unmarked as incident           | --                                                                                                          | `null`                             | --                      |
| Alert is resolved                       | `fixed`, `expected`, `no_action_needed`, `false_positive`                                                   | --                                 | --                      |

## Payload Examples

**Example 1: Rule breach**

```json
{
    "account": {
        "id": "9a59c6f1-203a-4c72-8cd7-132db7f21b92"
    },
    "action": "incident",
    "payload": {
        "event_list": [
            {
                "data": {
                    "hits": [
                        {
                            "field": null,
                            "measurement_timestamp": "Jan 15, 06:10PM",
                            "timestamp": "",
                            "value": "50.0",
                            "value_rounded": "50"
                        }
                    ],
                    "metric_display_name": "custom_metric_1234",
                    "rule_comparisons": [
                        {
                            "metric": "custom_metric_1234",
                            "operator": ">",
                            "threshold": "49.0"
                        }
                    ]
                },
                "rule": {
                    "comparisons": [
                        {
                            "field": null,
                            "full_table_id": null,
                            "metric": "custom_metric_1234",
                            "operator": "GT",
                            "threshold": 49.0,
                            "warehouse_uuid": "custom_metric_1234"
                        }
                    ],
                    "creator_id": null,
                    "description": "test rule",
                    "execution_time": null,
                    "prev_execution_time": null,
                    "rule_type": "custom_sql",
                    "start_time": null,
                    "timezone": null,
                    "uuid": "0c8e06aa-a44b-4f2d-8a37-5ed3f394431d"
                },
                "table_name": null
            }
        ],
        "group_id": null,
        "incident_id": "f83867b6-8f25-428b-807c-08518f63384c",
        "url": "https://getmontecarlo.com/incidents/f83867b6-8f25-428b-807c-08518f63384c"
    },
    "type": "open_custom_rule_anomaly_incident",
    "version": "v0.1"
}
```

**Example 2: Volume anomaly**

```json
{
    "account": {
        "id": "9a59c6f1-203a-4c72-8cd7-132db7f21b92"
    },
    "action": "incident",
    "payload": {
        "event_list": [
            {
                "event_details": "Has 42 rows vs. 101 expected",
                "table_name": "artemis"
            }
        ],
        "group_id": "zeus:ares",
        "incident_id": "f83867b6-8f25-428b-807c-08518f63384c",
        "url": "https://getmontecarlo.com/incidents/f83867b6-8f25-428b-807c-08518f63384c"
    },
    "type": "open_anomaly_incident",
    "version": "v0.1"
}
```