Outgoing Webhook DispatcherHMAC SHA-256 Signed
Outgoing Webhooks & Delivery Inspector
Test real webhook receivers and event-driven architectures without waiting for third-party service webhooks. Whenever a REST or GraphQL mutation occurs inside Playground API, the server dispatches a real HTTP POST request to your application or local ngrok tunnel with cryptographic HMAC SHA-256 signatures and provides an in-browser delivery inspector with retry simulation.
Interactive Webhook Inspector Studio
Register your receiver URL, fire test pings, trigger mutations, and inspect dispatched headers, payloads, and response codes.
Webhook Dispatcher & Inspector Studio0 Active Receivers
Live HMAC SHA-256 signed event delivery inspector and retry sandbox
Recent outgoing webhook deliveries. Click any row to inspect signed headers and JSON payloads.
No webhook deliveries recorded yet. Perform a mutation (e.g. POST /posts) or click "Send Test Ping" above.
Dispatched Header Specifications
| Header Name | Example Value | Description |
|---|---|---|
| X-Playground-Signature | sha256=a8c5f...4e91 | HMAC-SHA256 signature computed over raw JSON body using your secret key. |
| X-Playground-Event | post.created | Name of the triggered event channel (e.g. post.created, auth.registered). |
| X-Playground-Delivery | del_89f1...2c90 | Unique delivery UUID for idempotency deduplication. |
| User-Agent | Playground-API-Webhook-Dispatcher/1.0 | Official dispatcher user-agent identifier. |
Receiver Verification Recipes
1. Node.js / Express Webhook Receiver
1
import express from 'express';
2
import crypto from 'crypto';
3
4
const app = express();
5
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'whsec_demo_secret_key_123';
6
7
// Capture raw body for exact cryptographic HMAC verification
8
app.post('/api/webhooks/playground', express.raw({ type: 'application/json' }), (req, res) => {
9
const signature = req.headers['x-playground-signature']; // "sha256=<hash>"
10
const event = req.headers['x-playground-event'];
11
const rawBody = req.body.toString('utf8');
12
13
// Verify HMAC SHA-256 signature
14
const expectedSignature = 'sha256=' + crypto
15
.createHmac('sha256', WEBHOOK_SECRET)
16
.update(rawBody)
17
.digest('hex');
18
19
if (signature !== expectedSignature) {
20
return res.status(401).json({ error: 'Invalid HMAC signature' });
21
}
22
23
const payload = JSON.parse(rawBody);
24
console.log(Received Webhook [${event}]:, payload.data);
25
26
// Return 200 OK to acknowledge delivery
27
res.status(200).json({ received: true });
28
});
29
30
app.listen(3000, () => console.log('Webhook receiver running on port 3000'));
2. Next.js App Router Webhook Route Handler
1
// app/api/webhooks/route.ts (Next.js App Router)
2
import { NextRequest, NextResponse } from 'next/server';
3
import crypto from 'crypto';
4
5
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'whsec_demo_secret_key_123';
6
7
export async function POST(req: NextRequest) {
8
const rawBody = await req.text();
9
const signature = req.headers.get('x-playground-signature');
10
const event = req.headers.get('x-playground-event');
11
12
const expectedSignature = 'sha256=' + crypto
13
.createHmac('sha256', WEBHOOK_SECRET)
14
.update(rawBody)
15
.digest('hex');
16
17
if (signature !== expectedSignature) {
18
return NextResponse.json({ error: 'Signature mismatch' }, { status: 401 });
19
}
20
21
const data = JSON.parse(rawBody);
22
console.log(Processed ${event} event:, data);
23
24
return NextResponse.json({ received: true });
25
}
3. Python FastAPI Receiver
1
from fastapi import FastAPI, Request, HTTPException, Header
2
import hmac
3
import hashlib
4
import json
5
6
app = FastAPI()
7
WEBHOOK_SECRET = "whsec_demo_secret_key_123"
8
9
@app.post("/api/webhooks/playground")
10
async def receive_webhook(request: Request, x_playground_signature: str = Header(None)):
11
raw_body = await request.body()
12
13
expected_sig = "sha256=" + hmac.new(
14
WEBHOOK_SECRET.encode(), raw_body, hashlib.sha256
15
).hexdigest()
16
17
if not hmac.compare_digest(x_playground_signature or "", expected_sig):
18
raise HTTPException(status_code=401, detail="Invalid signature")
19
20
payload = json.loads(raw_body)
21
print(f"Received event {payload['event']}: {payload['data']}")
22
return {"status": "ok"}
Supported Event Catalog
post.*Posts Resource Events
- • post.created — Triggered on POST /posts
- • post.updated — Triggered on PUT / PATCH /posts/:id
- • post.deleted — Triggered on DELETE /posts/:id
auth.*Authentication Events
- • auth.registered — Triggered on POST /auth/register
- • auth.login — Triggered on POST /auth/login
custom.*Custom Dynamic Collections
- • custom.<collection>.created — POST /custom/:collection
- • custom.<collection>.updated — PUT / PATCH /custom/:collection/:id
- • custom.<collection>.deleted — DELETE /custom/:collection/:id
user.* / comment.*Users, Comments & Todos
- • user.created / user.updated / user.deleted
- • comment.created / comment.updated / comment.deleted
- • todo.created / todo.updated / todo.deleted