Agent skill

Webhooks

by kid-sid in kid-sid/claude-spellbook

A skill your agent uses when designing a webhook delivery system, implementing HMAC signature verification on a receiver, handling retries and failures on the sender side, building idempotent…

MITAuto-check passedBackend & APIs

Install Webhooks

skills CLI
$ npx skills add kid-sid/claude-spellbook --skill webhooks -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install kid-sid/claude-spellbook webhooks --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/kid-sid/claude-spellbook.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/webhooks .claude/skills/webhooks && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
webhooks
GitHub stars
189
Token cost
~3.2k tokens
SKILL.md length
648 words
Files
1
Skills in repo
54
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when designing a webhook delivery system, implementing HMAC signature verification on a receiver, handling retries and failures on the sender side, building idempotent…

  • Designing a webhook delivery system
  • SKILL.md covers When to Activate, Sender vs. Receiver…, Event Envelope Design and HMAC Signature Verification, plus 6 more sections
  • Calls stripe, npx and ngrok; needs STRIPE_SECRET
  • Implementing HMAC signature verification on a receiver

What it does

Webhooks is an agent skill from kid-sid/claude-spellbook. Use when designing a webhook delivery system, implementing HMAC signature verification on a receiver, handling retries and failures on the sender side, building idempotent webhook consumers, or testing webhook integrations locally.

Its SKILL.md is about 3.2k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Backend & APIs, covering Webhooks. The repository describes itself as: A curated collection of skills, prompts, and workflows that extend Claude's capabilities — your personal grimoire for AI-powered development. The licence is MIT.

When your agent uses it

  • Designing a webhook delivery system
  • Implementing HMAC signature verification on a receiver
  • Handling retries and failures on the sender side
  • Building idempotent webhook consumers

Example prompts

  • “/webhooks”

Requirements

  • Python 3
  • Node.js
  • A credential in STRIPE_SECRET

What it can do on your machine

Read from SKILL.md and the folder at commit a7c2ac9. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • stripe
    • npx
    • ngrok
    • curl

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npx and curl, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • STRIPE_SECRET

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Webhooks loads about 3.2k tokens when it runs. Until then it costs about 60 tokens; SKILL.md has 648 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~60
When it runs · the whole SKILL.md, loaded when a task matches
~3.2k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from kid-sid/claude-spellbook at commit a7c2ac9, republished under its MIT licence (© kid-sid). 648 words, ~3,211 tokens.

Download SKILL.mdSave it as .claude/skills/webhooks/SKILL.md (or your agent's skills folder).
name
webhooks
description
Use when designing a webhook delivery system, implementing HMAC signature verification on a receiver, handling retries and failures on the sender side, building idempotent webhook consumers, or testing webhook integrations locally.

Webhooks

Patterns for reliable webhook delivery, signature verification, idempotent consumption, and local testing.

When to Activate

  • Designing a system that pushes events to external URLs
  • Implementing HMAC signature verification on a webhook receiver
  • Building retry and failure handling on the webhook sender side
  • Making a webhook consumer idempotent (safe to receive twice)
  • Exposing and documenting a webhook API for third-party integrations
  • Testing webhook integrations locally without a public URL
  • Auditing an existing webhook implementation for security or reliability gaps

Sender vs. Receiver Responsibilities

ConcernSender (you push)Receiver (you consume)
SigningSign every payload with HMACVerify signature before processing
DeliveryRetry with backoff on non-2xxReturn 2xx immediately, process async
OrderingAdd sequence or timestampDon't assume ordered delivery
IdempotencyInclude a stable event IDDeduplicate on event ID
SchemaVersion the event typeHandle unknown fields gracefully

Event Envelope Design

Every webhook payload should include a consistent envelope regardless of event type.

json
{
  "id": "evt_01HX4K9MZQR8FVNJ2Y3BCD5",
  "type": "order.completed",
  "version": "2024-01",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "order_id": "ord_789",
    "amount": 4999,
    "currency": "usd"
  }
}
FieldPurpose
idStable, unique event ID — used for deduplication
typeNamespaced event name (resource.action)
versionSchema version — allows evolving payloads without breaking receivers
created_atWhen the event occurred (not when it was delivered)
dataEvent-specific payload

