Runframe
API Reference

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:

  1. Identifier — Routes webhooks to your organization and the correct integration
  2. Security token — Authenticates requests (like an API key)

Get your webhook URL from the Integrations Hub in the Runframe dashboard.


Supported integrations

IntegrationPayload formatSignature verification
DatadogDatadog webhook payload (native)Routing key only
SentrySentry webhook payload (native)Optional HMAC-SHA256 (sentry-hook-signature header)
PrometheusAlertmanager webhook payload (native)Routing key only
CloudWatchSNS message wrapping CloudWatch alarmSNS certificate-based RSA signature (automatic)
Custom WebhookRunframe-defined JSON schemaOptional 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

FieldTypeDescription
titlestringBrief incident summary (max 500 chars)
service_idstringService ID to route the incident to (e.g., SER-00001, max 50 chars)

Optional fields

FieldTypeDescription
descriptionstringDetailed description (max 5000 chars)
severitystringSEV0, SEV1, SEV2, SEV3, or SEV4
statusstringnew, investigating, or resolved (default: new)
source_urlstringLink back to the source (dashboard, CI run, etc.)
dedup_keystringDeduplication key — same key updates existing incident (max 255 chars)
metadataobjectKey-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 3000

Need more?