Webhooks
SUPERWISE Chat webhooks are inbound: your monitoring and alerting tools (PagerDuty, Datadog, or any custom system) POST alert payloads to a registered channel. SUPERWISE Chat verifies the request signature, normalizes the payload, and delivers the alert into a conversation you choose — so alerts land where your team is already working and can be triaged in context. Every inbound alert is checked for safety on the way in, the same way a message from a person is.
This is a reference. For other ways to call the API, see the Gateway API reference and Authentication. For a broader integration overview, see Integrate.
At a glance
Section titled “At a glance”| Direction | Inbound only — you send events to SUPERWISE Chat |
| Transport | HTTPS POST, JSON body |
| Authentication (ingest) | Per-channel HMAC-SHA256 signature in X-Webhook-Signature |
| Authentication (management) | Bearer JWT with the webhook.manage capability |
| Providers | PagerDuty · Datadog · Custom |
| Destination | A conversation you map at registration time |
Endpoints
Section titled “Endpoints”| Method | Path | Auth | Purpose |
|---|---|---|---|
POST | /v1/webhooks/register | JWT + webhook.manage | Register a channel and receive its signing secret (once) |
POST | /v1/webhooks/{channel_id} | HMAC signature | Ingest an alert |
GET | /v1/webhooks/{channel_id}/info | JWT + webhook.manage | Check a channel’s registration status |
All paths are relative to your API base URL (see the Gateway API reference).
channel_id format
Section titled “channel_id format”A channel_id is your identifier for the channel. It must match ^[a-zA-Z0-9_-]{1,64}$ — 1 to 64 characters, letters, digits, hyphens, and underscores only. Other characters are rejected with 400.
Register a channel
Section titled “Register a channel”Registration mints the HMAC secret used to sign every inbound alert and binds the channel to a destination conversation.
Request
POST /v1/webhooks/registerAuthorization: Bearer <JWT>Content-Type: application/json
{ "channel_id": "ops-alerts", "conversation_id": "conv-abc123"}| Field | Type | Required | Notes |
|---|---|---|---|
channel_id | string | yes | 1–64 chars, see format above |
conversation_id | string | yes | An existing conversation owned by your tenant |
Response — 201 Created
{ "channel_id": "ops-alerts", "conversation_id": "conv-abc123", "webhook_url": "/v1/webhooks/ops-alerts", "secret": "<64-char-hex-secret>", "signature_header": "X-Webhook-Signature", "message": "Webhook registered successfully. Use HMAC-SHA256 with this secret to sign requests."}The secret is a 32-byte random value rendered as a 64-character hex string.
Errors
| Status | When |
|---|---|
400 | Missing channel_id/conversation_id, or an invalid channel_id format |
403 | Caller lacks the webhook.manage capability |
404 | The conversation_id does not exist for your tenant |
409 | The channel is already registered — delete it first to re-register |
Send an alert
Section titled “Send an alert”Point your monitoring tool at the channel’s URL and sign each request.
Request
POST /v1/webhooks/{channel_id}X-Webhook-Signature: <hmac-sha256-hex>X-Webhook-Provider: pagerdutyContent-Type: application/json
{ ...provider payload... }Request headers
Section titled “Request headers”| Header | Required | Description |
|---|---|---|
X-Webhook-Signature | yes | HMAC-SHA256 of the request body, hex-encoded (see Signing requests) |
X-Webhook-Provider | no | pagerduty, datadog, or custom. If omitted, the provider is inferred from the payload shape, defaulting to custom |
X-Webhook-Timestamp | no | Unix epoch milliseconds. When present, requests older than 5 minutes are rejected as replays |
Provider detection
Section titled “Provider detection”When X-Webhook-Provider is absent, the gateway infers the provider from the body:
- A top-level
eventobject → PagerDuty - A top-level
alert_idfield → Datadog - Otherwise → Custom
Setting X-Webhook-Provider explicitly is recommended for predictable normalization.
Response — 200 OK
A successful ingest returns status: "success" along with the IDs you can use to find the alert later:
{ "status": "success", "message": "Webhook received and processed", "alert_id": "<alert id from your payload, or generated>", "message_id": "<id of the message created in the conversation>", "trace_id": "<reference id for support>"}Errors
| Status | When |
|---|---|
400 | Invalid channel_id format, or a body that fails validation |
401 | Missing X-Webhook-Signature, an invalid signature, or an expired X-Webhook-Timestamp |
404 | The channel is not registered, or has no conversation mapping |
429 | The channel exceeded its ingest rate limit |
Provider payloads
Section titled “Provider payloads”The gateway normalizes each provider into a common alert with severity, description, alert_id, triggered_at, and provider metadata. You send the provider’s native shape; SUPERWISE Chat does the mapping.
PagerDuty
Section titled “PagerDuty”{ "event": { "event_type": "incident.triggered", "id": "evt-001", "created_on": "2026-06-13T10:00:00Z", "data": { "id": "inc-42", "title": "API latency high", "description": "p99 latency exceeded 2s", "urgency": "high", "status": "triggered" } }}| Source field | Maps to |
|---|---|
event.data.urgency (high → high, else medium) | severity |
event.data.description (falls back to title) | description |
event.id | alert id (generated if absent) |
event.created_on | triggered-at (now, if absent) |
Datadog
Section titled “Datadog”{ "alert_id": "dd-7781", "alert_title": "Disk usage critical", "alert_status": "Triggered", "alert_transition": "Triggered", "alert_severity": "critical", "body": "host db-01 at 95% disk", "last_updated": "2026-06-13T10:05:00Z"}| Source field | Maps to |
|---|---|
alert_severity (critical→critical, error→high, warning→medium, else low) | severity |
body (falls back to alert_title) | description |
alert_id | alert id |
last_updated | triggered-at |
Custom
Section titled “Custom”For any other system, send your own structured payload:
{ "alert_type": "security_incident", "severity": "high", "description": "Unusual login pattern detected", "affected_entities": [ { "entity_type": "user", "entity_id": "user-99" } ], "alert_id": "my-system-12345", "triggered_at": "2026-06-13T10:10:00Z", "metadata": { "region": "us-east" }}| Field | Type | Notes |
|---|---|---|
alert_type | string | One of quota_threshold, usage_anomaly, compliance_violation, security_incident; defaults to security_incident |
severity | string | low, medium, high, or critical |
description | string | The alert text shown in the conversation |
affected_entities | array | Optional { entity_type, entity_id }; entity_type is user, project, conversation, or tenant |
alert_id | string | Optional; generated if omitted |
triggered_at | string | Optional ISO 8601 timestamp; defaults to now |
metadata | object | Optional free-form context |
Signing requests
Section titled “Signing requests”Compute an HMAC-SHA256 over the request body using your channel secret, hex-encode it, and send it in X-Webhook-Signature. The gateway recomputes the signature and compares it in constant time.
const crypto = require('crypto');
// `body` is the exact JSON string you send as the request body.function sign(body, secret) { return crypto.createHmac('sha256', secret).update(body).digest('hex');}
const payload = JSON.stringify({ alert_type: 'usage_anomaly', severity: 'medium', description: 'Spike detected' });const signature = sign(payload, process.env.WEBHOOK_SECRET);
await fetch('https://<your-host>/v1/webhooks/ops-alerts', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Webhook-Provider': 'custom', 'X-Webhook-Signature': signature, }, body: payload,});Replay protection
Section titled “Replay protection”Include X-Webhook-Timestamp as the current Unix time in milliseconds. Requests whose timestamp is more than 5 minutes from server time are rejected with 401. The header is optional, but enabling it on your sender hardens the channel against replayed requests.
Check channel status
Section titled “Check channel status”Confirm a channel is registered and mapped before you wire up a sender.
GET /v1/webhooks/{channel_id}/infoAuthorization: Bearer <JWT>Response — 200 OK
{ "channel_id": "ops-alerts", "registered": true, "has_conversation_mapping": true, "conversation_id": "conv-abc123", "webhook_url": "/v1/webhooks/ops-alerts"}Unknown channels — and channels that belong to another tenant — return 404 so the endpoint cannot be used to probe for existing channels.
Rotate or delete a secret
Section titled “Rotate or delete a secret”There is no in-place rotation. To rotate a secret or change the destination conversation, delete the registration and register the channel again — the re-registration mints a fresh secret. A channel can only be registered once; re-registering an existing channel returns 409.
Quick setup (PagerDuty)
Section titled “Quick setup (PagerDuty)”- Register the channel and copy the returned
secret:Terminal window curl -X POST https://<your-host>/v1/webhooks/register \-H "Authorization: Bearer $JWT" \-H "Content-Type: application/json" \-d '{"channel_id": "ops-alerts", "conversation_id": "conv-abc123"}' - In PagerDuty, add a webhook on the service and set the URL to
https://<your-host>/v1/webhooks/ops-alerts. - Configure PagerDuty to send the
secretas an HMAC-SHA256 signature inX-Webhook-Signature, and (recommended) setX-Webhook-Provider: pagerduty. - Trigger a test incident. The alert appears as a message in
conv-abc123, ready for your team to triage.
Related
Section titled “Related”- Gateway API reference — base URLs, formats, and the full endpoint surface
- Authentication — JWTs, capabilities, and API keys
- Integrate — connecting SUPERWISE Chat to your stack