Skip to main content

Overview

Webhooks in Sift enable the sending of real-time notifications to other systems when specific events occur, such as when a Rule is violated or resolved. Unlike APIs that require constant polling, webhooks automatically push data to a designated URL as soon as the event happens. Each webhook includes a trigger event (like a live Rule Violation), a payload that contains event-specific information (which can be customized using built-in variables), and a target URL that receives the data via an HTTP POST request. This allows Sift to integrate seamlessly with tools like Microsoft Teams, Slack, Jira, OpsGenie, or PagerDuty. It’s important to note that webhooks in Sift only fire during live Rule evaluations; they don’t execute when Rules are run on historical data.

Role-based access control (RBAC)

Webhooks follow Sift’s standard RBAC model:
Unauthorized users who attempt to access restricted pages, such as a specific webhook page, by entering the URL directly will be redirected to the Manage page and shown a warning toast indicating insufficient permissions.

Trigger event types

When creating a webhook, you must select a trigger event type. The following trigger event type is available:

Settings

When creating a webhook in Sift, the following settings must be configured:
The available built-in variables depend on the selected trigger event type. See Built-in variables for details.

Webhook payloads

Webhook payloads define the content sent to the destination URL when a webhook is triggered. Payloads in Sift are fully customizable and can be formatted as either plain text or JSON. Each payload can include built-in variables specific to the selected trigger event type, allowing users to dynamically insert relevant data such as Rule names, statuses, timestamps, and more. For example, the following JSON payload displays a few of the built-in variables for a Rule Violation trigger event type:
{
  "ruleName": "{{.RuleName}}",
  "status": "{{.Status}}",
  "sentAt": "{{.SentAt}}"
}
To simplify integration with popular tools, Sift provides predefined payload templates for services like Slack, OpsGenie, Jira, and PagerDuty. These can be selected from the Template list during webhook creation or editing. In addition to static content, Sift’s payload editor supports control flow, including if-else logic for dynamic formatting. For example, the following JSON payload for Slack demonstrates how conditional expressions can be used to alter the payload based on the Rule’s status:
{
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "{{if eq .Status \"resolved\"}}:white_check_mark: Resolved{{else}}:bangbang: *Alert - {{.Status}}*{{end}}"
      }
    }
  ]
}

Built-in variables

Each trigger event type includes a set of built-in variables that are available for use and viewing when creating or editing a webhook. These variables can be used to customize the webhook payload with dynamic, event-specific data.

Rule Violation

The following table lists the built-in variables available for the Rule Violation trigger event type:

Log generation and retention policy

Webhook logs are automatically generated each time a webhook fires or fails to fire. If a webhook fails to fire, the log entry will include the reason for the failure to assist with troubleshooting. These logs are retained for four weeks, after which they are automatically deleted as part of routine maintenance. Sift does not retain logs generated from test webhook executions.

Rate limits and retry policy

To protect users and their integrations from unintended overload, each webhook in Sift is subject to a rate-limiting policy. This ensures stable and predictable behavior, even during high-volume Rule evaluations. Rate-limit policy:
  • If a webhook exceeds its rate limits and burst tokens are exhausted, it will fail to send. A corresponding failure entry will be recorded in the webhook logs, indicating that the webhook was blocked due to rate-limiting.
  • Sift will retry a failed webhook once, but only if the failure is due to a reason other than rate-limiting. If a webhook fails due to being rate-limited, it will not be retried.

Custom HTTP headers

When configuring a webhook in Sift, you can optionally define certain HTTP headers to customize how the receiving system handles the request. These headers are sent along with the webhook payload and are commonly used to specify content format or provide authentication. Supported custom headers:

Secure webhooks

Sift supports secure webhook delivery using HMAC-SHA256 signatures. These signatures ensure the webhook was sent by Sift and has not been tampered with in transit. Webhooks are only signed if your Sift environment has a webhook signing key configured (generated). If no key exists, webhooks will not include an X-Sift-Signature header, and should not be trusted for secure operations. Once a webhook signing key is configured in your Sift environment, each outgoing webhook will include the signature header that enables verification using HMAC-SHA256. The signature is computed using the following formula:
HMAC_SHA256(signing_key, sent_at + raw_payload)
The sentAt value must be present in the payload to successfully validate the signature. However, this value is not included automatically, you must explicitly add it using {{.SentAt}} in your webhook payload template. Without it, signature verification will fail because the required signed input cannot be reconstructed.
Verifying webhook signatures (Python example)
from flask import Flask, request, abort
import hmac
import hashlib
import json
import time

app = Flask(__name__)

# Replace with your actual Sift webhook signing key
SECRET_KEY = b'your-Sift-webhook-signing-key'

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    raw_body = request.get_data()
    received_signature = request.headers.get('X-Sift-Signature')

    if not received_signature:
        abort(400, 'Missing X-Sift-Signature header')

    try:
        payload = json.loads(raw_body)
    except json.JSONDecodeError:
        abort(400, 'Invalid JSON payload')

    sent_at = payload.get('sentAt')
    if not sent_at:
        abort(400, 'Missing sentAt in payload')

    # Strongly recommended: Reject old webhooks (for example, older than 5 minutes)
    if abs(int(time.time()) - int(sent_at)) > 300:
        abort(408, 'Webhook is too old')

    # Reconstruct the signed message and compute signature
    message = sent_at.encode() + raw_body
    expected_signature = hmac.new(SECRET_KEY, message, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(received_signature, expected_signature):
        abort(403, 'Invalid signature')

    return 'Webhook verified and accepted', 200

if __name__ == '__main__':
    app.run(debug=True)

Replay attacks

To prevent replay attacks, we recommend rejecting any webhook where the sentAt timestamp is older than 5 minutes. This ensures that even if a valid webhook is intercepted, it cannot be reused later to trigger unauthorized actions.

Return a 2xx response immediately

Your endpoint should return a successful status code (2xx) as soon as possible, before running any complex logic that might delay the response or cause a timeout.