HMAC Signature Verification

Never trust a webhook payload without verifying its signature. Use a constant-time comparison to prevent timing attacks.

Signing (Sender)
python
# Python
import hmac, hashlib, json, time

def sign_payload(payload: dict, secret: str) -> tuple[str, str]:
    body = json.dumps(payload, separators=(",", ":")).encode()
    timestamp = str(int(time.time()))
    signed = f"{timestamp}.{body.decode()}"
    sig = hmac.new(secret.encode(), signed.encode(), hashlib.sha256).hexdigest()
    return f"t={timestamp},v1={sig}", body
typescript
// TypeScript
import { createHmac } from "crypto";

function signPayload(payload: object, secret: string): { signature: string; body: string } {
  const body = JSON.stringify(payload);
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const signed = `${timestamp}.${body}`;
  const sig = createHmac("sha256", secret).update(signed).digest("hex");
  return { signature: `t=${timestamp},v1=${sig}`, body };
}
go
// Go
func SignPayload(payload []byte, secret string, now time.Time) string {
    timestamp := strconv.FormatInt(now.Unix(), 10)
    signed := timestamp + "." + string(payload)
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(signed))
    sig := hex.EncodeToString(mac.Sum(nil))
    return fmt.Sprintf("t=%s,v1=%s", timestamp, sig)
}
Verifying (Receiver)
python
# Python — FastAPI
import hmac, hashlib, time
from fastapi import Request, HTTPException

TOLERANCE_SECONDS = 300  # reject replays older than 5 minutes

async def verify_webhook(request: Request, secret: str) -> bytes:
    sig_header = request.headers.get("X-Webhook-Signature", "")
    body = await request.body()

    parts = dict(p.split("=", 1) for p in sig_header.split(","))
    timestamp = parts.get("t", "")
    received_sig = parts.get("v1", "")

    # Reject replayed requests
    if abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS:
        raise HTTPException(400, "Webhook timestamp too old")

    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.{body.decode()}".encode(),
        hashlib.sha256,
    ).hexdigest()

    # Constant-time comparison — prevents timing attacks
    if not hmac.compare_digest(expected, received_sig):
        raise HTTPException(400, "Invalid webhook signature")

    return body
typescript
// TypeScript — Express
import { createHmac, timingSafeEqual } from "crypto";
import { Request, Response, NextFunction } from "express";

const TOLERANCE_SECONDS = 300;

export function verifyWebhook(secret: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    const header = req.headers["x-webhook-signature"] as string ?? "";
    const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
    const { t: timestamp, v1: receivedSig } = parts;

    if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) {
      return res.status(400).json({ error: "Webhook timestamp too old" });
    }

    const body = (req as any).rawBody as Buffer; // requires rawBody middleware
    const expected = createHmac("sha256", secret)
      .update(`${timestamp}.${body}`)
      .digest("hex");

    if (!timingSafeEqual(Buffer.from(expected), Buffer.from(receivedSig))) {
      return res.status(400).json({ error: "Invalid webhook signature" });
    }

    next();
  };
}
go
// Go
func VerifyWebhook(r *http.Request, body []byte, secret string) error {
    header := r.Header.Get("X-Webhook-Signature")
    parts := parseHeader(header) // split "t=...,v1=..." into map
    timestamp, received := parts["t"], parts["v1"]

    ts, _ := strconv.ParseInt(timestamp, 10, 64)
    if math.Abs(float64(time.Now().Unix()-ts)) > 300 {
        return errors.New("webhook timestamp too old")
    }

    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(timestamp + "." + string(body)))
    expected := hex.EncodeToString(mac.Sum(nil))

    if !hmac.Equal([]byte(expected), []byte(received)) {
        return errors.New("invalid webhook signature")
    }
    return nil
}

Reliable Delivery (Sender)

Delivery Pipeline
Event occurs → persist to outbox → worker picks up → HTTP POST → record result
                                                              ↓
                                                    success: mark delivered
                                                    failure: schedule retry

Use the transactional outbox pattern — write the event to a DB table in the same transaction as the business change. A background worker reads and delivers it. This prevents events being lost if the app crashes between the DB write and the HTTP call.

