Resources / Integration
Why Dynamics 365 F&O integrations double-post, and how to make them exactly-once
Every integration retries sooner or later. When the call it retries posts a journal or invoices an order, the retry is how duplicates reach the ledger. Here's how to make retries harmless.
Retries are not an edge case
Networks drop, gateways time out, and message brokers redeliver by design. A caller that does not hear back cannot know whether its request failed or succeeded silently, so it sends it again. For a read, that costs nothing. For a call that posts a journal, invoices a sales order or settles a payment, it is how the same document reaches the ledger twice, and the duplicate is usually found at month end by someone reconciling a balance that will not tie.
Why duplicate checks in the caller fall short
The usual fix is to make the calling system remember what it sent and check before sending again. That moves the problem rather than solving it: the caller still cannot see whether a call that timed out actually posted, every integration has to rebuild the same logic, and each one gets it slightly differently wrong. The only place that knows for certain whether a document was created is the ERP, inside the transaction that created it.
Make the ERP answer the retry
An exactly-once service takes a request id from the caller and records a claim on it in the same transaction as the document it creates. A repeat of that id returns the original result instead of posting again, whether the first call finished a second ago or crashed halfway. A scheduled sweep finds any claim a crash left in progress and resolves it: finalized if the document posted, released if it never existed, or parked for a person to look at. Add a dry-run preview that validates a request without posting or consuming a number, and integrators can test against production rules without touching the books.
Recovery has to be exactly-once too
Failures still happen, and the fix matters as much as the guarantee. When a failed message is retried from a dead-letter queue, it should reuse its original request id, so a message that half-succeeded the first time cannot post again on the second. That combination (idempotent services, a reconciliation sweep, previews and replayable recovery) is how Lattice's integration operations work across every one of its posting services, so your integrators get the guarantee without building it.
Related product