How it works
- Register a webhook in the dashboard at Settings →
Developers → Webhooks (or via the dashboard’s
me/webhooksendpoints). Pick the events you want, paste your HTTPS URL, and Postbreeze generates a signing secret. - Postbreeze signs every delivery with an HMAC-SHA256 of the request body. Verify the signature in your handler before doing anything with the payload.
- You respond
2xxwithin 10 seconds. Anything else is a failure and triggers the retry schedule below.
Webhooks are outbound only — Postbreeze pushes events to your
endpoint. You don’t poll for them, and you don’t need an API key on
your endpoint.
Quick start
handler.ts
Request format
Every delivery is aPOST with Content-Type: application/json. The
body is the same shape for every event — the per-event detail lives
on payload.
Headers Postbreeze sets
You can also configure custom headers on each webhook (e.g.
X-Internal-Token: …) — Postbreeze merges them in but cannot
override the default headers above.
Signature verification
TheX-Postbreeze-Signature header has the form:
tis the Unix timestamp in seconds when Postbreeze signed the request.v1is the HMAC-SHA256 of<t>.<rawBody>using your webhook’s signing secret, hex-encoded.
- Split the header by
,and readt+v1. - Compute
HMAC-SHA256("<t>.<rawBody>", secret). - Compare in constant time.
- Reject if the timestamp is more than 5 minutes off the current time (replay protection).
Rotating the secret
Rotate the signing secret from Settings → Developers → Webhooks → Rotate secret. During the rotation window (24 hours by default), Postbreeze signs every delivery with both secrets:v1 value. After the window closes the old secret
stops being attached.
Idempotency & deduplication
Webhooks are delivered at least once. A receiver that times out on attempt 1 and succeeds on attempt 2 receives the same event twice. UseX-Postbreeze-Delivery-Id as your deduplication key — it’s
stable across retries of the same delivery. The cheapest pattern is
a uniquely-indexed table:
ack and skip.
Retry policy
A delivery is successful when your endpoint returns a2xx
response within 10 seconds. Anything else fails the attempt.
After 6 attempts the delivery is dropped to the failed-deliveries
list (visible in the dashboard) and does not retry further.
Which failures retry?
Auto-disable
After 15 consecutive terminal failures Postbreeze automatically disables the webhook and fires a finalwebhook.disabled_by_system
event to the same endpoint (best-effort). Re-enable it from the
dashboard once the endpoint is back.
Event catalogue
All event keys, in canonical order. Schemas use TypeScript-style notation —string | null means nullable.
Posts
post.published payload
post.partial payload
post.platform.published payload
post.platform.failed payload
Accounts
account.connected payload
account.disconnected payload
Comments
comment.received payload
Webhook lifecycle
These are meta-events about the webhook itself — useful for detecting your endpoint being auto-disabled.webhook.disabled_by_system payload
Best practices
- Verify the signature on every delivery. Don’t skip in development.
- Deduplicate by
deliveryId. Treat at-least-once as a guarantee, not a wish. - Acknowledge quickly. Return
2xxwithin 10 seconds; queue heavy work for a background worker. - Treat events as notifications, not state. If you missed a delivery (auto-disable, your endpoint was down), re-fetch from the API to reconcile — never trust the webhook to be your only source of truth.
- Lock the receiver down. Reject requests that aren’t a
POSTwithContent-Type: application/json. Use the signing secret as your only authority — don’t IP-allowlist Postbreeze. - Handle the
disabled_by_systemevent explicitly. Page on it; otherwise you’ll only notice events have stopped flowing when something downstream breaks. - Use the dashboard’s “Deliveries” tab to inspect recent failures — every attempt logs its HTTP status, response body (truncated), and the request headers we sent.
Limits
- Max 50 webhooks per user
- Max payload size persisted: 64 KB (large payloads still deliver, but the response/request bodies stored for the deliveries log are truncated)
- Custom headers: up to 10 per webhook, max 1 KB total
- URL must be HTTPS —
http://is rejected at create time - URL must resolve to a public IP — RFC1918, link-local, and IMDS hostnames are rejected at both create time and delivery time (DNS rebinding protection)
Managing webhooks
Webhooks are managed from the dashboard at Settings → Developers → Webhooks — create, rotate secrets, disable, send a test event, and browse delivery history. The management endpoints are cookie-only (/me/webhooks); API keys can’t mint or revoke webhooks.