Skip to content
campaiDeveloper
campai.com

Treat a webhook as a notification that may be retried or missed. Your receiver should acknowledge durable acceptance quickly, and a separate worker should reconcile the resulting business state. A successful HTTP response does not prove that your downstream action finished.

Start with the signature and inbox implementation in Build a webhook receiver. This guide describes the current CRM webhook worker; it does not describe the separate legacy webhook system.

What you’ll have at the end

A recovery procedure for duplicated, delayed, and missed notifications.

  1. Delivery
  2. Durable acceptance
  3. Repeatable processing
  4. Reconciliation
Before you start
  • A receiver that persists accepted deliveries
  • A local processing ledger
  • A full-scan reconciliation job
On this page

The reviewed worker uses a 10-second HTTP timeout and queues three attempts. Its exponential backoff starts at five minutes: the next attempts are approximately five minutes and then ten minutes after the preceding failed attempt. Queue load and execution time can add delay, so these are not exact delivery times or a service guarantee.

After the third failed attempt, that job stops retrying. Do not expect an indefinite replay until your server returns. A prolonged outage requires reconciliation from current API records.

Header Meaning
X-Signature Base64 HMAC-SHA512 signature over the request body
X-Webhook-ID The subscription that generated this delivery
X-Organization The originating organization
X-Request-ID An identifier created for this individual attempt
X-Webhook-Final-Attempt The worker's string true or false indicator for its final attempt

X-Request-ID changes between attempts. It is useful for logs, not for deduplicating the underlying event. The signature covers the body, not all headers. Bind the expected subscription and organization to receiver configuration, and do not use a caller-controlled header to authorize another organization's data.

Persist a digest of the verified raw body together with the configured subscription identity. Insert or claim it atomically in your worker's database. The receiver example's file name provides an inbox key, but a completed-item ledger is still needed after the file is consumed.

A worker can crash after a downstream action succeeds but before recording completion. Prevent duplicates at that boundary too: use the downstream service's documented idempotency mechanism, or persist an operation ID and reconcile its result. A local “processed” flag alone cannot atomically commit an unrelated remote API action.

Where possible, make work express the desired state: “refresh this contact projection” is easier to repeat safely than “increment the member count.” Re-fetch through Get contact and replace the small owned projection. For deletion, handle a missing record explicitly instead of retrying a GET forever.

The producer also has queue deduplication based on subscription, event type, and record-content hash. That is an implementation optimization, not a substitute for consumer idempotency or a complete audit stream. Do not build accounting totals by counting notifications.

An older notification may be processed after a newer change, especially when retries or your own queue are involved. Prefer refreshing current state. If you must apply event data directly, define an ordering rule using the actual event contract and a per-record checkpoint; do not invent a monotonically increasing sequence number.

Keep operationTime as source metadata. Do not impose a short replay-expiry window that rejects legitimate delayed retries. Signature verification proves possession of the signing secret; it does not by itself prevent replay of a previously valid body.

Open the developer area's Logs and select webhook logs. Inspect the event type, destination URL, attempt, status, time, request payload, and response. Keep these records restricted because the payload can contain personal data.

The worker records each attempt. A network error can have no HTTP status; a 401 can indicate a signature mismatch; a 404 often means the receiver route is wrong; a 503 should prompt a check of receiver storage or availability. Compare the logged attempt with your own request correlation without copying full records into public diagnostics.

The implementation remembers the first failed response. A later failure at least six hours after that first failure can mark the subscription failed. This is evaluated during failures, not by an exact six-hour timer. Failed subscriptions are skipped. A success clears the first-failure timestamp but does not itself reactivate a subscription already marked failed. Inspect its state and use the product's activation control after repairing the receiver.

Track the last successful reconciliation independently of the last webhook. Run periodic API scans for the records your integration owns, such as List contacts. Compare current state with your stored projection and repair differences. Use member synchronization for paging and absence handling.

Booking and event changes require polling because the current CRM webhook set does not include them. See Synchronize bookings and Show events on your website.

Test a receiver outage lasting beyond all three attempts, duplicate acceptance, a worker crash after its downstream action, an out-of-order update, and a subscription in failed state. Recovery should produce the correct current records without creating a second invoice, sending a second message, or issuing a second credential.

Check your result

Same delivery twice
No duplicate downstream effect.
Old event arrives last
Current authoritative state wins.
Receiver was offline
Reconciliation recovers beyond the retry window.
Continue with Keep member data in sync