Integrations

Webhook Integration

Configure custom webhook integrations to connect Pingara with any external system. Understand payload formats, event types, authentication, and testing strategies.

5 min readUpdated April 7, 2026
webhookapicustomintegration

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

  1. Go to Settings → Alert Policies → [Your Policy]
  2. Click Add Channel
  3. Select Webhook
  4. Enter your endpoint URL
  5. (Optional) Add authentication headers
  6. 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

EventTriggerIncident Field
incident.createdNew incident detected (2 consecutive failures)Full incident data
incident.resolvedIncident resolved (2 consecutive successes)Includes resolvedAt
incident.updatedIncident status changed (manual update)Updated status and notes
monitor.degradedPerformance below Apdex thresholdPerformance metrics only
monitor.recoveredPerformance returned to normalPrevious and current metrics
ssl.expiringSSL certificate approaching expiryDays remaining, expiry date
monitor.pausedMonitor was pausedNo incident data
monitor.resumedMonitor was resumedNo 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

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:

AttemptDelay
1st retry30 seconds
2nd retry2 minutes
3rd retry10 minutes
Final retry30 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:

  1. Go to webhook.site or requestbin.com
  2. Copy the unique URL
  3. Add it as a webhook endpoint in Pingara
  4. 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

  1. Check alert policy — Is the webhook channel enabled?
  2. Check triggers — Is the event type enabled (Down, Recovery, etc.)?
  3. 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/json middleware
  • Parses the JSON body correctly
  • Handles missing optional fields gracefully

Next Steps