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 and follow the wizard.

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:

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:

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

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 eventalert_feedbackdeclared_alert_severityowner
Alert is creatednullnullnot included in payload
Alert is acknowledgedinvestigating----
Alert status is updatedinvestigating, 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 incidentinvestigating (only if current is null or no_status)SEV-1, SEV-2, SEV-3, SEV-4--
Alert is unmarked as incident--null--
Alert is resolvedfixed, expected, no_action_needed, false_positive----

Payload Examples

Example 1: Rule breach

{
    "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

{
    "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"
}