webhooks

Webhook design patterns — delivery, retry with exponential backoff, HMAC signature verification, payload validation, idempotency keys

39 stars

Best use case

webhooks is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Webhook design patterns — delivery, retry with exponential backoff, HMAC signature verification, payload validation, idempotency keys

Teams using webhooks should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/webhooks/SKILL.md --create-dirs "https://raw.githubusercontent.com/InugamiDev/ultrathink-oss/main/.claude/skills/webhooks/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/webhooks/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How webhooks Compares

Feature / AgentwebhooksStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Webhook design patterns — delivery, retry with exponential backoff, HMAC signature verification, payload validation, idempotency keys

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Webhooks Skill

## Purpose

Design patterns for sending and receiving webhooks reliably: HMAC signature verification, exponential backoff retries, idempotency, and payload validation.

## Receiving: Signature Verification

```typescript
import { createHmac, timingSafeEqual } from 'crypto';

function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {
  const expected = `sha256=${createHmac('sha256', secret).update(payload).digest('hex')}`;
  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(signature, 'utf8');
  return a.length === b.length && timingSafeEqual(a, b);
}
```

## Receiving: Handler with Idempotency

```typescript
// app/api/webhooks/route.ts
export async function POST(req: NextRequest) {
  const rawBody = await req.text();
  const signature = req.headers.get('x-webhook-signature') ?? '';

  if (!verifyWebhookSignature(rawBody, signature, process.env.WEBHOOK_SECRET!)) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }

  const event = webhookEventSchema.parse(JSON.parse(rawBody));
  const idempotencyKey = event.id;

  // Skip if already processed
  const existing = await db.webhookEvent.findUnique({ where: { idempotencyKey } });
  if (existing) return NextResponse.json({ status: 'already_processed' });

  try {
    await processWebhookEvent(event);
    await db.webhookEvent.create({
      data: { idempotencyKey, eventType: event.type, processedAt: new Date() },
    });
    return NextResponse.json({ status: 'processed' });
  } catch {
    return NextResponse.json({ error: 'Processing failed' }, { status: 500 });
  }
}
```

## Receiving: Payload Validation

```typescript
import { z } from 'zod';

const webhookEventSchema = z.discriminatedUnion('type', [
  z.object({
    type: z.literal('order.created'),
    id: z.string().uuid(),
    data: z.object({ orderId: z.string(), amount: z.number().positive() }),
  }),
  z.object({
    type: z.literal('order.cancelled'),
    id: z.string().uuid(),
    data: z.object({ orderId: z.string(), reason: z.string().optional() }),
  }),
]);
```

## Sending: Delivery with Exponential Backoff

```typescript
async function deliverWebhook(url: string, secret: string, event: WebhookEvent) {
  const payload = JSON.stringify(event);
  const signature = `sha256=${createHmac('sha256', secret).update(payload).digest('hex')}`;

  for (let attempt = 0; attempt <= 5; attempt++) {
    try {
      const res = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Webhook-Signature': signature,
          'X-Webhook-Id': event.id,
        },
        body: payload,
        signal: AbortSignal.timeout(10_000),
      });
      if (res.ok) return { success: true, attempts: attempt + 1 };
      if (res.status >= 400 && res.status < 500 && res.status !== 429) {
        return { success: false, attempts: attempt + 1 }; // Don't retry 4xx
      }
    } catch { /* network error, will retry */ }

    if (attempt < 5) {
      const delay = Math.min(1000 * Math.pow(2, attempt) + Math.random() * 1000, 30_000);
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  return { success: false, attempts: 6 };
}
```

## Best Practices

1. **Always verify signatures** with timing-safe comparison before processing
2. **Always use idempotency keys** — webhooks may be delivered more than once
3. **Respond 200 quickly**, then process asynchronously for heavy work
4. **Don't retry on 4xx** (except 429) — the payload itself is the problem
5. **Include timestamps** in signed payloads to prevent replay attacks
6. **Use a job queue** (BullMQ, SQS) for production delivery instead of in-process retries
7. **Log all events** with idempotency keys for debugging delivery issues

Related Skills

ultrathink

39
from InugamiDev/ultrathink-oss

UltraThink Workflow OS — 4-layer skill mesh with persistent memory and privacy hooks for complex engineering tasks. Routes prompts through intent detection to activate the right domain skills automatically.

ultrathink_review

39
from InugamiDev/ultrathink-oss

Multi-pass code review powered by UltraThink's quality gate — checks correctness, security (OWASP), performance, readability, and project conventions in a single structured pass.

ultrathink_memory

39
from InugamiDev/ultrathink-oss

Persistent memory system for UltraThink — search, save, and recall project context, decisions, and patterns across sessions using Postgres-backed fuzzy search with synonym expansion.

ui-design

39
from InugamiDev/ultrathink-oss

Comprehensive UI design system: 230+ font pairings, 48 themes, 65 design systems, 23 design languages, 30 UX laws, 14 color systems, Swiss grid, Gestalt principles, Pencil.dev workflow. Inherits ui-ux-pro-max (99 UX rules) + impeccable-frontend-design (anti-AI-slop). Triggers on any design, UI, layout, typography, color, theme, or styling task.

Zod

39
from InugamiDev/ultrathink-oss

> TypeScript-first schema validation with static type inference.

webinar-registration-page

39
from InugamiDev/ultrathink-oss

Build a webinar or live event registration page as a self-contained HTML file with countdown timer, speaker bio, agenda, and registration form. Triggers on: "build a webinar registration page", "create a webinar sign-up page", "event registration landing page", "live training registration page", "workshop sign-up page", "create a webinar page", "build an event page", "free webinar landing page", "live demo registration page", "online event page", "create a registration page for my webinar", "build a training event page".

web-workers

39
from InugamiDev/ultrathink-oss

Offload heavy computation from the main thread using Web Workers, SharedWorkers, and Comlink — structured messaging, transferable objects, and off-main-thread architecture patterns

web-vitals

39
from InugamiDev/ultrathink-oss

Core Web Vitals monitoring (LCP, FID, CLS, INP, TTFB), measurement with web-vitals library, reporting to analytics, and optimization strategies for Next.js

web-components

39
from InugamiDev/ultrathink-oss

Native Web Components, custom elements API, Shadow DOM, HTML templates, slots, lifecycle callbacks, and framework-agnostic design patterns

wasm

39
from InugamiDev/ultrathink-oss

WebAssembly integration — Rust to WASM with wasm-pack/wasm-bindgen, WASI, browser usage, server-side WASM, and performance considerations

vue

39
from InugamiDev/ultrathink-oss

Vue 3 Composition API, Nuxt patterns, reactivity system, component architecture, and production development practices

Vitest

39
from InugamiDev/ultrathink-oss

> Blazing fast unit testing powered by Vite — Jest-compatible API, native ESM, TypeScript.