Webhooks API
Runframe webhooks API reference. Create incidents from Datadog, Sentry, Prometheus, CloudWatch, and Custom Webhooks. Learn payload format, authentication, and examples.
Webhooks
Create incidents automatically from external tools using webhooks.
Overview
Runframe webhooks allow you to create incidents from any monitoring tool, custom script, or third-party service. Send a POST request to Runframe with incident details and we'll create the incident and notify your team.
Webhook endpoint
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
- Security token - Authenticates requests (like an API key)
Get your webhook URL from Settings → Integrations in the Runframe dashboard.
Creating a webhook
Generate a webhook URL
- Navigate to Settings → Integrations
- Click Connect for your integration (Datadog, Sentry, Prometheus, CloudWatch, or Custom Webhook)
- Copy the unique webhook URL (includes your routing key)
- Link services to route alerts to specific teams
Configure your tool
Use the webhook URL in your monitoring tool or custom script:
curl https://api.runframe.io/webhooks/abc123def456... \
-X POST \
-H "Content-Type: application/json" \
-d '{
"title": "API latency spike",
"service_id": "SER-00001",
"severity": "SEV1",
"description": "API response times > 5s"
}'Webhook payload (Custom Webhook)
The Custom Webhook integration accepts a Runframe-defined JSON payload. Other integrations (Datadog, Sentry, Prometheus, CloudWatch) accept their native payload formats — Runframe parses them automatically.
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 (default: uses fallback chain) |
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 payloads
Minimal incident
{
"title": "Server is down",
"service_id": "SER-00001"
}Complete incident
{
"title": "Database connection pool exhaustion",
"service_id": "SER-00002",
"description": "Database connections exhausted. API returning 500 errors.",
"severity": "SEV1",
"status": "new",
"source_url": "https://grafana.example.com/dashboard/db",
"dedup_key": "db-pool-prod-001",
"metadata": {
"environment": "production",
"region": "us-east-1"
}
}With deduplication
Send the same dedup_key to update an existing incident instead of creating a new one:
{
"title": "High error rate (updated)",
"service_id": "SER-00001",
"severity": "SEV2",
"dedup_key": "error-rate-api-001",
"metadata": {
"error_rate": "15%",
"region": "us-east-1"
}
}Response format
Success response
{
"data": {
"incidentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"incidentNumber": "INC-042",
"status": "investigating"
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-01-15T10:30:00Z"
}
}incidentId is a UUID. incidentNumber is the human-readable incident reference.
Error response
{
"error": {
"code": "ROUTE_NOT_FOUND",
"message": "Webhook routing key is invalid or expired",
"requestId": "req_abc123"
}
}Error codes
| Code | Description |
|---|---|
ROUTE_NOT_FOUND | Invalid or expired routing key |
INVALID_PAYLOAD | Request body failed validation |
RATE_LIMITED | Exceeded 100 requests/min/key |
SIGNATURE_INVALID | HMAC signature verification failed |
TIMESTAMP_EXPIRED | Timestamp outside replay window |
Security
Keep webhook URLs secret
Treat webhook URLs like passwords. Anyone with the URL can create incidents in your organization.
Best practices:
- Don't commit webhook URLs to git
- Use environment variables for scripts
- Regenerate webhook URLs if they're accidentally exposed
- Disconnect unused integrations
Rate limiting
Runframe rate-limits incoming webhooks to 100 requests per minute per routing key. Exceeding the limit returns 429 Too Many Requests.
HMAC signing (Custom Webhook)
For Custom Webhooks, configure a signing secret for an additional security layer. See the Webhook Security page for details.
Testing webhooks
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"
}'Examples
Datadog webhook
curl https://api.runframe.io/webhooks/abc123def456... \
-X POST \
-H "Content-Type: application/json" \
-d '{
"title": "{{alert.name}}",
"description": "{{alert.text}}",
"severity": "{{alert.priority}}"
}'Sentry webhook
curl https://api.runframe.io/webhooks/abc123def456... \
-X POST \
-H "Content-Type: application/json" \
-d '{
"title": "{{event.title}}",
"description": "{{event.culprit}}",
"severity": "{{event.level}}"
}'Prometheus Alertmanager webhook
receivers:
- name: 'runframe'
webhook_configs:
- url: 'https://api.runframe.io/webhooks/abc123def456...'
send_resolved: trueCloudWatch via SNS
CloudWatch alarms are sent via SNS — no manual curl needed. Configure the SNS subscription to point to your Runframe webhook URL. See the CloudWatch integration guide for setup instructions.
Custom Webhook (Python)
import requests
import os
webhook_url = os.environ['RUNFRAME_WEBHOOK_URL']
def create_incident(title, service_id, severity='SEV2', description=''):
payload = {
'title': title,
'service_id': service_id,
'severity': severity,
'description': description
}
response = requests.post(webhook_url, json=payload)
return response.json()
# Usage
incident = create_incident(
title='High memory usage',
service_id='SER-00001',
severity='SEV2',
description='Memory usage above 90% on production servers'
)Custom Webhook with HMAC signing (Python)
import requests
import hmac
import hashlib
import time
import json
import os
webhook_url = os.environ['RUNFRAME_WEBHOOK_URL']
signing_secret = os.environ['RUNFRAME_SIGNING_SECRET']
def create_signed_incident(payload):
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
})
return response.json()Need more?
- Authentication – Webhook security and signature verification
- Integrations – Pre-built Datadog, Sentry, Prometheus, CloudWatch, and Custom Webhook guides
- Slash Commands – Complete
/inccommand reference - Web Dashboard – Webhook management UI
Integrations
Connect Runframe to Datadog, Sentry, Prometheus, AWS CloudWatch, Jira, Google Meet, Custom Webhooks, and more. Learn webhook setup and field mapping.
Webhook Security
Secure your Runframe webhook integrations. Learn about routing keys, HMAC signature verification, rate limiting, and best practices.