Currently Available: Need a skilled Software Developer for your next project?
Categories
APIs

Prevent Duplicate CRM Updates With a Webhook Gateway

When a business system sends contact, company, or deal changes to customer relationship management (CRM) software, it sends a webhook, an HTTP request that tells another service an event occurred. The sender may deliver the same event again after a timeout or failed response. If the integration creates a new CRM record for every delivery, it can create duplicates or repeat side effects such as sending notifications.

A webhook gateway receives these requests, checks them, and manages their delivery to the CRM. To prevent duplicates, the gateway must recognize events it has already received and make repeated CRM writes produce the same result. It stores each accepted event in a durable inbox, a database record saved before the gateway acknowledges the request. An idempotent write produces the same intended result when a worker repeats it. These controls reduce duplicate work, but they do not guarantee exactly-once effects across the sender, gateway, and CRM.

Separate duplicate events from duplicate records

A sender retrying one event is different from two separate events requesting the same business action. The sender might retry the same event, while two distinct events might each request an action that should happen only once. A retry can also create a second CRM record if the integration identifies records by a changing value instead of a stable business identifier.

The gateway should use the event or delivery ID that the provider documents for identifying repeats. It should associate the ID with the provider and, when needed, the account or subscription that received the event. For example, GitHub documents X-GitHub-Delivery as unique per event and says a redelivery keeps the original value. Other providers may define different identifiers and guarantees, so verify the relevant webhook contract before choosing a key.

Do not use the CRM record ID as the event key. Several legitimate events can update the same contact or deal. A payload hash can help flag identical request bodies, but it does not prove that two deliveries represent the same original event.

Accept the event before acknowledging it

A reliable gateway follows a short path: authenticate the request, save it durably, then acknowledge it and process it asynchronously.

Webhook provider
      |
      v
Authenticated gateway
      |
      v
Durable inbox and outbox
      |
      v
Worker
      |
      v
CRM upsert

The inbox is a database record of an event the gateway has accepted. The gateway should store it under a scoped event key and use a database rule that allows only one record for each key. The database then rejects simultaneous copies with the same key, even if two requests arrive before either request handler checks for an existing row. A separate “look up, then insert” check leaves a race in which both requests pass the lookup.

If an authenticated request repeats a key with the same event content, the gateway can acknowledge it without scheduling the event for processing again. If the same key arrives with different content, the gateway should keep the original and flag the conflict for investigation rather than silently replacing the stored event.

The gateway should return a successful HTTP response only after the event is durably accepted. A success response tells the sender to stop retrying; returning it before storage risks losing the event if the gateway crashes. At the same time, the gateway should avoid doing a slow CRM update while the sender waits. GitHub, for example, asks webhook receivers to respond within 10 seconds, but that deadline applies to GitHub and should not be assumed for other providers. Twilio also recommends acknowledging webhook requests promptly and handling longer work asynchronously in its webhook security guidance.

If the gateway stores an inbox row in one database and publishes a job to a separate queue, a crash between those actions can leave an accepted event with no job. The transactional outbox pattern closes that gap: the gateway writes the inbox row and an outbox item in one database transaction, then a separate dispatcher publishes the item. The AWS outbox guidance notes that downstream messages can still be duplicated, so the worker must also handle repeats safely.

Make CRM writes safe to repeat

Checking whether the gateway has already accepted an event answers, “Have I already accepted this source event?” A CRM upsert answers a different question: “Should this business entity update an existing CRM record or create one?” An upsert updates a matching record and creates one when no match exists, using a stable external identifier such as a source-system customer ID.

Salesforce’s REST API supports upserting a record by external ID. If that value matches multiple records, Salesforce returns an error rather than choosing one. The integration should therefore use an external ID configured to identify one CRM record, and handle a multiple-match error as a data-quality issue. See Salesforce’s upsert documentation.

An upsert does not prevent every duplicate effect. If the worker sends the same update twice, the CRM might still trigger a notification, increment a counter, or run another automation twice. The worker should track each event’s processing state and make any other effects safe to repeat. AWS describes idempotent handling as designing repeated processing so it does not change the result beyond the first successful application in its Lambda application guidance.

Treat timeouts and retries as normal failure cases

A timeout does not tell the gateway whether the CRM applied the request. The CRM might have saved the update while its response failed to reach the worker. If the worker treats every timeout as proof that nothing happened, a retry can repeat side effects.

The worker should retry a CRM write only when the operation is safe to repeat. For an uncertain result, it can first look up the CRM record by its external ID, if the CRM API supports that lookup, and reconcile the stored state. The worker should record each attempt and its outcome. It should send failures that have run out of retries, or cannot be retried, to a review state or a dead-letter queue, a separate place to hold jobs that cannot be processed, rather than silently discarding them.

Webhook retry policies vary by provider and product. Amazon EventBridge, for example, documents a default target retry policy of up to 24 hours and 185 attempts; after retries run out, an event is dropped unless a dead-letter queue is configured. That policy is specific to EventBridge and can be configured. Check the exact sender’s documentation instead of relying on a universal retry schedule. The EventBridge retry policy illustrates why the gateway needs its own durable record and failure handling.

Handle ordering separately

Deduplication prevents the same identified event from being processed repeatedly. It does not ensure that distinct events arrive in the order the source created them. If an older update reaches the worker after a newer one, the older values could overwrite current CRM data.

If the provider documents a sequence number or object version, the worker should use it to reject stale updates. Without a documented sequence number or object version, the integration can fetch the current source object before writing or process updates one at a time for each CRM record. A timestamp alone still does not guarantee ordering; the provider must document it as a reliable ordering mechanism. Supabase likewise advises webhook consumers to account for duplicate and out-of-order events in its platform webhook documentation.

Keep enough evidence to diagnose failures

The gateway should store the event key, source account, event type, receipt time, processing state, and a protected payload or reference to it. It should also track worker attempts, CRM responses, and the final outcome. These records help an operator tell whether the gateway received a duplicate delivery, a CRM write failed, or an event never reached the worker.

Choose how long to keep inbox records based on the provider’s retry and manual-redelivery behavior, along with replay, audit, and privacy requirements. A short broker-level deduplication window does not determine how long the gateway needs to remember an event. For example, EventBridge documents a five-minute suppression window for a particular custom event bus feature; that bounded feature does not guarantee exactly-once CRM effects.

What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *