Webhook Integration
Configure custom webhook integrations to connect Pingara with any external system. Understand payload formats, event types, authentication, and testing strategies.
Webhooks let you connect Pingara with any system that can receive HTTP POST requests — custom dashboards, ticketing systems, ChatOps bots, automation platforms, or your own backend services.
How Webhooks Work
When an alert condition is met, Pingara sends an HTTP POST request to your configured endpoint with a JSON payload containing all incident details.
Pingara detects incident
↓
POST https://your-endpoint.com/pingara-webhook
Content-Type: application/json
↓
Your system receives and processes the event
Setting Up a Webhook
Step 1: Prepare Your Endpoint
Create an HTTP endpoint that:
- Accepts POST requests
- Parses JSON request bodies
- Returns a 2xx status code on success
- Is publicly accessible from the internet
Example (Node.js/Express):
app.post('/pingara-webhook', (req, res) => {
const event = req.body;
console.log('Pingara event:', event.event, event.monitor.name);
// Process the event...
res.status(200).json({ received: true });
});
Step 2: Add the Webhook in Pingara
- Go to Settings → Alert Policies → [Your Policy]
- Click Add Channel
- Select Webhook
- Enter your endpoint URL
- (Optional) Add authentication headers
- Click Save
Step 3: Test the Webhook
Click Send Test to send a sample event to your endpoint. Verify:
- Your endpoint receives the request
- The JSON payload is parsed correctly
- Your endpoint returns 200 OK
Payload Format
Standard Payload Structure
Every webhook event follows this structure:
{
"event": "incident.created",
"timestamp": "2024-01-15T14:32:05.000Z",
"monitor": {
"id": "m_abc123def456",
"name": "API Production",
"url": "https://api.example.com/health",
"status": "down",
"region": "us-east-1"
},
"incident": {
"id": "inc_789ghi012",
"status": "investigating",
"startedAt": "2024-01-15T14:32:00.000Z",
"resolvedAt": null,
"errorType": "timeout",
"errorMessage": "Connection timeout after 30000ms",
"affectedRegions": ["us-east-1", "eu-west-1"],
"metrics": {
"dnsLookupTime": 45,
"tcpConnectTime": 30120,
"tlsHandshakeTime": 0,
"ttfb": 0,
"totalDuration": 30165,
"statusCode": null,
"responseSize": 0
}
},
"organization": {
"id": "org_xyz789",
"name": "Acme Corp"
}
}
Event Types
| Event | Trigger | Incident Field |
|---|---|---|
incident.created | New incident detected (2 consecutive failures) | Full incident data |
incident.resolved | Incident resolved (2 consecutive successes) | Includes resolvedAt |
incident.updated | Incident status changed (manual update) | Updated status and notes |
monitor.degraded | Performance below Apdex threshold | Performance metrics only |
monitor.recovered | Performance returned to normal | Previous and current metrics |
ssl.expiring | SSL certificate approaching expiry | Days remaining, expiry date |
monitor.paused | Monitor was paused | No incident data |
monitor.resumed | Monitor was resumed | No incident data |
Event-Specific Payloads
incident.resolved:
{
"event": "incident.resolved",
"timestamp": "2024-01-15T14:44:00.000Z",
"monitor": {
"id": "m_abc123def456",
"name": "API Production",
"url": "https://api.example.com/health",
"status": "up"
},
"incident": {
"id": "inc_789ghi012",
"status": "resolved",
"startedAt": "2024-01-15T14:32:00.000Z",
"resolvedAt": "2024-01-15T14:44:00.000Z",
"duration": 720
}
}
ssl.expiring:
{
"event": "ssl.expiring",
"timestamp": "2024-01-15T00:00:00.000Z",
"monitor": {
"id": "m_abc123def456",
"name": "API Production",
"url": "https://api.example.com"
},
"ssl": {
"daysUntilExpiry": 14,
"expiresAt": "2024-01-29T23:59:59.000Z",
"issuer": "Let's Encrypt Authority X3"
}
}
Authentication
Shared Secret (Recommended)
Add a shared secret as a custom header to verify webhook authenticity:
In Pingara:
Header: X-Pingara-Secret
Value: your-secret-token-here
In your endpoint:
app.post('/pingara-webhook', (req, res) => {
const secret = req.headers['x-pingara-secret'];
if (secret !== process.env.PINGARA_WEBHOOK_SECRET) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Process the verified event...
res.status(200).json({ received: true });
});
Bearer Token
Use a Bearer token for OAuth-compatible systems:
In Pingara:
Header: Authorization
Value: Bearer your-access-token
Basic Auth
Encode credentials in the URL:
https://user:password@your-endpoint.com/pingara-webhook
Warning: Basic auth in URLs is less secure. Use header-based authentication when possible.
Retry Behavior
If your endpoint fails to respond with a 2xx status code, Pingara retries the webhook:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 10 minutes |
| Final retry | 30 minutes |
After all retries fail, the notification is marked as failed in the notification history.
Handling Retries
Your endpoint should be idempotent — processing the same event twice should be safe. Use the incident.id and event fields to deduplicate:
const processedEvents = new Set();
app.post('/pingara-webhook', (req, res) => {
const eventKey = `${req.body.incident?.id}-${req.body.event}`;
if (processedEvents.has(eventKey)) {
return res.status(200).json({ status: 'already_processed' });
}
processedEvents.add(eventKey);
// Process the event...
res.status(200).json({ received: true });
});
Common Integration Patterns
Create a Ticket on Incident
app.post('/pingara-webhook', async (req, res) => {
if (req.body.event === 'incident.created') {
await ticketSystem.create({
title: `[Pingara] ${req.body.monitor.name} is down`,
description: req.body.incident.errorMessage,
priority: 'high',
tags: ['monitoring', 'auto-created'],
});
}
res.status(200).json({ received: true });
});
Post to a Custom Dashboard
app.post('/pingara-webhook', async (req, res) => {
await dashboard.addEvent({
source: 'pingara',
type: req.body.event,
monitor: req.body.monitor.name,
timestamp: req.body.timestamp,
metrics: req.body.incident?.metrics,
});
res.status(200).json({ received: true });
});
Trigger an Automated Remediation
app.post('/pingara-webhook', async (req, res) => {
if (req.body.event === 'incident.created') {
// Automatically restart the service
await cloudProvider.restartService(req.body.monitor.name);
console.log('Auto-remediation triggered for', req.body.monitor.name);
}
res.status(200).json({ received: true });
});
Testing Webhooks
Using the Test Button
Pingara's Send Test button sends a realistic sample event to verify connectivity. The test payload uses dummy data but follows the exact same format as real events.
Using a Request Catcher
For development, use a request inspection tool:
- Go to webhook.site or requestbin.com
- Copy the unique URL
- Add it as a webhook endpoint in Pingara
- Send a test and inspect the full request
Local Development
Use a tunnel service like ngrok to test webhooks locally:
ngrok http 3000
# Use the generated https URL as your webhook endpoint
Troubleshooting
Webhook Not Firing
- Check alert policy — Is the webhook channel enabled?
- Check triggers — Is the event type enabled (Down, Recovery, etc.)?
- Check monitor link — Is the policy linked to the monitor?
Receiving 4xx/5xx Errors
- 401/403 — Authentication failed. Verify your secret/token.
- 404 — Endpoint URL is wrong. Check the path.
- 500 — Your server has a bug. Check server logs.
- Timeout — Your endpoint is too slow. Respond within 10 seconds.
Payload Parsing Errors
Ensure your endpoint:
- Sets
Content-Type: application/jsonmiddleware - Parses the JSON body correctly
- Handles missing optional fields gracefully
Next Steps
- Slack Integration — Pre-built Slack notifications
- Alert Channels — All available notification channels
- Setting Up Alerts — Configure alert policies