Engineering

Webhook Retry Strategy for Reliable GTM Data Workflows

The Enrichments TeamOctober 19, 202611 min read
Abstract editorial illustration for “Webhook Retry Strategy for Reliable GTM Data Workflows” — Enrichments

A webhook retry strategy protects GTM data only when it treats delivery as separate from record updates. A sender can retry after a timeout even when your endpoint processed the event, and a delayed event can arrive after a newer enrichment has already changed the CRM record.

Why webhook retries can damage GTM data

A webhook retry strategy can damage GTM data when your receiver treats every delivery as a new instruction to update a record.

A sender usually cannot know whether your endpoint completed processing after a network timeout. Your endpoint may have received the event, started a CRM update, and lost its response before the sender saw an acknowledgment. The sender retries. Your receiver sees the same logical event again.

That is normal for webhook delivery retries. Your system should assume the same event can arrive more than once, out of order, or after a meaningful delay.

Without duplicate event handling, repeated deliveries can cause several problems:

  • A CRM record receives the same update repeatedly.
  • A workflow starts again for a contact already routed to an owner.
  • A delayed event overwrites a newer title, company, or location.
  • A downstream integration creates duplicate tasks or list memberships.
  • A transient CRM outage becomes a growing queue of unreconciled enrichment updates.

An acknowledgment only proves that your receiving endpoint accepted the delivery. It does not prove that every downstream step succeeded.

For example, an endpoint can accept an enrichment-related event, write a receipt record, and return success. It may still fail later while updating the CRM. That is not a reason for the sender to redeliver the original event. It is a reason for your receiver to recover its own incomplete processing.

Keep these states separate:

  • Delivery accepted: Your endpoint received and stored the event.
  • Event processed: Your worker evaluated the event and decided what should change.
  • CRM update applied: The target record update completed.
  • Follow-up complete: Any routing, task creation, or workflow trigger finished.

When those states are collapsed into a single request handler, a retry can repeat side effects that already happened.

Define which failures deserve a retry

Retry failures that may resolve without changing the event; stop retrying failures that require a human or configuration change.

A timeout, temporary server error, unavailable dependency, or short-lived CRM outage can justify a retry. The event is still valid. The destination may simply be unavailable.

A malformed payload, rejected authentication, unsupported event shape, or invalid field mapping is different. Retrying the same request will not fix it. It can make the failure harder to diagnose by filling logs and queues with copies of the same bad event.

Use a small failure taxonomy in the receiver.

Transient failures

These usually belong on a retry path:

  • Network timeouts.
  • Temporary failures from the receiving service.
  • Temporary CRM API failures.
  • Rate limits or dependency throttling.
  • Database contention that prevents the receiver from claiming the event.

For these failures, retain the original event and retry processing later. Do not create a new event record for every attempt.

Permanent failures

These usually belong in a review path:

  • Invalid JSON or a payload that does not match your expected contract.
  • Missing identity data required for matching.
  • Rejected authentication.
  • A mapping that targets a field your CRM does not accept.
  • A business rule that prevents automatic updates.
  • An enrichment value that conflicts with a protected CRM field.

A permanent failure needs a visible reason and an owner. It should not disappear into a generic retry loop.

Assign failure ownership

A useful operating model makes the owner explicit:

Failure sourceTypical ownerRecovery action
Sender contract or authenticationSender integration ownerCorrect the configuration or payload contract
Receiving endpoint or event processorApplication ownerRestore the service, fix processing logic, replay retained events
CRM dependency or field rulesGTM systems ownerResolve the CRM issue, adjust mappings, resume pending updates

This separation matters during incident response. If a receiving endpoint is healthy but the CRM rejects a field update, changing webhook acknowledgment behavior will not repair the CRM update.

Do not retry every non-success response without classification. A malformed event or broken authentication setting can become a retry storm that hides the original failure.

Use backoff, retry limits, and a dead-letter path

Use exponential backoff with jitter for temporary failures, then move unresolved events to a dead-letter queue for review and controlled replay.

Exponential backoff increases the wait between attempts. Jitter adds a random variation to that delay. Together, they reduce the chance that every queued event retries at the same moment when a receiver or CRM recovers.

This matters after an outage. A receiver that immediately replays its full backlog can create a second outage in the CRM, database, or queue consumer. Backoff gives dependencies time to stabilize.

