Webhooks
Assert sends an HTTP POST to your endpoint when a monitor fails, recovers, or slows past its threshold. The payload carries the assertion that failed, the expected value, the value your API returned, the region that ran the check, and the time. Route it into whatever you already run.
A failure re-runs across regions before the POST goes out. Assert sends one alert per incident and posts a monitor.recovered event when the assertion passes again. Routing is per monitor. Webhooks are on every paid plan.
Setup Guide
Step 1: Create Your Endpoint
Create an HTTP endpoint that accepts POST requests:
// Express.js example
app.post('/webhooks/ApiAssert', (req, res) => {
const payload = req.body;
console.log('Alert received:', payload.event);
console.log('Monitor:', payload.monitor.name);
// Process the alert...
res.status(200).send('OK');
});
Step 2: Add Webhook in Assert
- Go to Alerts → Channels in your Assert dashboard
- Click Add Channel → Webhook
- Enter your endpoint URL
- Add authentication headers if your endpoint requires them
- Click Save
Step 3: Test the Webhook
Click Send Test. Assert posts a sample payload to your endpoint.
Step 4: Assign to Monitors
Open each monitor. Add the webhook channel under Alert Channels.
Payload Format
Assert sends JSON payloads with this structure:
{
"event": "monitor.failed",
"timestamp": "2024-12-11T14:34:00Z",
"monitor": {
"id": "mon_abc123",
"name": "Production API",
"url": "https://api.example.com/health",
"method": "GET",
"interval": 60
},
"check": {
"id": "chk_xyz789",
"status_code": 500,
"response_time_ms": 2340,
"region": "us-east",
"checked_at": "2024-12-11T14:34:00Z"
},
"failure": {
"type": "assertion",
"message": "$.status expected 'success', got 'error'",
"assertion": {
"path": "$.status",
"operator": "equals",
"expected": "success",
"actual": "error"
}
},
"alert": {
"id": "alt_def456",
"state": "triggered",
"consecutive_failures": 3
}
}
Event Types
| Event | Description |
|---|---|
monitor.failed |
The monitor fails a check |
monitor.recovered |
The monitor passes again |
monitor.degraded |
The response time exceeds your threshold |
test |
You send a test from the dashboard |
Authentication
Bearer Token
Add a Bearer token header:
Header: Authorization
Value: Bearer your-secret-token
Custom Headers
Add any custom headers your endpoint requires:
X-API-Key: your-api-key
X-Webhook-Source: ApiAssert
Signature Verification
Assert signs every payload with HMAC-SHA256. Check the signature before you trust the request:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your handler:
const signature = req.headers['x-ApiAssert-signature'];
if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
Use Cases
Trigger CI/CD Pipeline
Restart services when health checks fail:
# GitHub Actions workflow
on:
repository_dispatch:
types: [ApiAssert-alert]
jobs:
restart:
runs-on: ubuntu-latest
steps:
- run: ./scripts/restart-service.sh
Update Status Page
Post to your status page when monitors fail:
app.post('/webhooks/ApiAssert', async (req, res) => {
if (req.body.event === 'monitor.failed') {
await statusPage.createIncident({
name: `${req.body.monitor.name} is down`,
status: 'investigating'
});
}
res.send('OK');
});
Forward to Slack in Your Own Format
Transform the payload and post it to Slack:
app.post('/webhooks/ApiAssert', async (req, res) => {
const { monitor, check, failure } = req.body;
await fetch(process.env.SLACK_WEBHOOK, {
method: 'POST',
body: JSON.stringify({
blocks: [
{
type: 'header',
text: { type: 'plain_text', text: `🚨 ${monitor.name}` }
},
{
type: 'section',
text: { type: 'mrkdwn', text: failure.message }
}
]
})
});
res.send('OK');
});
Retry Policy
Assert retries a failed delivery, up to 3 attempts in total, with exponential backoff of 10s, 30s, 90s. Each request times out after 10 seconds. Any 2xx response code counts as a success.
Troubleshooting
Not receiving webhooks?
- Check that your endpoint is reachable from the public internet
- Check that your firewall allows incoming POST requests
- Check the URL, including the protocol
Getting 4xx/5xx errors?
- Check your endpoint logs
- Check the authentication headers
- Check that your endpoint returns 2xx on success
Payload not what you expected?
- Use Send Test to see the exact payload format
- Check the API changelog for recent changes
Next Steps
- Set up Slack for team notifications
- Configure PagerDuty for on-call escalation
- Explore response validation