Architecture

ERP to Ecommerce Sync Patterns: Polling, Webhooks and Event Queues

Compare polling, webhooks and queue-based sync for connecting an ERP to an online store, using Business Central webhooks as the worked example, with reconciliation, ordering and duplicate handling.

By EpiFive Editorial TeamReviewed/updated 8 min readHow we check articles
On this page

Use webhooks or short-interval polling to learn quickly that something changed in the ERP or the store, put each change on a queue, process it idempotently, and run a scheduled reconciliation job to catch whatever the real-time path missed. No single mechanism is reliable alone: polling is simple but slow and wasteful, webhooks are fast but not guaranteed, and queues add resilience but not discovery. This article compares the three and uses Business Central webhooks as a concrete example.

Who this is for

This is for developers and integration architects connecting an ERP such as Microsoft Dynamics 365 Business Central to an online store or marketplace for products, stock, prices, customers and orders. By the end you will know which sync mechanism fits each data type, what to build around it, and which failure modes to test.

What typically syncs, and in which direction

DataUsual masterDirectionTolerance for delay
Products, descriptions, categoriesERP or PIMERP to storeMinutes to hours
Prices and discountsERPERP to storeMinutes
Stock levelsERPERP to storeSeconds to minutes; oversell risk
CustomersOften bothTwo-wayMinutes
OrdersStoreStore to ERPSeconds to minutes
Shipments and invoicesERPERP to storeMinutes

Name one system of record for every field. Two-way sync without a clear master per field produces overwrite loops. Where both sides can edit, decide a rule, such as "latest modified timestamp wins" or "ERP wins for price".

Pattern 1: Polling

The integration asks the source for changes on a schedule, typically with a filter on a modified timestamp.

GET /api/v2.0/companies({companyId})/items?$filter=lastModifiedDateTime gt 2026-10-25T08:00:00Z

The filter above is illustrative; check which fields and filters your ERP's API supports.

  • Strengths: simple, works with any source that has an API, easy to reason about and to replay.
  • Weaknesses: latency equal to the polling interval, wasted calls when nothing changed, and exposure to API rate limits. See Microsoft Graph throttling and Dataverse service protection limits for how throttling behaves on Microsoft platforms.
  • Key technique: persist a high-water mark (the last modified timestamp processed), and overlap each window slightly so that records committed late are not missed. The overlap will return some records twice, so processing must be idempotent.

Pattern 2: Webhooks

The source calls your endpoint when data changes. Business Central documents this clearly, so it makes a good example.

How Business Central webhooks work

  • Register a subscription with POST /api/v2.0/subscriptions, passing notificationUrl, the resource to watch and an optional clientState.
  • Handshake. Business Central calls your notificationUrl with a validationToken query parameter. You must return that token in the response body with status 200. The handshake is mandatory when creating and when renewing a subscription.
  • clientState is returned in each notification, so you can use it as a shared secret to verify the sender.
  • Expiry. Subscriptions expire after three days unless renewed with a PATCH request, which repeats the handshake. The expirationDateTime property shows when.
  • Payload. A notification lists subscriptionId, clientState, expirationDateTime, resource, changeType and lastModifiedDateTime. The change type is created, updated, deleted or collection. There is no record data in the payload, so you call the API to read the current state.
  • Batching and delay. Business Central does not notify immediately. By default it waits 30 seconds after the first change to an entity, and if more than 1,000 records change during that delay, it sends a single collection notification whose resource includes a filter you use to fetch all matching entities. Otherwise it sends one notification per change.
  • Retries. If Business Central can't reach the subscriber, it retries over the next 36 hours, but only if you answer with 408, 429 or a 5xx status. Any other response code means no retries and the subscription is deleted.

A minimal registration looks like this:

POST https://{businesscentralPrefix}/api/v2.0/subscriptions
Content-type: application/json
 
{
  "notificationUrl": "https://integration.contoso.com/hooks/bc",
  "resource": "/api/v2.0/companies(f64eba74-dacd-4854-a584-1834f68cfc3a)/items",
  "clientState": "a-long-random-secret"
}

The details of subscriptions, including supported entities and restrictions, are in Business Central API webhooks and subscriptions.

Webhook strengths and weaknesses

  • Strengths: low latency, no wasted calls.
  • Weaknesses: delivery is not guaranteed, receivers must be available and fast, subscriptions can lapse, and some entities are not supported. For example, Business Central webhooks are not available for APIs on temporary tables, system tables or API-type queries. Notifications for changes made by users who cannot schedule job queues may be delayed until another change by an authorised user.

Shopify's webhook guidance, as another example from the store side, says delivery isn't guaranteed, so apps shouldn't rely solely on webhooks and should run reconciliation jobs that periodically fetch data using updated_at filters. It also says to verify HMAC signatures, ignore duplicates using the X-Shopify-Webhook-Id header, and notes that ordering is not guaranteed within or across topics for the same resource, so use the X-Shopify-Triggered-At header or the payload's updated_at field to sequence events.

Pattern 3: Event queues

A queue is not a way to discover changes; it is how you absorb and process them safely. Put the receiving endpoint in front of a queue such as Azure Service Bus:

ERP webhook / poller  -->  receiver (validate, enqueue, return 200)  -->  queue  -->  workers  -->  store API
                                                                           |
                                                                           +--> dead-letter queue
  • The receiver does only three things: validate the sender (token handshake, clientState or signature), enqueue a message and return a success status quickly. This protects subscriptions, because a slow or failing receiver can cause retries or, in the case of Business Central, a deleted subscription.
  • Workers read the queue, call the ERP API for the current record, transform it and call the store API.
  • Failures retry, and messages that keep failing go to the dead-letter queue for investigation.