python
# Python — outbox record
@dataclass
class WebhookDelivery:
    id: str
    endpoint_url: str
    payload: dict
    attempt: int = 0
    max_attempts: int = 5
    next_attempt_at: datetime = field(default_factory=datetime.utcnow)
    delivered_at: datetime | None = None
    last_error: str | None = None
Retry Schedule
AttemptDelay
1st retry30 seconds
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry8 hours
After maxMove to dead-letter queue
python
def next_retry_delay(attempt: int) -> int:
    schedule = [30, 300, 1800, 7200, 28800]
    return schedule[min(attempt, len(schedule) - 1)]
Delivery Worker
python
import httpx

async def deliver_webhook(delivery: WebhookDelivery, secret: str) -> None:
    sig, body = sign_payload(delivery.payload, secret)
    try:
        async with httpx.AsyncClient(timeout=10) as client:
            r = await client.post(
                delivery.endpoint_url,
                content=body,
                headers={
                    "Content-Type": "application/json",
                    "X-Webhook-Signature": sig,
                    "X-Webhook-ID": delivery.id,
                },
            )
        if r.status_code < 200 or r.status_code >= 300:
            raise ValueError(f"non-2xx: {r.status_code}")
        await mark_delivered(delivery.id)
    except Exception as exc:
        await schedule_retry(delivery, error=str(exc))

Idempotent Consumers

Webhooks can be delivered more than once. Always deduplicate on event.id.

python
# Python — Redis deduplication
import redis.asyncio as redis

async def handle_webhook(event: dict, r: redis.Redis) -> None:
    event_id = event["id"]
    key = f"webhook:seen:{event_id}"

    # SET NX — only set if not already present
    already_seen = not await r.set(key, "1", nx=True, ex=86400)
    if already_seen:
        return  # duplicate — safe to ignore

    # Process after deduplication
    await process_event(event)
typescript
// TypeScript — DB-based deduplication
async function handleWebhook(event: WebhookEvent): Promise<void> {
  const inserted = await db.webhookEvent.upsert({
    where: { id: event.id },
    create: { id: event.id, type: event.type, processedAt: null },
    update: {}, // no-op if already exists
  });

  if (inserted.processedAt) return; // already processed

  await processEvent(event);
  await db.webhookEvent.update({ where: { id: event.id }, data: { processedAt: new Date() } });
}

Receiver Response Contract

ScenarioResponseNotes
Accepted for processing200 OKBody ignored by sender
Duplicate (already processed)200 OKNot an error — idempotent
Invalid signature400 Bad RequestDo not retry
Unknown event type200 OKAccept and ignore unknown types
Downstream not ready503 Service UnavailableSender will retry

Always return 2xx immediately. Do the actual work asynchronously (queue it). A slow receiver causes the sender to timeout and retry unnecessarily.

python
# FastAPI — accept immediately, process async
@router.post("/webhooks/stripe")
async def stripe_webhook(request: Request, background_tasks: BackgroundTasks):
    body = await verify_webhook(request, secret=STRIPE_SECRET)
    event = json.loads(body)
    background_tasks.add_task(process_stripe_event, event)
    return {"received": True}  # 200 immediately

Local Testing

bash
# Use a tunnel to expose localhost
npx localtunnel --port 3000 --subdomain my-app
# or
ngrok http 3000

# Replay a real webhook for testing
curl -X POST http://localhost:3000/webhooks \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Signature: t=1234567890,v1=abc..." \
  -d '{"id":"evt_test","type":"order.completed","data":{}}'

# Stripe CLI — replay events against local server
stripe listen --forward-to localhost:3000/webhooks/stripe
stripe trigger payment_intent.succeeded

Show full SKILL.md (264 more words)Show less

Red Flags

  • No signature verification — any caller can send a fake payload; always verify HMAC before touching the body.
  • String equality for signature comparison — use hmac.compare_digest / timingSafeEqual / hmac.Equal; plain == leaks timing information.
  • No timestamp validation — without a replay window check, an attacker can resend a captured payload indefinitely.
  • Synchronous processing in the handler — a slow downstream makes the receiver timeout; the sender retries, creating a storm; always return 2xx immediately and queue the work.
  • Not idempotent — at-least-once delivery is the norm; without deduplication a retry charges a customer twice or sends a duplicate email.
  • Retrying 4xx responses — a 400 or 401 will never succeed; only retry 5xx and network errors.
  • No dead-letter queue — events that exhaust retries silently disappear; always persist to a DLQ for inspection and manual replay.
  • Delivering directly from the request handler — if the app crashes mid-delivery, the event is lost; use a transactional outbox instead.

