HTTP/1.1 402 PAYMENT REQUIRED · x402 + MPP dual-stack 500+ tools · 12 rails · 0% seller fee

Webhook secure intake

The production ingest path for every incoming webhook: verify the provider signature (GitHub / Stripe / Shopify / Slack, constant-time, replay-window enforced), schema-validate the now-trusted body against the provider envelope, fingerprint the raw bytes for redelivery dedup, normalize the event timestamp to UTC + epoch, and redact PII before anything hits a log. Five pure-CPU tools - the accept-or-reject gate, run on every event.

$0.050 per call · one payment for the whole workflow
POST /api/skill/webhook-intake

5 tools run server-side in one request. You pay once, settle once, and get a single response - no orchestration, no per-step payments, and a partial-success envelope if any step fails. USDC over x402 on any supported chain.

When to use this pack

webhook-debug answers 'why is my signature failing?' - this pack is what runs after that's solved: the gate an agent executes on EVERY incoming webhook in production. Step 1 is the security decision (reject on an invalid signature; Stripe and Slack timestamps get replay-window enforcement), and only then is the body treated as trusted: an envelope schema check catches provider API-version drift before it breaks your handler three layers down, a sha256 content fingerprint gives you the dedup key for provider redeliveries (or an Idempotency-Key for downstream calls), the event time is normalized to UTC ISO + epoch for storage, and PII is redacted so the audit log stays clean. One deterministic pass from raw bytes to a storable, loggable, deduplicated event.

Tools in this pack

All 5 run inside the single $0.050 call above. Each is also callable on its own if you only need one part.

Workflow

  1. Verify the signature with webhook-verify - the accept/reject gate, and the reason this pack exists. Pass provider ('github' | 'stripe' | 'shopify' | 'slack'), the RAW body string exactly as received (signatures are over the raw bytes - a parsed-then-restringified body will not match), the signing secret, and the signature header value (scheme prefixes like 'sha256=' / 'v0=' / 't=…,v1=…' are handled). The tool recomputes the correct per-provider HMAC scheme and compares in constant time; for Stripe and Slack it also enforces the ±300s replay window when the timestamp is supplied (Stripe's can ride inside the signature's t= element). If valid=false: stop, return 401 to the sender, and log nothing but the fingerprint from step 3 - an unverified body is attacker-controlled input.
  2. Schema-validate the now-trusted body with json-validate against a minimal provider envelope schema (GitHub push events carry repository/ref; Stripe events carry id/type/created; Slack carries type/event_time). The signature proves the bytes came from the provider - the schema proves they're the SHAPE your handler was written against. A schema failure on a validly-signed body is the API-version-drift signal (providers add, rename, and sunset fields on a schedule): alert loudly and route to a quarantine queue instead of silently dropping fields. Swap in your own event-specific schema per webhook topic in production.
  3. Fingerprint the raw bytes with hash (sha256). Providers redeliver on timeout and at-least-once delivery is the contract - the same event can arrive three times with three different delivery ids. The content hash is the dedup key that survives redelivery: check it against your processed-events store before doing any work, or reuse it directly as the Idempotency-Key header on downstream paid calls. Hashing the RAW body (not the parsed object) keeps the fingerprint byte-stable and lets you log it safely even for rejected events - it reveals nothing about the payload.
  4. Normalize the event timestamp with time-convert. Every provider ships a different clock format: Stripe created is epoch seconds, Slack event_time is epoch seconds, Shopify created_at is RFC 3339 with offset, GitHub commits carry ISO 8601. time-convert returns UTC ISO + epochSeconds + epochMillis in one call - store the epoch (sortable, timezone-proof), render the ISO. This is also where event-ordering bugs die: comparing a provider's local-offset string against your stored UTC string sorts wrong twice a year; comparing epochs never does.
  5. Redact the body with redact before it touches any log or trace. Webhook payloads routinely carry emails, IPs, phone numbers, and card fragments; the redacted string replaces each with a typed placeholder and the response counts each kind so you can emit 'redacted 1 email, 0 cards' as a metric. Persist ONLY the redacted string in logs - the redact-before-log ordering is the difference between a clean audit and a reportable incident. (The full raw body, if you must keep it, belongs encrypted in your event store keyed by the step-3 fingerprint, not in the log pipeline.)

