webhook-tester

from curiouslearner/devkit

Comprehensive development toolkit: 52 professional skills for Claude Code across development, code quality, API, database, security, DevOps, data analytics, and collaboration

19 stars4 forksUpdated Oct 20, 2025
npx skills add https://github.com/curiouslearner/devkit --skill webhook-tester

SKILL.md

Webhook Tester Skill

Test webhook integrations locally with tunneling, inspection, and debugging tools.

Instructions

You are a webhook testing expert. When invoked:

  1. Local Webhook Testing:

    • Set up local webhook receivers
    • Expose localhost to internet using tunnels
    • Capture and inspect webhook payloads
    • Verify webhook signatures
    • Test retry mechanisms
  2. Debugging Webhooks:

    • Inspect request headers and body
    • Validate webhook signatures
    • Test different payload formats
    • Simulate webhook failures
    • Log and replay webhooks
  3. Integration Testing:

    • Test webhook delivery
    • Verify idempotency
    • Test retry logic
    • Validate error handling
    • Performance testing
  4. Security Validation:

    • Verify signature validation
    • Test HTTPS requirements
    • Validate origin checking
    • Test replay attack prevention

Usage Examples

@webhook-tester
@webhook-tester --setup-tunnel
@webhook-tester --inspect
@webhook-tester --verify-signature
@webhook-tester --replay

Tunneling Tools

ngrok (Most Popular)

Basic Setup

# Install ngrok
# Download from https://ngrok.com/download
# Or use package manager
brew install ngrok/ngrok/ngrok  # macOS
choco install ngrok             # Windows

# Authenticate (get token from ngrok.com)
ngrok config add-authtoken YOUR_TOKEN

# Start tunnel to localhost:3000
ngrok http 3000

# Custom subdomain (requires paid plan)
ngrok http 3000 --subdomain=myapp

# Multiple ports
ngrok http 3000 3001

# Use specific region
ngrok http 3000 --region=us

# Enable inspection UI
ngrok http 3000 --inspect=true

ngrok Configuration File

# ~/.ngrok2/ngrok.yml
version: "2"
authtoken: YOUR_TOKEN

tunnels:
  api:
    addr: 3000
    proto: http
    subdomain: myapi

  webhooks:
    addr: 4000
    proto: http
    subdomain: webhooks

  web:
    addr: 8080
    proto: http
    bind_tls: true

# Start all tunnels
ngrok start --all

# Start specific tunnel
ngrok start api

ngrok API

// Using ngrok programmatically
const ngrok = require('ngrok');

async function startTunnel() {
  const url = await ngrok.connect({
    addr: 3000,
    region: 'us',
    onStatusChange: status => console.log('Status:', status)
  });

  console.log('Tunnel URL:', url);
  // Use this URL as webhook endpoint
  return url;
}

// Cleanup
async function stopTunnel() {
  await ngrok.disconnect();
  await ngrok.kill();
}

Cloudflare Tunnel (Free, No Account Required)

# Install
brew install cloudflare/cloudflare/cloudflared  # macOS
# Or download from cloudflare.com

# Quick tunnel (no auth required)
cloudflared tunnel --url http://localhost:3000

# Output will be: https://random-words.trycloudflare.com

localtunnel

# Install
npm install -g localtunnel

# Start tunnel
lt --port 3000

# Custom subdomain (may not be available)
lt --port 3000 --subdomain myapp

# Use localtunnel programmatically
const localtunnel = require('localtunnel');

const tunnel = await localtunnel({ port: 3000 });
console.log('Tunnel URL:', tunnel.url);

tunnel.on('close', () => {
  console.log('Tunnel closed');
});

VS Code Port Forwarding

# In VS Code with GitHub account
# 1. Open Terminal
# 2. Click "Ports" tab
# 3. Click "Forward a Port"
# 4. Enter port number (e.g., 3000)
# 5. Share the public URL

Webhook Receiver Setup

Express.js Webhook Endpoint

const express = require('express');
const crypto = require('crypto');

const app = express();

// Raw body parser for signature verification
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString();
  }
}));

// Webhook endpoint
app.post('/webhooks/github', (req, res) => {
  console.log('Received webhook from GitHub');
  console.log('Headers:', req.headers);
  console.log('Body:', req.body);

  // Verify signature
  const signature = req.headers['x-hub-signature-256'];
  const secret = process.env.WEBHOOK_SECRET;

  if (!verifyGitHubSignature(req.rawBody, signature, secret)) {
    console.error('Invalid signature');
    return res.status(401).send('Invalid signature');
  }

  // Process webhook
  const event = req.headers['x-github-event'];
  handleGitHubEvent(event, req.body);

  // Always respond quickly (GitHub expects response within 10s)
  res.status(200).send('OK');
});

function verifyGitHubSignature(payload, signature, secret) {
  if (!signature) return false;

  const hmac = crypto.createHmac('sha256', secret);
  const digest = 'sha256=' + hmac.update(payload).digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(digest)
  );
}

function handleGitHubEvent(event, payload) {
  switch (event) {
    case 'push':
      console.log('Push event:', payload.ref);
      break;
    case 'pull_request':
      console.log('PR event:', payload.action);
      break;
    default:
      console.log('Unhandled event:', event);
  }
}

// Stripe we

...
Read full content

Repository Stats

Stars19
Forks4
LicenseMIT License