Your retry policy should be bounded. Do not allow an event to retry forever. At some point, the event should leave the automatic path and enter a dead-letter queue.

A dead-letter queue is not a trash folder. It is an operational record of events that need a decision.

For each dead-letter event, retain:

  • The original event body.
  • The event identifier supplied by the sender, if the contract includes one.
  • Your internal receipt identifier.
  • Receipt time and source time.
  • Delivery attempts and processing attempts.
  • Failure classification and the latest error detail.
  • The target CRM record, if matching succeeded.
  • The intended field updates.
  • The current processing state.
  • The version of the mapping or update policy used when processing began.

That record lets you answer a practical question: should you replay the event as originally received, reprocess it under a corrected mapping, or retain it for manual review?

Do not rebuild a payload from application logs. Logs may be sampled, truncated, redacted, or missing the exact state needed to reproduce the failure. Replay from the durable event record you persisted at receipt time.

Alert on dead-letter growth and repeated permanent failures. The alert should identify the integration, failure class, and affected downstream dependency. It should not require someone to inspect every retry attempt manually.

For manual recovery, use a controlled process:

  • Confirm the underlying failure is resolved.
  • Review whether the event is still fresh enough to apply.
  • Re-evaluate identity matching if the CRM record may have changed.
  • Replay the stored event through the normal processor.
  • Preserve the old attempt history rather than replacing it.

That last point matters. A replay should create a new processing attempt against the same durable receipt record, not erase evidence of why the event failed earlier.

Make the receiving endpoint idempotent

An idempotent consumer records an event before processing it and ensures repeated deliveries do not repeat completed side effects.

Your receiving endpoint should persist a durable idempotency record as early as possible. The record can use the sender’s event identifier when one exists, paired with the event source or integration name. If the sender does not provide a stable identifier, derive a receiver-side key from stable event attributes with care. A timestamp alone is not a safe duplicate key.

The idempotency record should contain a processing state, result, and ownership marker for concurrent work.

Useful states include:

  • received: The event body is stored but no worker has claimed processing.
  • processing: A worker owns the current attempt.
  • applied: The intended downstream changes completed.
  • deferred: The event requires retry after a temporary failure.
  • review: The event needs manual action.
  • rejected: The event cannot be processed under the current contract.

When the same event arrives again, the endpoint checks this record before taking action:

  • If processing already completed, return an acknowledgment without repeating side effects.
  • If another worker is processing the event, avoid starting a competing update.
  • If a prior attempt failed temporarily, allow a controlled resume.
  • If the event was rejected, return the appropriate outcome without silently retrying it.

This protects against concurrent duplicate deliveries as well as delayed retries.

Separate event receipt from the downstream write. The request handler should store the receipt and hand processing to a durable worker or queue. The worker can then update the CRM, create follow-up work, or retry a dependency without requiring the sender to redeliver the event.

The same principle applies when your system calls an enrichment API. An API request is safely deduplicated only when the original caller uses the same Idempotency-Key header on the retry. Without that header, a retry is a new request. It creates a new job, takes a new hold, and can be charged as a separate request.

If you call an API from a webhook consumer, persist the idempotency key alongside the event state. Reuse it only for a retry of that same original request.

Protect record freshness during delayed delivery

A delayed enrichment event should not automatically overwrite a newer CRM value.

Treat enrichment output as an input to a controlled update policy. It is not an instruction to replace every field on the matched record.

Store source timestamps with both the inbound event and the CRM field update. Then define rules per field. Your policies may differ for email, title, employer, location, and company data.

A practical policy can include rules such as:

  • Apply a value only when the incoming source timestamp is newer than the last accepted source timestamp for that field.
  • Do not replace a manually protected CRM value automatically.
  • Do not replace a verified work email with an older or less trusted value.
  • Retain a conflicting update for review when the identity match is uncertain.
  • Preserve prior values and update history when a change is significant.

For work emails, distinguish the address from the deliverability result. Enrichments returns work email addresses only when you request the email field. A returned address can carry an email status of verified, probable, unverified, risky, undeliverable, or unknown. Your CRM update policy should decide which statuses are eligible for automatic use.

Do not apply the same treatment to phone values. A mobile number is available through the phone field when sourced from a LinkedIn profile URL, but it does not carry a deliverability verdict. A number being available is not a representation that it is lawful to call.

Identity changes need their own conflict rules. A person can change employers. A company can change domain. A title can change while the CRM still holds an older value.

