Webhooks
Payload
One JSON POST per matching change. Schema: docs/payload.schema.json (JSON Schema, draft 2020-12). Example:
{
"event": "opportunity.new",
"change_id": "chg_5f2c9e1ab84d03aa77c1",
"notice_id": "70Z02326QNAExample01",
"title": "Custom software development for logistics systems",
"naics": "541511",
"set_aside": "SBA",
"agency": "DEPT OF DEFENSE",
"status": "active",
"posted_date": "2026-08-15",
"response_deadline": "2026-09-15",
"changed_fields": [],
"description": "Sources sought: custom software development services supporting logistics systems. See the notice for response instructions.",
"description_truncated": false,
"description_link": "https://sam.gov/opp/70Z02326QNAExample01/view",
"occurred_at": "2026-08-16T06:21:04Z"
}
description is string|null — it carries the notice's plain-text description from the daily extract when present (flattened text; upstream caps it nominally at 5,000 characters), and description_link always carries the SAM.gov notice URL. The payload shape never changes between data paths.
Signature
Every POST carries X-SamHooks-Signature: sha256=<hex> — HMAC-SHA256 over the raw request body with your signing key — plus X-SamHooks-Delivery-Id. Verify:
const crypto = require("node:crypto");
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.SAMHOOKS_SIGNING_KEY)
.update(rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sigHeader));
Retries
Non-2xx or timeout → up to 5 attempts with exponential backoff (Cloudflare Queues). After the 5th failure the delivery is marked failed-final in your delivery log.
Auto-disable
20 consecutive failed-final deliveries flip your endpoint to disabled. While disabled: no webhook deliveries are enqueued, matching changes still appear in your RSS feed, and GET /v1/status shows disabled with the failure count.
Re-enable (self-serve)
Fix your endpoint, then send any successful PUT /v1/filters/:id — an unchanged body is fine. That resets the failure counter to 0 and returns the endpoint to active. POST /v1/test-delivery works while disabled, so verify the fix first. No support ticket exists or is needed.