Claude Skills Guide

Claude Code Webhook Handler Implementation Workflow Guide

Webhook handlers form the backbone of event-driven architectures. Whether you’re processing payments from Stripe, receiving GitHub push notifications, or handling Twilio SMS callbacks, reliable webhook implementation requires careful attention to security, validation, error handling, and idempotency. This guide walks you through implementing webhook handlers using Claude Code, leveraging skills like the tdd skill for test-driven development and the supermemory skill for maintaining context across complex implementations.

Why Claude Code Excels at Webhook Handler Development

Claude Code brings several advantages to webhook handler implementation. The ability to read your existing codebase, understand your project conventions, and generate appropriate code patterns makes it particularly effective for this task. Unlike generic code generation, Claude Code can adapt to your specific patterns—whether you follow Express middleware conventions, FastAPI dependencies, or Ruby on Rails controllers.

When implementing webhook handlers, you’ll typically work across multiple files: the handler itself, signature verification utilities, event routing logic, and tests. Claude Code maintains context across these files, ensuring consistency and reducing the mental overhead of managing complex implementations.

Setting Up Your Webhook Handler Project

Before implementing webhook handlers, ensure your project has the right structure. A typical webhook handler project includes:

  1. Endpoint definition - where the webhook receives requests
  2. Signature verification - validating the request authenticity
  3. Event routing - directing events to appropriate handlers
  4. Business logic - processing specific event types
  5. Error handling - managing failures and retries

Here’s a basic Express.js webhook endpoint structure:

// routes/webhooks.js
const express = require('express');
const router = express.Router();
const verifySignature = require('../utils/verify-signature');
const eventRouter = require('../handlers/event-router');

router.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  try {
    const signature = req.headers['x-webhook-signature'];
    
    if (!verifySignature(req.body, signature)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    const event = JSON.parse(req.body);
    await eventRouter.route(event);
    
    res.status(200).json({ received: true });
  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(500).json({ error: 'Processing failed' });
  }
});

module.exports = router;

Notice the use of express.raw() for the body parser. This is critical—it prevents Express from parsing the body prematurely, which would interfere with signature verification.

Implementing Signature Verification

Signature verification prevents attackers from spoofing webhook events. Most webhook providers use HMAC signatures with a shared secret. Here’s a verification utility:

// utils/verify-signature.js
const crypto = require('crypto');