Use explicit matching logic:

  • Match people using stable identifiers where available, such as a LinkedIn URL or work email.
  • Treat name-plus-employer matching as less durable than a stable identifier.
  • Confirm company moves before replacing employer and company-domain fields.
  • Avoid merging records solely because names look similar.
  • Send ambiguous matches to review rather than applying updates automatically.

A delayed event should remain for review when it conflicts with a newer field value, maps to multiple CRM records, lacks sufficient identity evidence, or would trigger a consequential downstream workflow.

Pair webhooks with job polling when needed

When an enrichment workflow is asynchronous, retain the job URL or job identifier and poll for final status instead of assuming the original request completed the work.

This is especially important when a webhook-like internal event starts an API request and another system needs to apply the results later. Your event processor should store the returned job metadata with its own durable state.

For the Enrichments REST API, every search, enrichment request, and job poll returns an envelope with job, results, page, and estimatedCredits. Read the state from job.status.

A job can be queued, running, succeeded, empty, failed, or cancelled. Only succeeded, empty, failed, and cancelled are terminal states.

Do not assume a request is asynchronous in every case. Small searches and small enrichments can return inline with populated results and a terminal job status. Larger requests, requests for the email field, and requests sent with async: true or Prefer: respond-async are queued. For queued work, results remains null until the job reaches a terminal state.

Poll the job endpoint:

GET /api/v1/jobs/{id}

The job endpoint always responds successfully at the HTTP layer, so your consumer must inspect job.status rather than treating the response itself as proof that the enrichment succeeded.

When results are available, process them as item wrappers. Each item contains:

  • position
  • status
  • data
  • evidence
  • error

The resolved person or company record is inside data. Item outcomes can be pending, running, succeeded, not_found, or failed.

That distinction prevents a common recovery mistake: treating the overall job as a guarantee that every requested row produced data. A job can finish while individual items have different outcomes. Apply CRM updates only for the item wrappers your update policy accepts.

Use the opaque forward-only cursor in page when retrieving additional result pages. Do not reconstruct paging from assumptions about offsets or result ordering.

The API documentation and generated OpenAPI document at /api/v1/openapi should be the source of truth for request and response schemas. If you are integrating an agent workflow, the MCP documentation describes the available tools and the free get_job polling tool.

Before you ship, test the recovery path deliberately:

  • Simulate a timeout after the receiver stores an event but before it returns an acknowledgment.
  • Deliver the same event again while the original processing attempt is still running.
  • Replay an event after a temporary CRM failure.
  • Send a delayed event after a newer CRM field update.
  • Force an item-level not_found or failed result and confirm it does not create an invalid CRM update.
  • Move an event to the dead-letter queue and replay it after correcting the root cause.
  • Retry an API request with the same Idempotency-Key and confirm your integration treats it as the same logical request.

A reliable GTM workflow does not depend on perfect delivery timing. It preserves the event, records what happened, applies controlled updates, and makes recovery safe when timing is imperfect.

Frequently asked questions

What is a webhook retry strategy?
A webhook retry strategy defines how a receiver handles temporary delivery or processing failures without repeating completed side effects. It should treat delivery acceptance, event processing, CRM updates, and follow-up work as separate states.
Which webhook failures should be retried?
Retry temporary failures such as network timeouts, temporary service or CRM API failures, rate limits, dependency throttling, and database contention. Send malformed payloads, rejected authentication, invalid mappings, and protected-field conflicts to a review path instead.
Why should webhook consumers be idempotent?
Webhook senders can retry after a timeout even when an endpoint processed the event. An idempotent consumer persists an event record before processing and uses it to prevent repeated deliveries from repeating completed downstream side effects.
What should a dead-letter queue retain for a failed webhook event?
Retain the original event body, sender event identifier when available, internal receipt identifier, timestamps, attempt history, failure details, target record, intended updates, processing state, and mapping or update-policy version. This supports controlled replay or manual review.
How can delayed webhook events avoid overwriting newer CRM data?
Store source timestamps with inbound events and field updates, then apply field-level update rules. A delayed event should be reviewed when it conflicts with a newer value, has uncertain identity matching, maps to multiple records, or would trigger a consequential workflow.

Keep reading

Put this into practice

Enrichments resolves people and companies from a REST API, an MCP server, the chat agent or a CSV upload, checks every email address it finds, and bills you only for the data that comes back.

Start enriching for free