Checklist

  • Every outgoing payload signed with HMAC-SHA256 and includes a timestamp
  • Receiver verifies signature with constant-time comparison before processing
  • Replay window enforced — payloads older than 5 minutes rejected
  • Receiver returns 2xx immediately — actual processing is async
  • Consumer is idempotent — deduplicates on event.id via Redis or DB upsert
  • Sender uses transactional outbox — event persisted before delivery attempt
  • Retry schedule uses increasing delays — no fixed-interval hammering
  • 4xx responses not retried — only 5xx and network errors trigger retry
  • Dead-letter queue captures events that exhaust all retry attempts
  • Webhook endpoint URL and signing secret stored in env vars — not hardcoded
  • Unknown event types accepted with 200 and silently ignored
  • Local testing flow documented — tunnel or CLI replay tool configured

© kid-sid, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in skills/webhooks of kid-sid/claude-spellbook.

Open the folder on GitHubat commit a7c2ac9

Compare with similar skills

Webhooks next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Webhooks compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Webhooks this skillkid-sid/claude-spellbook189—~3.2kAutomated safety check: PassMIT
Novu Design Workflownovuhq/novu40k—~2.6kAutomated safety check: PassCustom licence
Golivemikehasa/golive-skill1.2k—~13kAutomated safety check: NotesMIT
Stripe Appsfossasia/eventyay1.7k1 repos~3.6kAutomated safety check: PassApache-2.0
Dingtalk Messageagentscope-ai/ReMe3.6k—~1.6kAutomated safety check: PassApache-2.0
PR Review Provideryansongda/pay5.4k—~2.4kAutomated safety check: PassMIT

