Specfork

@specfork/webhooks

Signed, retried, SSRF-safe outbound webhooks for events the requiring spec defines.

baseintegrations Version 0.3.1 15 requirements Licence CC-BY-4.0 Specfork/webhooks

Webhooks

Outbound HTTP callbacks. The requiring spec names the events and scopes; this spec delivers them. Signing follows the Standard Webhooks scheme. Not affiliated with or endorsed by Cal.com, Inc. The name is used only to credit the source.

Area Prefix Covers
webhooks.md HOOK Subscriptions, URL safety, signing, retries, logs, tests

Webhooks

Status: Draft · Prefix: HOOK

Purpose

Send signed HTTP callbacks for events an app defines, reliably and safely.

Requirements

ID Requirement Verified by
HOOK-1 Subscription: {id, url, events [names defined by the requiring spec], active (default true), secret (generated, ≥ 256 bits, shown once), scope (owner set by the requiring spec), template?, description? (≤ 200 chars), filters? (fields the requiring spec defines)}. (scope, url) unique. duplicate url in scope → refused
HOOK-2 URL check on save and before each send: https only; host must not resolve to loopback, private, link-local or metadata addresses. https://169.254.169.254 → refused
HOOK-3 Delivery: POST, JSON body {id (delivery id), event, created_at, data}, headers Webhook-Id, Webhook-Timestamp (unix s), Webhook-Signature: v1,<base64 HMAC-SHA256(secret, id + "." + timestamp + "." + body)>. receiver verifies signature with secret
HOOK-4 Redirects not followed; timeout 10 s; any non-2xx or timeout is a failure. receiver 500 → retried
HOOK-5 Retries with backoff: 30 s, 2 min, 10 min, 1 h, 6 h (6 attempts total); same delivery id on every attempt. 500 then 200 → delivered once, same id
HOOK-6 Each attempt logged {delivery id, attempt, status code, duration, error?}, kept 30 days, visible to the subscription owner; failed deliveries can be resent. manual: log shows attempts
HOOK-7 A subscription failing every delivery for 5 consecutive days is set inactive and its owner notified. manual
HOOK-8 Optional template renders the body from data with a logic-less template language (Mustache); result must be valid JSON, else save is refused. invalid template → save refused
HOOK-9 "Send test" posts a sample of a chosen event, returns the receiver's status, and is subject to HOOK-2. test → status shown
HOOK-10 Events are sent at least once and in no guaranteed order; receivers dedupe by id. manual: documented for receivers
HOOK-11 Time-based events (e.g. "starts in") are stored as scheduled deliveries and cancelled when the requiring spec says so. manual: see requiring spec
HOOK-12 Event patterns: prefix.* matches every event starting with prefix., * matches all; events added later are included. subscribe booking.*, new booking.x → delivered
HOOK-13 filters limit deliveries to events whose data match every given field (e.g. inbox_ids); unknown filter fields → save refused. filter inbox 1, event for inbox 2 → not sent
HOOK-14 Sends per subscription are limited to 10 at once and 100 per second; excess is queued, never dropped. 500 events in 1 s → all delivered within ~5 s
HOOK-15 An event whose data lacks a field a subscription filters on is not sent to that subscription. filter inbox_ids, contact event → not sent

Out of scope

  • Zapier/Make-specific payloads, per-event custom headers.

Decisions

  • Signature scheme follows Standard Webhooks (id + timestamp in the signed content blocks replays); the source signed only the body.
  • Non-2xx counts as failure and is retried; the source retried only network errors.

From Specfork/webhooks at 2de50d9, licence CC-BY-4.0.