Acumatica · Acumatica

Acumatica Business Events Retry Strategies

Acumatica Business Events Retry Strategies is the work that turns a collection of business systems into a coherent operation.

John Kihiu12 min read

Business Events don't retry. If a custom subscriber's external call fails, that's it — the event fired once, the subscriber ran once, and unless the failure was caught and queued for another attempt, it's gone. Retry has to be built, and it has to live outside the subscriber itself.

Why retry can't live in the subscriber

The subscriber runs inline with the record save (covered in the deep dive). Retrying inside it — looping on a failed HTTP call with a sleep between attempts — means the user's save is blocked for the entire retry window. A subscriber that fails should write a row somewhere and return immediately; the retrying happens in a separate process that has all the time in the world, because nobody is sitting at a screen waiting on it.

Exponential backoff, not fixed interval

A downstream outage rarely resolves in exactly the time a fixed retry interval assumes. Backing off exponentially (1 minute, 2, 4, 8, capped at some ceiling like 30 minutes) means a brief blip gets retried quickly while a longer outage doesn't hammer the target with retries that are guaranteed to fail. Cap the total attempt count too — an event retried forever with no ceiling is functionally the same as a memory leak, just in a queue table instead of RAM.

C# · BACKOFF SCHEDULE
public static DateTime NextAttempt(int attemptNumber)
{
    var backoffMinutes = Math.Min(Math.Pow(2, attemptNumber), 30);
    return DateTime.UtcNow.AddMinutes(backoffMinutes);
}

// Processing screen selects only rows due for retry:
// WHERE Status = 'Pending' AND NextAttemptOn <= @now

Idempotency is what makes retry safe

Retry only works if replaying the same event twice is harmless on the receiving end. That means every payload carries a stable idempotency key (covered in the webhooks article), and the receiver de-duplicates on it — creating the same shipment record twice because a webhook was retried after a timeout is a worse outcome than the original failure would have been.

A timeout is not the same as a failure

If the external call times out, you don't actually know whether the receiver processed it before the timeout hit. Retrying is still the right move, but only if the receiver is idempotent — otherwise a slow-but-successful call followed by a retry creates a duplicate, silently, with nothing in Acumatica showing anything went wrong.

Where the retry loop lives

A scheduled processing screen (Acumatica's own processing framework, running on a timer) reading rows due for retry is usually simpler to operate than standing up an external queue purely for retry semantics — it's inside the instance, visible to anyone with access to Acumatica, and doesn't need another system to monitor. Where the target queue already gives you retry for free (SQS with a redrive policy, Service Bus with max delivery count), lean on that instead and keep the Acumatica side simple: enqueue once, let the cloud queue handle the reattempts.

Wrapping up

Retry in a Business Events integration is exponential backoff, a capped attempt count, and idempotent receivers — none of which Acumatica gives you natively, all of which have to be designed into the queue table and processor that sit downstream of the subscriber.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.