home / skills / willsigmon / sigstack / webhook-expert
This skill helps you design and implement secure, scalable webhook automation with real-time event handling, retries, and verified payload processing.
npx playbooks add skill willsigmon/sigstack --skill webhook-expertReview the files below or copy the command above to add this skill to your agents.
---
name: Webhook Expert
description: Webhook automation - real-time triggers, payload handling, retry logic, security
allowed-tools: Read, Edit, Bash
model: sonnet
---
# Webhook Automation Expert
Master webhook-driven automation for real-time integrations.
## Key Concepts
- Event-driven, push-based
- Eliminates polling
- Real-time execution
- Lightweight and scalable
## Testing Webhooks
### Webhook.site
```bash
# Get test URL at webhook.site
# Send test payload
curl -X POST https://webhook.site/your-uuid \
-H "Content-Type: application/json" \
-d '{"event": "test", "data": {"key": "value"}}'
```
### ngrok for Local Testing
```bash
ngrok http 3000
# Exposes localhost:3000 to public URL
```
## Webhook Patterns
### Express Handler
```typescript
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
// Verify webhook signature
function verifySignature(payload: string, signature: string, secret: string): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-hub-signature-256'] as string;
if (!verifySignature(JSON.stringify(req.body), signature, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send('Invalid signature');
}
// Process webhook
console.log('Event:', req.body.event);
// Respond quickly, process async
res.status(200).send('OK');
processWebhookAsync(req.body);
});
```
### Retry Logic
```typescript
async function sendWebhook(url: string, payload: object, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (response.ok) return true;
if (response.status >= 500) {
// Server error, retry with exponential backoff
await sleep(Math.pow(2, attempt) * 1000);
continue;
}
return false; // Client error, don't retry
} catch (error) {
if (attempt === maxRetries) throw error;
await sleep(Math.pow(2, attempt) * 1000);
}
}
return false;
}
```
## Security Best Practices
1. Always verify signatures
2. Use HTTPS only
3. Validate payload schema
4. Implement idempotency
5. Rate limit incoming requests
6. Log all webhook activity
## Webhook-as-a-Service
### Hook0 (Open Source)
- Self-hostable
- Automatic retries
- Fine-grained subscriptions
- REST API
Use when: Real-time integrations, event-driven automation, third-party callbacks
This skill teaches webhook automation for real-time integration, payload handling, retry logic, and security hardening. It focuses on lightweight, scalable event-driven patterns that replace polling and enable immediate reactions to events. The content includes local testing tips, server-side handlers, retry strategies, and operational best practices.
It inspects webhook flow from receiver verification to asynchronous processing and delivery retries. The skill demonstrates how to verify signatures, respond quickly to senders, and perform background processing to avoid timeouts. It also covers sending webhooks with exponential backoff, idempotency, and logging to ensure reliable delivery and observability.
How do I test webhooks locally?
Use ngrok to expose localhost or webhook.site to get a public URL. Send sample payloads with curl and inspect requests for headers and body.
When should I retry versus drop a delivery?
Retry on transient errors (5xx or network failures) with exponential backoff. Do not retry on client errors (4xx) unless you know the error is temporary.
How do I avoid processing duplicates?
Require idempotency keys in payloads or store event IDs and ignore repeats. Combine idempotency with at-least-once delivery semantics to ensure safe retries.