Webhooks API Reference
See Webhook Alerts → for setup instructions. This page covers the full technical reference.
Event Types
| Event | Trigger |
|---|---|
incident.created | Monitor failure threshold exceeded |
incident.acknowledged | Team member acknowledges an incident |
incident.resolved | Monitor recovery threshold exceeded |
monitor.paused | Monitor manually paused |
monitor.resumed | Monitor manually resumed |
ssl.expiry_warning | SSL cert expiry within warning threshold |
ssl.expiry_critical | SSL cert expiry within critical threshold |
Full Payload Schema
interface PingSLAWebhookPayload {
event: string;
timestamp: string; // ISO 8601 UTC
webhookId: string; // Delivery ID for idempotency
incident?: {
id: string; // e.g. "inc_abc123"
url: string; // Dashboard link
status: 'open' | 'acknowledged' | 'resolved';
createdAt: string;
acknowledgedAt?: string;
resolvedAt?: string;
durationSeconds?: number;
};
monitor: {
id: string; // e.g. "mon_xyz456"
name: string;
type: 'http' | 'ping' | 'keyword' | 'ssl';
url?: string; // For HTTP/keyword monitors
hostname?: string; // For ping/ssl monitors
};
failure?: {
reason: string; // Human-readable failure description
statusCode?: number; // For HTTP monitors
responseTime?: number; // ms, null if timed out
region: string; // Probe region that triggered
keyword?: string; // For keyword monitors
};
ssl?: {
expiresAt: string;
daysRemaining: number;
issuer: string;
};
workspace: {
id: string;
name: string;
};
}
Signature Verification
POST /your-webhook-endpoint HTTP/1.1
Content-Type: application/json
X-PingSLA-Signature: sha256=<hmac_hex>
X-PingSLA-Timestamp: <unix_timestamp>
X-PingSLA-Event: incident.created
X-PingSLA-Webhook-ID: whd_xxxxxxxxx
Verification algorithm (Node.js):
import crypto from 'crypto';
export function verifyWebhook(rawBody, headers, secret) {
const timestamp = headers['x-pingsla-timestamp'];
const signature = headers['x-pingsla-signature'];
// Reject requests older than 5 minutes (replay attack prevention)
const age = Math.abs(Date.now() / 1000 - parseInt(timestamp, 10));
if (age > 300) throw new Error('Webhook timestamp too old');
const payload = `${timestamp}.${rawBody}`;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
throw new Error('Invalid webhook signature');
}
}
// Express example
app.post('/webhooks/pingsla', express.raw({ type: 'application/json' }), (req, res) => {
try {
verifyWebhook(req.body, req.headers, process.env.PINGSLA_WEBHOOK_SECRET);
const event = JSON.parse(req.body);
// Handle event
res.sendStatus(200);
} catch (err) {
res.status(400).json({ error: err.message });
}
});
Verification algorithm (Python):
import hmac, hashlib, time
def verify_webhook(raw_body: bytes, headers: dict, secret: str) -> None:
timestamp = headers.get('x-pingsla-timestamp', '')
signature = headers.get('x-pingsla-signature', '')
age = abs(time.time() - float(timestamp))
if age > 300:
raise ValueError('Webhook timestamp too old')
payload = f"{timestamp}.{raw_body.decode('utf-8')}"
expected = 'sha256=' + hmac.new(
secret.encode(), payload.encode(), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
raise ValueError('Invalid webhook signature')
Idempotency
The webhookId field in every payload is a unique delivery ID. Use it to deduplicate retries on your side:
const seenWebhooks = new Set();
function handleWebhook(payload) {
if (seenWebhooks.has(payload.webhookId)) return; // duplicate
seenWebhooks.add(payload.webhookId);
// process ...
}
Response Requirements
Your endpoint must:
- Return HTTP
2xxwithin 10 seconds - Return
2xxeven if you plan to process the event asynchronously - Not return
3xxredirects (PingSLA does not follow redirects for webhooks)
Non-2xx responses trigger the retry schedule.
Testing Webhooks
Use webhook.site or requestbin.com to inspect webhook payloads during development.
From the PingSLA dashboard: Settings → Notification Channels → [your webhook] → Test sends a incident.created test payload.