Skip to main content
Webhooks let you react to events in Postbreeze as they happen instead of polling. When a post publishes, an account disconnects, or a comment lands in the inbox, Postbreeze fires a signed HTTPS request to a URL you control.

How it works

  1. Register a webhook in the dashboard at Settings → Developers → Webhooks (or via the dashboard’s me/webhooks endpoints). Pick the events you want, paste your HTTPS URL, and Postbreeze generates a signing secret.
  2. Postbreeze signs every delivery with an HMAC-SHA256 of the request body. Verify the signature in your handler before doing anything with the payload.
  3. You respond 2xx within 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 a POST 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

The X-Postbreeze-Signature header has the form:
  • t is the Unix timestamp in seconds when Postbreeze signed the request.
  • v1 is the HMAC-SHA256 of <t>.<rawBody> using your webhook’s signing secret, hex-encoded.
To verify:
  1. Split the header by , and read t + v1.
  2. Compute HMAC-SHA256("<t>.<rawBody>", secret).
  3. Compare in constant time.
  4. Reject if the timestamp is more than 5 minutes off the current time (replay protection).
Verify against the raw request body, not a re-serialized JSON object. Any whitespace difference or key reordering changes the hash. Read the body as a string before parsing it.

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:
Accept either 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. Use X-Postbreeze-Delivery-Id as your deduplication key — it’s stable across retries of the same delivery. The cheapest pattern is a uniquely-indexed table:
Insert on receive; if the insert fails on the unique constraint, you have already processed this delivery — ack and skip.

Retry policy

A delivery is successful when your endpoint returns a 2xx 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 final webhook.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 2xx within 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 POST with Content-Type: application/json. Use the signing secret as your only authority — don’t IP-allowlist Postbreeze.
  • Handle the disabled_by_system event 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 HTTPShttp:// 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.