Service Bus peek-lock delivery is at least once, so duplicates are possible; set MessageId from a stable key and make workers idempotent. The mechanics of queue-based buffering are covered in Queue-Based Load Leveling with Azure Service Bus, and receiver design in Webhooks that don't double-charge.

Choose a pattern

RequirementRecommended approach
Source has no webhooksPoll with a high-water mark, plus a queue for processing
Updates must appear in secondsWebhooks into a queue, plus periodic reconciliation
Bulk imports of thousands of recordsBatch pull on a schedule; do not trigger a webhook storm
Store API rate limits are tightQueue with a bounded number of workers
Compliance requires an audit trailPersist every message and every processing outcome

The strongest default is a hybrid: webhooks for speed, a queue for resilience and a scheduled reconciliation job for correctness.

Build the reconciliation job

Reconciliation is the part teams skip and later regret. A simple version:

  1. On a schedule, such as hourly for stock and nightly for everything else, fetch records modified since a safe point in the past from the ERP.
  2. Compare them with the store, using a hash of the synced fields or the modified timestamp.
  3. For each difference, enqueue the same message the webhook path would have created.
  4. Report counts of differences found. A rising count signals that real-time delivery is failing.

Because reconciliation reuses the normal processing path, it needs no special logic and exercises the idempotency you already built.

Handle ordering and duplicates

  • Duplicates: deduplicate on a stable key such as entityId + lastModifiedDateTime, or the sender's event ID where one exists.
  • Out of order: do not blindly apply the payload. Fetch the current record, and skip the update when the target already holds data with an equal or newer modified timestamp.
  • Deletes: decide whether a deleted change maps to a delete, an unpublish or an archive in the store. Deleting orders or customers in a store can break history.
  • Partial failures: if a product update succeeds but its stock update fails, retry the failed part rather than repeating the whole record.

Troubleshooting

Notifications stopped arriving. Check whether the Business Central subscription expired after three days, or was deleted after your endpoint returned a status outside 408, 429 and 5xx. Recreate it and add a renewal job.

Subscription creation fails. Verify that your endpoint answers the validationToken request with status 200 and the token in the body, within a short time.

Stock is wrong in the store. Compare the modified timestamps on both sides and run reconciliation. If webhooks batch into collection notifications, make sure you handle that change type by fetching with the supplied filter.

Duplicate orders in the ERP. Store orders must carry a unique external reference, and the worker must check it before creating an order. See Idempotent API Design.

The queue backs up. The store or ERP API is throttling. Reduce worker concurrency and honour Retry-After headers.

Checklist

  • One system of record per field.
  • Webhook or poller feeds a queue; the receiver returns quickly.
  • Business Central subscriptions renewed before the three-day expiry, with alerts on failure.
  • Workers idempotent, keyed on a stable identifier.
  • Modified-timestamp comparison to handle out-of-order events.
  • Scheduled reconciliation job reusing the normal path.
  • Dead-letter queue monitored.

References

Questions people ask

Are webhooks enough to keep an ERP and a webshop in sync?

Not on their own. Shopify's documentation states that webhook delivery isn't guaranteed and recommends reconciliation jobs that periodically fetch data, and Business Central deletes a subscription if the receiver answers with a non-retryable error. Combine webhooks with a scheduled catch-up.

Do Business Central webhook notifications contain the changed data?

No. Each notification identifies the subscription, the resource, the change type and the last modified time, and your integration then reads the current record through the API. That makes notifications small and means you always process the latest state.

How long does a Business Central webhook subscription last?

Subscriptions expire after three days unless they are renewed with a PATCH request, which also requires the validationToken handshake. Build a renewal job and alert when it fails, otherwise notifications stop silently.

Should I use polling or webhooks?

Poll when the source has no webhooks, when volumes are low or when near-real-time is not needed. Use webhooks when changes must propagate quickly. Whichever you choose, keep a periodic reconciliation job as the safety net.

ERPWebhooksBusiness CentralAzure Service Bus
  1. Business Central API Webhooks: Create, Validate and Renew Subscriptions

    Receive Business Central change notifications instead of polling: build a validating receiver in Azure Functions, create a subscription, process notifications and renew before the three-day expiry.

    Architecture10 min read
  2. Dataverse Integration Choices: Webhooks vs Service Bus vs Plug-ins vs Flows

    Compare Dataverse plug-ins, webhooks, Azure Service Bus endpoints and Power Automate triggers for sending events to external systems, with limits, failure behaviour and setup steps.

    Architecture12 min read
  3. Webhooks that don't double-charge: retries, idempotency and dead letters

    Build webhook receivers that survive retries and duplicate deliveries: verify, deduplicate on the event ID, queue to Azure Service Bus, process idempotently and handle dead letters.

    Architecture12 min read
  4. Queue-Based Load Leveling Pattern with Azure Service Bus

    Smooth traffic spikes by putting an Azure Service Bus queue between your API and a back end, with an Azure Functions consumer, bounded concurrency, dead-lettering and duplicate handling.

    Architecture9 min read
  5. Transactional Outbox Pattern: Publish Events Reliably from a Database

    Stop losing events between your database and message broker. Implement the transactional outbox with PostgreSQL and Azure Service Bus: outbox table, polling relay, SKIP LOCKED and idempotent consumers.

    Architecture9 min read
  6. Azure Service Bus SBMP Retirement: Migrate to Azure.Messaging.ServiceBus

    SBMP and the WindowsAzure.ServiceBus and Microsoft.Azure.ServiceBus libraries reached end of support on 30 September 2026. Find affected code, switch to AMQP and move to Azure.Messaging.ServiceBus.

    Architecture10 min read