hermes

Build a webhook handler

Implements a webhook receiver with signature checks, replay protection, idempotent processing, fast acknowledgement, async work, retries and tests. Use when integrating Stripe, GitHub or similar.

context

Webhook endpoints are public URLs that move money, permissions or data, so they fail in costly ways: a framework parses the JSON before the signature is checked and the raw bytes are gone, so verification never works and someone disables it; a forged or replayed request is accepted; the provider retries after a slow response and the order ships twice; events arrive out of order and an old "subscription.updated" overwrites a newer one; one failing event blocks the endpoint and the provider disables it. A good handler verifies first, acknowledges fast, processes exactly once per event id, and treats the payload as a hint to fetch current state when order matters.

task

Implement a webhook receiver for . Only if [STACK] is given: Stack: If no stack is given, detect the language, framework and job queue from the repo and follow their conventions.

Events to handle:

events

Only if [SIGNATURE_SCHEME] is given: Signature scheme from the provider's docs:

signature scheme

  1. Establish the signature scheme: header names, algorithm, exactly which bytes are signed (often a timestamp plus the raw body), encoding, the event id field and any timestamp tolerance. Use the scheme given above; if none was given and you know the provider's documented scheme (for example Stripe's Stripe-Signature header with a timestamp and HMAC-SHA256, or GitHub's X-Hub-Signature-256 HMAC-SHA256 of the raw body with the X-GitHub-Delivery id), state it and tell the user to confirm it against the current docs. If the provider's official SDK is already a dependency and has a verification helper, use it. If you do not know the scheme, stop and ask for it.
  2. Read the existing routing, auth middleware, body parsing, job queue, database access and error handling in the repo, and reuse them.
  3. Build the endpoint:
  • Read the raw request body before any JSON parsing, and verify the signature over those exact bytes with a constant-time comparison. Reject with 400 or 401 and no detail on failure.
  • Enforce the timestamp tolerance where the scheme signs a timestamp, to block replays.
  • Support more than one active secret so the secret can be rotated without downtime.
  • Enforce a body size limit and accept only the expected content type.
  • Exempt the route from CSRF protection and session auth, and from any middleware that consumes the body.
  1. Make processing idempotent and fast:
  • Record the event id in a table with a unique constraint; if it already exists, acknowledge with 2xx and do nothing.
  • Persist the event and enqueue the work, then return 2xx quickly (well within the provider's timeout); do the real work in a background job. Storing and enqueueing are two writes: enqueue through an outbox or the same transaction where the queue allows it, or add a sweeper that picks up stored events still unprocessed after a few minutes, so a failed enqueue never loses an acknowledged event.
  • In the job, handle each listed event type in its own function; ignore and log unknown types with 2xx so new provider events do not cause retries.
  • Guard against out-of-order delivery: compare the event's created time or object version with what is stored, or fetch the current object from the provider's API before acting when order matters.
  • Make the side effects themselves idempotent (upserts, state checks, idempotency keys on outbound calls).
  1. Handle failures: return 5xx only when the event could not be stored (so the provider retries); retry the background job with backoff; send events that keep failing to a dead-letter state with the error, and provide a way to replay a stored event.
  2. Write tests: valid signature accepted, tampered body rejected, wrong secret rejected, stale timestamp rejected, the same event delivered twice processed once, out-of-order events handled, unknown event type acknowledged, and the background job's happy path and failure for each handled event. Build test signatures with a test secret, never a real one.
  3. Run the tests and the linter, and report the real results.
constraints
  • Never log the raw signature, the secret or full payloads that contain personal or payment data; log the event id and type.
  • Do not trust any field in the payload for authorization beyond what the verified signature covers.
  • Do not invent provider headers, event names or fields. Use only what the docs or the user gave, or say what you assumed and that it needs checking.
  • Do only what was asked. If you notice something else worth changing, mention it in one line at the end instead of changing it.
  • Keep the change as small as it can be while still being correct.
  • Before saying the work is done, run the check that proves it (tests, build, type check or the command the user gave) and report the real result.
  • If you could not run a check, say so plainly and say which one.
output format

Signature scheme

What is signed, the headers, algorithm and tolerance, and the source (user-provided, SDK, or from memory: confirm against the provider's docs).

Design

A Mermaid sequence diagram from the provider to the side effect, then the idempotency and ordering strategy in a few bullets.

Changes

One line per file.

Tests

One line per test and the real result of the run.

Configuration

| Setting | Env var | Required | Notes | (secrets, tolerance, queue names)

Operational notes

How to register the endpoint with the provider, rotate the secret, replay a failed event, and what to alert on.

2 required values still a placeholder; the assistant will ask for them.

details

kind
Prompt: a task you run by name to get one finished thing back
domain
Software engineering
category
Implementation
level
Intermediate
made for
Backend engineer, Full-stack engineer, Software engineer
needs
repo-read, file-write, shell
risk
runs-commands
version
v1.0.1 · incubating
reviewed
2026-10-02
works in
Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Antigravity, OpenCode, Windsurf, Zed, Continue, AGENTS.md

Edit on GitHubReport a problem

use in

Hodios CLI
npx @hermes-hq/hodios install build-webhook-handler --target claude-code
Agent Skills
npx skills add hermes-hq/hodios-dist --skill build-webhook-handler -a claude-code
Add the Hodios marketplace (once)
claude plugin marketplace add hermes-hq/hodios-dist
Install the software-engineering plugin
claude plugin install hodios-software-engineering@hodios

The plugin brings every entry in this domain at once.

pairs well with

All of Implementation
PersonaImplementation

Backend engineer

Acts as a backend engineer focused on correct data handling, clear API contracts, explicit failure modes and services that are easy to operate. Use as a builder or reviewer persona for server code.

backend-engineer
PromptImplementation

Integrate a third-party API

Implements a typed client for a third-party HTTP API from its docs, with auth, pagination, retries, rate limits and a test fake. Use when wiring an external service into your code.

integrate-third-party-api
PromptImplementation

Implement a background job

Implements a background or scheduled job with idempotency, retries with backoff, dead-letter handling, timeouts, concurrency limits and observability. Use to move slow work off the request path.

implement-background-job
PromptSecurity

Review a pull request for security

Reviews a diff for exploitable vulnerabilities and reports only findings with a concrete attack path. Use before merging changes to input handling, auth, data access or dependencies.

review-pr-for-security
PromptImplementation

Put a change behind a feature flag

Wraps new behaviour behind a feature flag with a safe default, a kill switch, tests for both paths and a cleanup ticket. Use when shipping a risky change incrementally.

add-feature-flag
PromptImplementation

Add rate limiting to an API

Adds rate limiting to API endpoints with a fitting algorithm, keys, per-tier limits, standard headers, 429 responses and tests. Use when protecting endpoints from abuse or overload.

add-rate-limiting