Webhooks
Send webhooks to Runframe to create incidents from Datadog, Sentry, Prometheus, CloudWatch, and custom sources. Learn endpoint format, payload schemas, and signature verification.
Webhooks
Send webhooks to Runframe to create incidents automatically from monitoring tools, scripts, and CI/CD pipelines.
Overview
Runframe provides a webhook endpoint for each integration you configure. External tools POST alert payloads to these endpoints, and Runframe creates or updates incidents automatically.
Webhook endpoint
POST https://api.runframe.io/webhooks/{routingKey}Each integration (Datadog, Sentry, Prometheus, CloudWatch, Custom Webhook) gets a unique routing key that acts as both:
- Identifier — Routes webhooks to your organization and the correct integration
- Security token — Authenticates requests (like an API key)
Get your webhook URL from the Integrations Hub in the Runframe dashboard.
Supported integrations
| Integration | Payload format | Signature verification |
|---|---|---|
| Datadog | Datadog webhook payload (native) | Routing key only |
| Sentry | Sentry webhook payload (native) | Optional HMAC-SHA256 (sentry-hook-signature header) |
| Prometheus | Alertmanager webhook payload (native) | Routing key only |
| CloudWatch | SNS message wrapping CloudWatch alarm | SNS certificate-based RSA signature (automatic) |
| Custom Webhook | Runframe-defined JSON schema | Optional HMAC-SHA256 (X-Runframe-Signature header) |
Native integrations (Datadog, Sentry, Prometheus, CloudWatch) accept the provider's standard payload format — no custom formatting needed. Runframe parses them automatically.
Custom Webhook payload
The Custom Webhook integration accepts a Runframe-defined JSON payload. Use this for any tool that can make an HTTP POST.
Required fields
| Field | Type | Description |
|---|---|---|
title | string | Brief incident summary (max 500 chars) |
service_id | string | Service ID to route the incident to (e.g., SER-00001, max 50 chars) |
Optional fields
| Field | Type | Description |
|---|---|---|
description | string | Detailed description (max 5000 chars) |
severity | string | SEV0, SEV1, SEV2, SEV3, or SEV4 |
status | string | new, investigating, or resolved (default: new) |
source_url | string | Link back to the source (dashboard, CI run, etc.) |
dedup_key | string | Deduplication key — same key updates existing incident (max 255 chars) |
metadata | object | Key-value string pairs for additional context (max 10 keys) |
Example payload
{
"title": "Database connection pool exhaustion",
"service_id": "SER-00002",
"description": "Database connections exhausted. API returning 500 errors.",
"severity": "SEV1",
"dedup_key": "db-pool-prod-001",
"metadata": {
"environment": "production",
"region": "us-east-1"
}
}Minimal payload
{
"title": "Server is down",
"service_id": "SER-00001"
}Signature verification (Custom Webhook)
Custom Webhooks support optional HMAC-SHA256 signing for security beyond the routing key.
Headers
When a signing secret is configured, include two headers:
X-Runframe-Signature: sha256=<hex HMAC>
X-Runframe-Timestamp: <unix seconds>The signature is computed as: HMAC-SHA256(signingSecret, "${timestamp}.${body}")
Runframe uses an asymmetric replay window: timestamps older than 5 minutes in the past or more than 1 minute in the future are rejected.
Signing example (bash)
TIMESTAMP=$(date +%s)
BODY='{"title":"Deploy failed","service_id":"SER-00002","severity":"SEV2"}'
SIGNATURE=$(echo -n "${TIMESTAMP}.${BODY}" | openssl dgst -sha256 -hmac "your-signing-secret" | awk '{print $2}')
curl -X POST https://api.runframe.io/webhooks/YOUR_ROUTING_KEY \
-H "Content-Type: application/json" \
-H "X-Runframe-Signature: sha256=${SIGNATURE}" \
-H "X-Runframe-Timestamp: ${TIMESTAMP}" \
-d "${BODY}"Signing example (Python)
import hmac, hashlib, time, json, requests, os
webhook_url = os.environ['RUNFRAME_WEBHOOK_URL']
signing_secret = os.environ['RUNFRAME_SIGNING_SECRET']
payload = {"title": "High memory usage", "service_id": "SER-00001", "severity": "SEV2"}
body = json.dumps(payload)
timestamp = str(int(time.time()))
signature = hmac.new(
signing_secret.encode(),
f"{timestamp}.{body}".encode(),
hashlib.sha256
).hexdigest()
response = requests.post(webhook_url, data=body, headers={
'Content-Type': 'application/json',
'X-Runframe-Signature': f'sha256={signature}',
'X-Runframe-Timestamp': timestamp
})Signing is optional
If no signing secret is configured for your Custom Webhook integration, the routing key alone authenticates requests. HMAC signing adds an extra layer for environments where URL secrecy is harder to guarantee.
Security
Keep webhook URLs secret
Treat webhook URLs like passwords. Anyone with the URL can create incidents in your organization.
Best practices:
- Store webhook URLs in environment variables or secret managers
- Don't commit webhook URLs to git
- Rotate webhook URLs if accidentally exposed (regenerate in Settings)
- Disconnect unused integrations
Rate limiting
Runframe rate-limits incoming webhooks to 100 requests per minute per routing key. Requests exceeding the limit receive a 429 Too Many Requests response.
Deduplication
Runframe deduplicates incoming webhooks using sourceEventId (derived from the alert payload or dedup_key):
- Same
sourceEventId— Updates the existing incident instead of creating a duplicate - No
sourceEventId— Every request creates a new incident
For Custom Webhooks, the dedup_key field controls this behavior. Native integrations (Datadog, Sentry, etc.) derive the dedup key from the provider's event ID automatically.
Testing
Test with curl
curl -X POST https://api.runframe.io/webhooks/YOUR_ROUTING_KEY \
-H "Content-Type: application/json" \
-d '{
"title": "Test webhook",
"service_id": "SER-00001",
"description": "Testing webhook integration"
}'Local development
Use tools like ngrok to expose a local endpoint if you need to test provider-side webhook configuration:
ngrok http 3000Need more?
- Webhook Security — Securing your webhook endpoints
- Integrations — Setup guides for Datadog, Sentry, Prometheus, CloudWatch, and Custom Webhook
- Web Dashboard — Integration management UI