Signed webhooks
Subscribe an HTTPS endpoint to committed event topics. Trestle signs every delivery and runs it through the durable retry and dead-letter engine.
Configure a target
Open Integrations, enter a descriptive name, the receiver’s public HTTPS URL and a comma-separated topic list. The signing secret is displayed once; copy it into the receiver’s secret store before leaving the page.
Name: Billing projection
HTTPS endpoint: https://hooks.example.com/trestle
Topics: record.created,record.updatedThe administrative API accepts the same model:
curl -X POST "$TRESTLE_URL/admin/v1/webhooks" \
-H "Cookie: trestle_admin=$ADMIN_SESSION" \
-H "X-Trestle-CSRF: $CSRF" \
-H "Content-Type: application/json" \
--data '{
"name":"Billing projection",
"url":"https://hooks.example.com/trestle",
"topics":["record.created","record.updated"]
}'
Verify a delivery
Trestle-Delivery: del_...
Trestle-Timestamp: 178...
Trestle-Signature: v1=hex-hmac-sha256Compute HMAC-SHA256 over timestamp + "." + rawBody, compare signatures in constant time, reject stale timestamps and deduplicate the delivery ID before applying side effects.
const signed = `${timestamp}.${rawBody}`;
const expected = createHmac("sha256", secret).update(signed).digest("hex");
if (!timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return new Response("invalid signature", {status: 401});
}
Envelope and delivery semantics
{
"version":"1",
"id":"del_...",
"topic":"record.created",
"collection":"issues",
"recordId":"rec_...",
"payload":{}
}Delivery is at least once. A non-2xx response, timeout or connection failure retries with backoff. Inspect the corresponding job for attempts and bounded diagnostics; repair the receiver before retrying a dead job.
Local development
Private and loopback destinations are deliberately refused. Use a temporary public HTTPS tunnel or a deployed development receiver, never weaken SSRF protections to reach localhost.
Dogfood path
CP21 saves a disabled webhook without relying on live DNS, then retains delivery-time DNS resolution and private-address refusal as security invariants. See the automation exercise.