Similar skills

  • Design notification workflows the Novu way — choose channels, set severity, decide when a workflow is critical, configure digests, and route based on subscriber state.

    40k GitHub stars~2.6k tokensUpdated today
    Backend & APIsAuto-check passed
  • Golive

    mikehasa/golive-skill

    Take an agent-written app from repo to live production on the user's OWN accounts, with providers they choose (hosting, database, auth, payments, email, domain/DNS).

    1.2k GitHub stars~13k tokensUpdated 3 days ago
    Backend & APIsAuto-check: notes
  • Stripe Apps

    fossasia/eventyay

    A skill your agent uses when building, modifying, or reviewing a Stripe App — or when the user describes something that implies one (e.g.

    1.7k GitHub starsUsed in 1 repo~3.6k tokens
    Backend & APIsAuto-check passed
  • Dingtalk Message

    agentscope-ai/ReMe

    钉钉消息发送技能。支持企业内部机器人(批量单聊/群聊)和 Webhook 自定义机器人两种接入方式,支持多机器人管理,支持文本、Markdown、链接、ActionCard、FeedCard等多种消息类型。

    3.6k GitHub stars~1.6k tokensUpdated yesterday
    Backend & APIsAuto-check passed
  • PR Review Provider

    yansongda/pay

    A skill your agent uses when reviewing PRs that add or modify a payment Provider in yansongda/pay - covers plugin pipeline, multi-tenant safety, signature verification, docs, and naming conventions.

    5.4k GitHub stars~2.4k tokensUpdated 9 days ago
    Backend & APIsAuto-check passed
  • Stripe Best Practices

    kanchengw/cnllm

    Guides Stripe integration decisions — API selection (Checkout Sessions vs PaymentIntents), Connect platform setup (Accounts v2, controller properties), billing/subscriptions, Treasury financial…

    175 GitHub starsUsed in 3 repos~925 tokens
    Backend & APIsAuto-check passed

More from kid-sid/claude-spellbook

All 54 skills in this repo
  • Accessibility

    kid-sid/claude-spellbook

    A skill your agent uses when building or reviewing UI components for keyboard and screen reader compatibility, adding ARIA to custom widgets, auditing a page for WCAG AA conformance, or preparing…

    189 GitHub stars~3.2k tokensUpdated 2 mo ago
    Auto-check passed
  • Agentex

    kid-sid/claude-spellbook

    A skill your agent uses when building, wiring, or debugging an Agentex agent — choosing agent type, configuring acp.py and manifest.yaml, using adk.messages or adk.state, or resolving…

    189 GitHub stars~2.2k tokensUpdated 2 mo ago
    Auto-check: notes
  • AI Engineer

    kid-sid/claude-spellbook

    A skill your agent uses when building production LLM applications — designing RAG pipelines, choosing vector databases, implementing agent orchestration, optimizing cost, or adding AI safety…

    189 GitHub stars~3.7k tokensUpdated 2 mo ago
    Auto-check passed
  • Angular

    kid-sid/claude-spellbook

    A skill your agent uses when building or refactoring Angular applications — choosing between signals, RxJS, and NgRx for state, configuring routing with guards and lazy loading, optimizing change…

    189 GitHub stars~5k tokensUpdated 2 mo ago
    Auto-check passed
  • API Design

    kid-sid/claude-spellbook

    A skill your agent uses when designing new REST endpoints, reviewing an existing API contract, adding pagination or filtering, planning a versioning strategy, or building a public or partner-facing…

    189 GitHub stars~3.6k tokensUpdated 2 mo ago
    Auto-check passed
  • Auth

    kid-sid/claude-spellbook

    A skill your agent uses when implementing login flows, issuing or validating JWTs, setting up OAuth2/OIDC with a provider, designing role-based or attribute-based access control, securing API…

    189 GitHub stars~3.2k tokensUpdated 2 mo ago
    Auto-check passed

Categories

Questions about Webhooks

What does Webhooks do?

A skill your agent uses when designing a webhook delivery system, implementing HMAC signature verification on a receiver, handling retries and failures on the sender side, building idempotent…. Webhooks is an agent skill from kid-sid/claude-spellbook. Use when designing a webhook delivery system, implementing HMAC signature verification on a receiver, handling retries and failures on the sender side, building idempotent webhook consumers, or testing webhook integrations locally.

When should I use Webhooks?

Webhooks fits situations like: designing a webhook delivery system; implementing HMAC signature verification on a receiver; handling retries and failures on the sender side; building idempotent webhook consumers.

How do I install Webhooks in Claude Code?

Run `npx skills add kid-sid/claude-spellbook --skill webhooks -a claude-code`. Or copy the skill folder (skills/webhooks in kid-sid/claude-spellbook) into .claude/skills/webhooks in your project. Claude Code loads it when a task matches its description.

How do I install Webhooks in Codex?

Run `npx skills add kid-sid/claude-spellbook --skill webhooks -a codex`. Or copy the skill folder (skills/webhooks in kid-sid/claude-spellbook) into .agents/skills/webhooks in your project. Codex loads it when a task matches its description.

Can I use Webhooks in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add kid-sid/claude-spellbook --skill webhooks -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/webhooks, .gemini/skills/webhooks, .github/skills/webhooks and .opencode/skills/webhooks in your project.

What does Webhooks need to run?

Going by SKILL.md and its folder, Webhooks needs the command-line tools its instructions call (stripe, npx, ngrok and curl) and credentials named STRIPE_SECRET. Our summary lists: Python 3; Node.js; A credential in STRIPE_SECRET.

Does Webhooks access the network?

SKILL.md contains no URLs. Its commands use npx and curl, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Webhooks safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Webhooks use?

Webhooks is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Webhooks use?

About 3.2k tokens (SKILL.md is roughly 13k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Webhooks?

Skills that share tags, products or a category with Webhooks: Novu Design Workflow (novuhq/novu, 40k stars), Golive (mikehasa/golive-skill, 1.2k stars), Stripe Apps (fossasia/eventyay, 1.7k stars) and Dingtalk Message (agentscope-ai/ReMe, 3.6k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Webhooks?

kid-sid (a GitHub user) maintains it in kid-sid/claude-spellbook, which has 189 GitHub stars. The repository holds 54 skills in this directory. The repository was last updated on August 5, 2026.

Source: kid-sid/claude-spellbook on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.