Skip to content
Development
Command

/forge-webhook-skill

Scaffold a complete webhook receiver for any provider from an AsyncAPI YAML spec or a JSON event-type list

From plugin
heymegabyte-claude-skills
2153 skills27 agents53 commands
Install
> /plugin marketplace add heymegabyte/claude-skills

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/forge-webhook-skill

Context preview

What this command does when you run it.

Scaffold a complete webhook receiver for any provider from an AsyncAPI YAML spec or a JSON event-type list

Command definition

forge-webhook-skill.md
description: Scaffold a complete webhook receiver for any provider from an AsyncAPI YAML spec or a JSON event-type list
argument-hint: <provider> <spec-url-or-path>
allowed-tools: Bash, Read, Write, Edit, Glob, WebFetch

Auto-forge a hardened webhook receiver skill for `<provider>` from an AsyncAPI YAML spec or JSON event-type list. Generates signature-verified Hono routes, Zod schemas, D1 idempotency + audit, typed handler stubs, E2E fixtures, a SKILL.md, and `.env.template`.

Usage

/forge-webhook-skill stripe https://raw.githubusercontent.com/stripe/openapi/master/openapi/async_api.yaml
/forge-webhook-skill github ./specs/github-webhooks.json
/forge-webhook-skill resend '["email.sent","email.bounced","email.complained"]'
/forge-webhook-skill twilio ./specs/twilio-events.json
/forge-webhook-skill square https://developer.squareup.com/reference/square_2024-01-18_async.yaml

Input formats accepted

**AsyncAPI YAML spec** — parse `channels[*].messages[*].payload` for event schemas + `x-event-type` for the type string.

**JSON event-type list** — flat array of strings:

["payment_intent.succeeded", "payment_intent.payment_failed", "customer.subscription.deleted"]

**JSON event-type map** — keyed object with optional per-event payload shape:

{
  "payment_intent.succeeded": { "description": "Payment captured", "idField": "id" },
  "invoice.paid": { "description": "Invoice settled", "idField": "id" }
}

Bare inline JSON string → parse directly. URL → fetch. File path → read.

---

Signature scheme registry (built-in, no lookup needed)

| Provider | Header | Algorithm | Verification function | |---|---|---|---| | `stripe` | `stripe-signature` | HMAC-SHA256 + timestamp replay check | `stripe.webhooks.constructEventAsync(rawBody, sig, secret)` via `stripe` npm | | `github` | `x-hub-signature-256` | HMAC-SHA256 of raw body | `timingSafeEqual(computedHmac, receivedHmac)` | | `square` | `x-square-hmacsha256-signature` | HMAC-SHA256 of URL + raw body | `timingSafeEqual(...)` | | `resend` | `svix-id`, `svix-timestamp`, `svix-signature` | Svix webhook verification | `new Webhook(secret).verify(rawBody, headers)` via `svix` npm | | `twilio` | `x-twilio-signature` | HMAC-SHA256 of full URL + sorted params | `twilio.validateRequest(authToken, sig, url, params)` via `twilio` npm | | `clerk` | `svix-id`, `svix-timestamp`, `svix-signature` | Svix (same as resend) | `new Webhook(secret).verify(rawBody, headers)` | | `sendgrid` | `x-twilio-email-event-webhook-signature` | ECDSA P-256 (not HMAC) | `ecPublicKeyVerify(rawBody, sig, publicKey)` | | `shopify` | `x-shopify-hmac-sha256` | HMAC-SHA256 | `timingSafeEqual(...)` | | `linear` | `linear-signature` | HMAC-SHA256 | `timingSafeEqual(...)` | | `svix` (generic) | `svix-id`, `svix-timestamp`, `svix-signature` | Svix | `new Webhook(secret).verify(rawBody, headers)` | | `<unknown>` | warn + scaffold stub | n/a | generate `// TODO: verify <provider> signature` stub |

---

What gets generated

For provider `stripe` with events `["payment_intent.succeeded", "customer.subscription.deleted"]`:

src/worker/routes/webhook-stripe.ts          ← Hono route: verify → idempotency → dispatch
src/worker/webhooks/stripe/schemas.ts        ← Zod schemas per event type
src/worker/webhooks/stripe/handlers.ts       ← Typed handler stubs (one fn per event_type)
src/worker/webhooks/stripe/index.ts          ← Re-exports + event union type
e2e/webhooks/stripe/valid-signature.spec.ts  ← Playwright test: valid sig → 200
e2e/webhooks/stripe/invalid-signature.spec.ts← Playwright test: bad sig → 401
e2e/webhooks/stripe/replay-attack.spec.ts    ← Replay outside 5min window → 200 (idempotent)
e2e/webhooks/stripe/fixtures/               ← JSON payloads: one per event type
skills/stripe-webhooks/SKILL.md             ← Skill manifest for the integration
.env.template                               ← STRIPE_WEBHOOK_SECRET=whsec_... entry (append-safe)

---

Execution steps

Parse the spec, then emit all files in order:

Step 1 — Resolve spec

PROVIDER="${ARGUMENTS%% *}"
SPEC="${ARGUMENTS#* }"

# Detect input type
if [[ "$SPEC" == http* ]]; then
  RAW=$(curl -sL "$SPEC")
elif [[ "$SPEC" == /* || "$SPEC" == ./* ]]; then
  RAW=$(cat "$SPEC")
else
  RAW="$SPEC"   # inline JSON
fi
echo "$RAW"

Parse `$RAW`:

  • YAML (starts with `asyncapi:`) → extract `channels[*].messages[*]`, collect `x-event-type` + `payload` schema
  • JSON array → each string is an `event_type` with no payload schema (emit `z.record(z.unknown())` placeholder)
  • JSON object → each key is `event_type`, value is metadata (`description`, `idField`)

Step 2 — Write `src/worker/routes/webhook-{provider}.ts`

Template (fill `PROVIDER`, `SIG_SCHEME`, `ENV_VAR`, `EVENT_TYPES`):

/**
 * Webhook receiver: {PROVIDER}
 * Signature scheme: {SIG_SCHEME}
 * Generated by /forge-webhook-skill {PROVIDER}
 *
 * Core invariants (from [[webhook-receiver-architecture]]):
 *   1. Verify signature BEFORE any DB write
 *   2. Idempotency check BEFORE processing — UNIQUE (provider, event_id) → 200 on dup
 *   3. Respond 200/204 fast — push work to ctx.waitUntil()
 *   4. Dead-letter unknown event_type to R2
 *   5. Never log raw payload — log payload_hash (SHA-256 hex) only
 */
import { Hono } from 'hono'
import { z } from 'zod'
import { dispatch{PROVIDER_PASCAL}Event } from '../webhooks/{provider}/handlers'
import { {PROVIDER_PASCAL}EventSchema } from '../webhooks/{provider}/schemas'

export const {provider}WebhookRoute = new Hono<{ Bindings: Env }>()

{provider}WebhookRoute.post('/', async (c) => {
  const rawBody = await c.req.text()
  const sigHeader = c.req.header('{SIG_HEADER}') ?? ''

  // ── 1. Signature verification ─────────────────────────────────────────────
  const secret = c.env.{ENV_VAR}
  const verified = await verify{PROVIDER_PASCAL}Signature(rawBody, sigHeader, secret)
  if (!verified) {
    return c.json({ error: 'Invalid signature' }, 401)
  }
Read more
Ships withheymegabyte-claude-skills

14-category autonomous product-building OS for 32+ AI coding tools. One-line prompts → deployed products.

Get the whole plugin

Other commands on heymegabyte-claude-skills.