internal-packages/webhook-sources/catalog/build-brief.md
You are building ONE provider for the webhook sample library. Do the work YOURSELF. Do NOT spawn or
delegate to any subagents. Only create your provider's own files; do NOT edit any shared file
(providers.json, any index.ts, samples.ts, or another provider's files).
PACKAGE: /Users/eric/code/triggerdotdev/isolated/webhooks/internal-packages/webhook-sources
Find your provider's row in the FACTS table below. Then:
A registry entry, always: src/registry/<id>.ts exporting entry (see clerk reference).
Sample bodies, depending on source:
source=hookdeck -> samples already exist in src/generated/hookdeck-samples.json. Do NOT author a
sample file. Just read that JSON, confirm entries for your provider, and report count + eventTypes.source=handauthored -> create src/handAuthored/<id>.ts exporting samples: SampleRecord[] with
4 to 6 representative events. USE WEB SEARCH against the docs URL to get the REAL payload shape.src/registry/clerk.ts:
import { type ProviderRegistryEntry } from "./types.js";
export const entry: ProviderRegistryEntry = {
id: "clerk", label: "Clerk", category: "auth-identity",
docsUrl: "https://clerk.com/docs/integrations/webhooks/overview",
preset: "svix", secretProvisioning: "provider",
eventTypeSource: { from: "body", path: "type" }, sampleSource: "handauthored",
};
src/handAuthored/clerk.ts exports export const samples: SampleRecord[] = [ { provider, providerLabel, presetId, eventType, name, description?, body, docsUrl?, provenance } ].
ProviderRegistryEntry: id, label, category, icon?, docsUrl?, preset?, secretProvisioning ("provider"|"integrator"|"either"), eventTypeSource ({from:"body",path} | {from:"header",name}), sampleSource ("hookdeck"|"octokit"|"capture"|"handauthored").
SampleRecord: provider (string), providerLabel?, presetId? (one of stripe|github|svix|square|discord), eventType (string), name (string), description?, body (the event JSON), extraHeaders? (record of NON-signature routing headers), docsUrl?, provenance ({kind:"handauthored", snapshotDate:"2026-07"}).
preset/presetId)Your row lists a preset HYPOTHESIS. Set entry.preset and every sample's presetId to it ONLY if the
provider's real signature wire format EXACTLY matches one of ours below. If it does not match, DROP the
preset: omit entry.preset, omit presetId, keep sampleSource, and report that you downgraded to
sample-only and why.
stripe: header stripe-signature = t=<unix>,v1=<hex>, HMAC-SHA256 over {t}.{rawBody}.github: header x-hub-signature-256 = sha256=<hex>, HMAC-SHA256 over rawBody. (A provider using
x-hub-signature WITHOUT the -256, or SHA1, does NOT match.)svix (Standard Webhooks): headers webhook-id/webhook-timestamp/webhook-signature (or the
svix-* equivalents), HMAC-SHA256 base64 over {id}.{timestamp}.{rawBody}, secret whsec_-prefixed.square: header x-square-hmacsha256-signature, HMAC-SHA256 base64 over {notificationUrl}{rawBody}.discord: headers X-Signature-Ed25519 + X-Signature-Timestamp, Ed25519 over {timestamp}{rawBody}.eventType MUST equal the real discriminant value for that sample (from the row's discriminant).discriminant=header:<name>): the body has no type field,
so add extraHeaders: { "<name>": "<eventType>" } to each sample so the type survives loading.
Body-discriminated providers must NOT set signature headers in extraHeaders (the composer signs at
send time; never store a signature).eventType to the meaningful value. Some are form-encoded (Twilio):
represent the body as the key/value object.// line comments via a hook. Use /** */ JSDoc only, or none. If a Write is
rejected for comments, remove them and retry..js. Do NOT run tests, aggregate, or typecheck; do NOT touch index files.FILES: <paths>. PRESET: <set to X | downgraded to sample-only because ...>. EVENTS: <event types>. SOURCE: <docs URL verified>. CAVEATS: <anything uncertain>.
Columns: id | label | category | tier | preset-hypothesis | scheme | discriminant | secret | source | docsUrl | suggested events