Integration · outbound events
Outbound webhooks
Receive publish outcomes and fresh performance evidence without polling. Every request is signed; delivery is at least once.
Current contract
Events
post.publishedPublish succeeded
The post reached its terminal published state. Destination evidence is included.
post.failedPublish did not fully succeed
The post is failed or partial. Inspect the payload status and destination errors.
performance.updatedNew measurement stored
A new evidence-backed metric snapshot is available. Null values remain null.
Stable envelope
Request
Postdom sends an HTTPS POST with the exact JSON body shown by the event. Use Webhook-Id as the idempotency key for your receiver.
{
"id": "0e386be3-…",
"type": "post.published",
"created_at": "2026-08-28T04:12:00.000Z",
"data": {
"post_id": "53b81c96-…",
"status": "published",
"destinations": []
}
}Headers
Webhook-Id identifies the event, Webhook-Timestamp is Unix seconds, and Webhook-Signature contains the v1 HMAC.
HMAC-SHA256
Verify before processing
Sign the raw request body—not parsed and re-serialized JSON—with event_id.timestamp.body. Compare signatures in constant time and reject stale timestamps.
import { createHmac, timingSafeEqual } from "node:crypto";
const eventId = request.headers.get("Webhook-Id");
const timestamp = request.headers.get("Webhook-Timestamp");
const supplied = request.headers.get("Webhook-Signature");
const body = await request.text();
// Reject stale timestamps first; five minutes is a sensible replay window.
const expected = "v1=" + createHmac("sha256", process.env.POSTDOM_WEBHOOK_SECRET)
.update(eventId + "." + timestamp + "." + body)
.digest("hex");
const valid = supplied?.length === expected.length &&
timingSafeEqual(Buffer.from(supplied), Buffer.from(expected));Secrets are shown once when an endpoint is created or rotated. Store them in a secret manager. New deliveries use the rotated secret immediately; a request already in flight may still use the previous secret for up to 15 seconds.
At least once
Delivery and retries
Any 2xx response completes a delivery. Redirects, timeouts, network failures, and other HTTP statuses are retried after approximately 1 minute, 5 minutes, 30 minutes, 2 hours, and 12 hours. The sixth failed attempt is exhausted.
Make receivers idempotent
A worker may retry after an ambiguous network result. Persist Webhook-Id before applying the event so a duplicate delivery does not duplicate downstream work.
Pausing or removing an endpoint cancels queued retries. A request already in flight may still finish.
Authenticated dashboard
Manage endpoints
Workspace owners and admins can add up to five HTTPS endpoints from Accounts → Developer webhooks, choose event types, pause delivery, rotate the secret, remove the endpoint, and inspect recent attempts. Workspace members can inspect status but cannot change it.
Endpoint URLs must use public DNS and the default HTTPS port. Postdom rejects IP literals, private or reserved DNS answers, credentials, fragments, and redirects.