ERP integrations fail most often not because the individual API calls are wrong, but because the integration assumes both systems are always available and processes messages synchronously. Azure Service Bus decouples that assumption: a message queued for the ERP survives a downstream outage, retries on failure without losing data, and lets producers and consumers scale and deploy independently of each other. For Acumatica and similar ERPs, where a nightly batch job or a third-party system integration can easily overwhelm a synchronous API, this is the difference between a brittle integration and one that survives a bad afternoon.
Queues vs. topics: picking the right entity
A Service Bus queue is a simple point-to-point channel: one producer, one logical consumer (or a competing set of consumers pulling from the same queue), useful for a single well-defined workflow like "sync new sales orders into the ERP." A topic adds publish/subscribe fan-out — one message published to a topic can be delivered to multiple independent subscriptions, each with its own filter, which is the right shape when an ERP event (say, an invoice posted) needs to notify several unrelated downstream systems without the publisher knowing who's listening.
await using var client = new ServiceBusClient(connectionString);
var sender = client.CreateSender("erp-sales-orders");
var message = new ServiceBusMessage(JsonSerializer.SerializeToUtf8Bytes(order))
{
MessageId = order.ExternalOrderId, // enables duplicate detection
ContentType = "application/json",
};
message.ApplicationProperties["source"] = "ecommerce-platform";
await sender.SendMessageAsync(message);
Idempotent consumers and duplicate detection
Service Bus guarantees at-least-once delivery by default, which means a consumer must tolerate receiving the same message more than once — a network blip between message processing and the completion acknowledgment is enough to cause a redelivery. Set MessageId to a stable business key (an order number, not a random GUID) and enable duplicate detection on the queue so Service Bus itself discards exact re-sends within the detection window; on the consumer side, check whether the referenced ERP record already exists before creating it, since duplicate detection alone doesn't cover every retry scenario.
Dead-letter queues for poison messages
Every queue and subscription in Service Bus has a built-in dead-letter sub-queue that messages land in automatically after exceeding MaxDeliveryCount retries or when a consumer explicitly dead-letters a message it can't process. A malformed order payload or an ERP validation failure shouldn't retry forever and block the queue behind it — dead-letter it with a reason code, alert on dead-letter queue depth, and give someone a runbook for inspecting and replaying those messages once the root cause is fixed.
The default MaxDeliveryCount (10) is often too high for ERP integrations where a validation failure is a data problem, not a transient one — retrying a message that will never succeed 10 times just delays detection. For known-permanent failures (bad account codes, missing required fields), fail fast and dead-letter after 2-3 attempts; reserve higher retry counts for genuinely transient issues like a downstream API timeout.
Sessions for ordered processing
Standard Service Bus queues don't guarantee message order once you have multiple concurrent consumers. If the ERP integration needs strict ordering — line items for the same order must be processed in sequence, for example — enable sessions and set SessionId to a value shared by all messages that must stay in order (the order ID). Service Bus then guarantees a single consumer processes all messages for a given session in the order they were sent, while still allowing different sessions to process in parallel.
A session-enabled receiver locks an entire session, not just one message, so an unusually slow message in a session blocks the rest of that session's queue. Only enable sessions where ordering genuinely matters — most ERP sync scenarios (independent order creation, independent inventory updates) don't need it and are faster without it.
Scaling and cost considerations
The Standard tier bills per operation and is the right starting point for most ERP integration volumes; the Premium tier moves to dedicated, predictable-latency capacity billed per messaging unit, worth it once throughput or latency-sensitivity justifies the fixed cost. Auto-forwarding lets a queue automatically push processed messages to another queue or topic, which is a useful pattern for chaining an ERP sync pipeline (validate → transform → post) without custom orchestration code for the handoff between stages.
| Feature | Use for |
|---|---|
| Queue | Single consumer/consumer group, point-to-point |
| Topic + subscriptions | Fan-out to multiple independent consumers |
| Duplicate detection | At-least-once delivery safety net |
| Dead-letter queue | Isolating permanently failing messages |
| Sessions | Strict per-entity ordering when required |
Wrapping up
Azure Service Bus earns its place in an ERP integration once synchronous, direct API calls between systems start failing under load or during the ERP's own downtime — queuing decouples the two systems' availability from each other. Design consumers to be idempotent from day one, set dead-letter thresholds based on whether a failure is transient or permanent, and reach for sessions only when ordering is a genuine business requirement rather than a default choice.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.