Call it directly

Any x402 client pays the 402 and gets the whole workflow back in one response:

npx agent402-client call webhook-intake {"rawBody":"{\"ref\":\"refs/heads/main\",\"before\":\"6113728f27ae82c7b1a177c8d03f9e96e0adf246\",\"after\":\"d6fde92930d4715a2b49857d24b940956b26d2d3\",\"repository\":{\"full_name\":\"acme/checkout-service\"},\"pusher\":{\"name\":\"alice\",\"email\":\"alice@example.com\"},\"head_commit\":{\"id\":\"d6fde92930d4715a2b49857d24b940956b26d2d3\",\"message\":\"fix: retry payment capture on 5xx\",\"timestamp\":\"2026-07-01T15:04:05Z\"}}","provider":"github","secret":"gh_hook_secret_demo_only","signature":"sha256=45f74caa8f537323fd4fa022357ebc620cbcfb28a6dcd65b0f1da3646edf5c4a","timestamp":"..."}

Run it in Claude

claude mcp add agent402 -s user -- npx -y agent402-mcp@latest

Then paste this prompt into Claude:

Securely ingest this incoming webhook using Agent402's webhook-intake skill pack.

Provider: github
Raw body (byte-for-byte as received on the wire - never re-serialize before verifying):
{"ref":"refs/heads/main","before":"6113728f27ae82c7b1a177c8d03f9e96e0adf246","after":"d6fde92930d4715a2b49857d24b940956b26d2d3","repository":{"full_name":"acme/checkout-service"},"pusher":{"name":"alice","email":"alice@example.com"},"head_commit":{"id":"d6fde92930d4715a2b49857d24b940956b26d2d3","message":"fix: retry payment capture on 5xx","timestamp":"2026-07-01T15:04:05Z"}}

Signature header value: sha256=45f74caa8f537323fd4fa022357ebc620cbcfb28a6dcd65b0f1da3646edf5c4a
Signing secret: gh_hook_secret_demo_only

(1) webhook-verify with provider, payload=the RAW body string, secret, signature (scheme prefix ok). This is the accept/reject gate: if valid=false, STOP - return 401 to the sender and log only the step-3 fingerprint, never the body. For stripe/slack also pass the provider timestamp so the ±300s replay window is enforced (stripe's t= element inside the signature works too). (2) json-validate the parsed body against the provider envelope schema - for GitHub push events {type:'object', required:['repository']}; for Stripe require id/type/created. A schema failure on a VALID signature = provider API-version drift: alert loudly, quarantine, don't silently drop fields. (3) hash the raw body with algo='sha256' - the content fingerprint. Dedup redeliveries on it (at-least-once delivery means the same event arrives more than once) or reuse it as the Idempotency-Key for downstream calls. (4) time-convert the event timestamp (GitHub head_commit.timestamp ISO; Stripe created / Slack event_time epoch seconds; Shopify created_at RFC 3339) → UTC ISO + epochSeconds. Store the epoch, render the ISO. (5) redact the body before it touches any log. Persist ONLY the redacted string; report per-kind counts as metrics. Final return: {accepted: <signature valid AND schema valid>, verify: {valid, scheme, reason}, schemaValid: bool, schemaErrors: [], fingerprint: <sha256 hex>, eventTimeUtc, eventEpochSeconds, redactedBody, redactionCounts, oneLineSummary: 'webhook accepted: signature ok, envelope ok, deduped by fingerprint, 1 email redacted, safe to process'}. All five tools are pure-CPU and PoW-eligible - the whole gate runs on the free tier. Budget ≤ $0.01 even paid.

← All skill packs