function verifySignature(body, signature, secret) {
  if (!signature || !secret) {
    return false;
  }
  
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

module.exports = verifySignature;

The timingSafeEqual function protects against timing attacks by ensuring the comparison takes constant time regardless of where the mismatch occurs.

When working with Claude Code, you can prompt it to add support for different signature algorithms or adapt the verification logic for providers that use different schemes—like GitHub’s format or Twilio’s custom headers.

Building the Event Router

The event router directs incoming events to appropriate handlers based on event type. This pattern keeps your code organized as you add support for more event types:

// handlers/event-router.js
const handlers = {
  'payment.succeeded': handlePaymentSucceeded,
  'payment.failed': handlePaymentFailed,
  'subscription.created': handleSubscriptionCreated,
  'customer.updated': handleCustomerUpdated,
};

async function route(event) {
  const handler = handlers[event.type];
  
  if (!handler) {
    console.warn(`No handler for event type: ${event.type}`);
    return;
  }
  
  try {
    await handler(event.data);
  } catch (error) {
    console.error(`Handler error for ${event.type}:`, error);
    throw error; // Re-throw to trigger retry
  }
}

module.exports = { route };

Each handler function contains the business logic for processing specific events. For example:

// handlers/payment-handlers.js
async function handlePaymentSucceeded(paymentData) {
  const { id, amount, customer_id } = paymentData;
  
  // Update order status
  await updateOrderByPaymentId(id, { status: 'paid' });
  
  // Send confirmation email
  await sendEmail(customer_id, 'payment-confirmation', { amount });
  
  // Update analytics
  await analytics.track('payment_completed', { amount, customer_id });
}

Handling Errors and Retries

Webhook providers typically expect a 2xx response within a reasonable timeframe. If your handler fails, you need a retry strategy. Most providers implement automatic retries with exponential backoff, but you should also implement idempotency in your handlers to prevent duplicate processing.

The idempotency pattern involves tracking processed events:

// utils/idempotency.js
const processedEvents = new Map();

async function withIdempotency(eventId, handler) {
  if (processedEvents.has(eventId)) {
    console.log(`Event ${eventId} already processed`);
    return;
  }
  
  try {
    await handler();
    processedEvents.set(eventId, Date.now());
    
    // Clean up old entries periodically
    if (processedEvents.size > 10000) {
      const cutoff = Date.now() - 24 * 60 * 60 * 1000;
      for (const [id, timestamp] of processedEvents) {
        if (timestamp < cutoff) processedEvents.delete(id);
      }
    }
  } catch (error) {
    processedEvents.delete(eventId);
    throw error;
  }
}

For production systems, replace the in-memory Map with Redis or a database to maintain idempotency across restarts and across multiple server instances.

Testing Webhook Handlers

The tdd skill integrates well with webhook testing. Write tests that cover:

  1. Signature verification - valid and invalid signatures
  2. Event routing - correct handler selection
  3. Handler behavior - business logic execution
  4. Error cases - network failures, validation errors
  5. Idempotency - duplicate event handling

Here’s a test example using Jest:

// tests/webhooks.test.js
const request = require('supertest');
const app = require('../app');
const { generateSignature } = require('../utils/test-utils');

describe('Webhook endpoint', () => {
  const validPayload = JSON.stringify({
    type: 'payment.succeeded',
    data: { id: 'evt_123', amount: 5000 }
  });
  
  it('accepts valid signature', async () => {
    const signature = generateSignature(validPayload, 'test-secret');
    
    const response = await request(app)
      .post('/webhook')
      .set('x-webhook-signature', signature)
      .send(validPayload);
    
    expect(response.status).toBe(200);
  });
  
  it('rejects invalid signature', async () => {
    const response = await request(app)
      .post('/webhook')
      .set('x-webhook-signature', 'invalid')
      .send(validPayload);
    
    expect(response.status).toBe(401);
  });
  
  it('routes payment.succeeded events', async () => {
    const signature = generateSignature(validPayload, 'test-secret');
    
    await request(app)
      .post('/webhook')
      .set('x-webhook-signature', signature)
      .send(validPayload);
    
    // Verify handler was called
    expect(handlePaymentSucceeded).toHaveBeenCalledWith(
      expect.objectContaining({ id: 'evt_123' })
    );
  });
});

Integrating Claude Skills for Enhanced Development

Several Claude skills complement webhook handler development. The pdf skill helps generate API documentation from your handler specifications. When documenting webhook events for team members, you can use the skill to create comprehensive documentation files.

The supermemory skill proves valuable when implementing webhooks for multiple providers—you can maintain context about each provider’s quirks and your implementation decisions across sessions.

For frontend integration, the frontend-design skill helps build admin dashboards to monitor webhook events, display processing status, and handle retry logic through a user interface.

Stripe-Specific Implementation

Stripe uses a timestamp-based signature scheme with replay attack prevention. Parse the Stripe-Signature header and validate within a tolerance window:

function verifyStripeSignature(payload, signature, secret, tolerance = 300) {
  const timestamp = signature.split(',')[0].split('=')[1];
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = createHmac('sha256', secret)
    .update(signedPayload).digest('hex');

  const currentTime = Math.floor(Date.now() / 1000);
  if (Math.abs(currentTime - parseInt(timestamp)) > tolerance) {
    throw new Error('Webhook timestamp outside tolerance window');
  }
  // Compare signatures...
}

Use the Stripe CLI for local development testing:

stripe listen --forward-to localhost:3000/webhooks
stripe trigger payment_intent.succeeded

Handle Stripe-specific event types including payment_intent.succeeded, customer.subscription.updated, and charge.dispute.created for chargeback scenarios. Always use test mode API keys in development — never mix test and live keys.

Best Practices Summary

Implementing reliable webhook handlers requires attention to several key areas:

Claude Code streamlines this entire workflow by generating boilerplate, suggesting improvements, and helping you write thorough tests. The context-aware nature of Claude Code means it understands your project structure and can adapt implementations to match your existing patterns.

With this workflow guide, you’re equipped to implement webhook handlers that are secure, reliable, and maintainable. The combination of proper architecture, thorough testing, and Claude Code’s assistance ensures your webhook integrations stand up to production demands.

Built by theluckystrike — More at zovo.one