Developer documentation

Customer webhooks

Signatures and duplicate handling

Customer webhooks send publication results and account disconnections to your HTTPS endpoint. Deliveries can be repeated: verify the signature before processing and deduplicate by event ID.

Delivery contract

The JSON body contains version, id, type, workspaceId, occurredAt, status, and either postId or connectionId. Types are post.published, post.partial, post.failed, and connection.disconnected. Post content and platform tokens are excluded.

A 2xx response completes delivery. Timeouts, connection failures, 408, 429, and 5xx receive limited retries. Other 3xx and 4xx responses are final failures. A lost response can result in a duplicate even after your server processed the event.

Verify the raw-body signature

Use the secret shown once at subscription creation. Verify X-MB-Timestamp (Unix seconds), X-MB-Event-Id, and the original body bytes with HMAC-SHA256. The signature format is v1=<hex>; allow a five-minute clock difference. Do not reserialize parsed JSON.

Node.js verification outline

// Keep the original request body bytes; do not stringify parsed JSON.
const expected = createHmac("sha256", secret)
  .update(timestamp + "." + eventId + ".", "utf8")
  .update(rawBody).digest("hex");
// Compare Buffer values with timingSafeEqual after checking their lengths.
// Reject timestamps more than five minutes from your server clock.
// Store (workspaceId, eventId) uniquely in the same transaction as your work.

Process each event once

A retry keeps the same event ID and body but uses a fresh timestamp and signature. Check that the signed header ID matches the body. Insert a unique (workspace_id, event_id) key and apply your business change in one database transaction. Return 2xx for a duplicate; roll back and return 5xx on processing failure. Protect external email or payment work with its own idempotency key or outbox.

Keep signing secrets on your server. Check delivery attempts in the webhook dashboard and retry only confirmed failures after checking whether your server already processed the ID.