APIs & integrations

Prevent duplicate orders and invoices when API messages repeat

Design processing that recognises repeated operations, investigates uncertain outcomes and preserves source-to-destination identity.

On this page

An API message can arrive again without a customer placing another order. A sender may retry, an employee may recover failed work or the connection may disappear after the destination has saved a record. Treating every attempt as a new instruction can create duplicate customers, drafts or notifications. This is a design question, rather than simply a matter of asking people to click less often.

This guide explains how to specify and test safe repetition. Its examples use fictional orders and draft documents. They do not establish that any particular product provides all the described features. The appropriate approach depends on the identity of the operation, the destination API and the evidence available after a response becomes uncertain.

Separate delivery from the business operation

One order can generate several events: created, approved and changed. A single event may also be delivered repeatedly. Define exactly what should happen once. For example, create one draft for version three of approved order O-184. This is more precise than processing a message containing an order number, because a later approved revision may legitimately need different treatment.

Give each execution a technical tracing reference while retaining a stable business key. A new execution identifier on every restart helps investigation but is unsuitable as the only duplicate protection. The integration needs to establish that separate attempts concern the same intended action. Also define when a change represents a new instruction or correction, rather than assuming that every event for an existing order should be ignored forever.

Check what the destination actually supports

Some APIs support an idempotency key that allows a repeated request to be recognised as the same operation. Stripe documents such a mechanism with its own conditions and differences between API versions. Treat this as a specific vendor example, not evidence that every accounting or CRM endpoint provides equivalent behaviour. Read the current requirements for the precise operation your project intends to call.

Investigate how long recognition lasts, what happens when parameters change and whether a destination record can be retrieved by external reference. If no suitable feature exists, the integration requires more of its own bookkeeping and verification. Record remaining uncertainty. A local processed table is not automatically sufficient when the integration can stop after the remote action but before updating that local table with the confirmed result.

Stripe: Idempotent requests ↗

Represent processing as a state

A single done flag cannot distinguish not started, in progress, confirmed and uncertain. Use a small, understandable state model for consequential operations. A received instruction might first be durably recorded, then prepared, submitted and finally confirmed with its destination reference. A failure can leave it in an explicitly uncertain state requiring investigation instead of an immediate new creation attempt.

That distinction helps employees choose the next action. Confirmed processing should not start another creation; missing data requires source correction; uncertainty requires checking the destination. Keep the model understandable and explain its meaning. An extensive list of technical intermediate stages without business context can make recovery harder and encourage people to use an unrestricted restart button simply to remove an error indicator from the dashboard.

StateWhat is known?Next action
ReceivedInstruction is retainedValidate and prepare
In progressAn attempt is activeAvoid an independent second creation
ConfirmedDestination reference is verifiedReturn the outcome without repetition
UncertainEffect is not reliably establishedInvestigate destination and record a decision

When an outcome is uncertain, first establish what has already completed. The recovery guide explains that sequence: An API integration has stopped: investigate and recover without duplicate work.

Handle concurrent attempts

Two equivalent messages can be processed almost simultaneously. Merely checking whether a record exists beforehand is insufficient: both executions may find nothing and then both create a destination record. Claiming a business operation therefore needs protection against concurrency, such as an appropriate unique key with consistent storage rules. The exact mechanism depends on the architecture and must be demonstrated rather than assumed.

Ask what the second execution does while the first is still running. Does it wait, retrieve the same outcome or finish as an identified repetition? Also test a restart after interruption. Protection should cover the intended operation without blocking every future legitimate change to that order. Include one case where an unchanged repetition is suppressed and another where a genuinely new revision follows the agreed processing route.

Investigate a timeout carefully

In a fictional trial, the destination creates a draft invoice but its response is lost. The source observes a timeout. Creating again may produce a second draft. The first question is therefore not how quickly to retry, but what evidence can establish whether the original instruction already had its intended effect. This is different from an error rejected before processing began.

Search using the agreed external reference or query a known operation where the product provides that facility. If the integration cannot reach a reliable conclusion, route the item to controlled review. Show which systems were checked and what remains undecided. An honest uncertain state is more useful than a misleading failure label that encourages an employee to start again without examining an existing document or action.

Distinguish repetition from correction

An order can change after its first processing. The business relationship remains the same, but a changed amount, address or line item needs a separate decision. Differentiate resubmitting the same contents from processing a new revision. A rule that silently ignores both may prevent duplicates while losing legitimate changes, creating a different operational problem that a duplicate-count check will not reveal.

Define which changes are permitted. A draft awaiting review might be updateable, whereas a document already processed further might need human assessment. The responsible owner decides appropriate financial treatment. Retain the reference to previous versions so the correction can be explained. Do not generate a new random identifier merely to bypass protection when somebody is actually trying to repeat an existing operation with an unresolved outcome.

Provide a safe employee recovery route

A recovery view should distinguish resubmission, source correction, destination investigation and closing without a new action. Show the source reference, last attempt, destination reference where known and the reason for the proposed next step. A consequential action needs enough context for someone to make a decision, instead of merely trying to clear a red item from an operational list.

Set permission boundaries as well. Investigating a failed item should not automatically grant the ability to delete records or modify final documents. Record the decision and the confirmed outcome. Check that recovery reuses the intended business key when appropriate. A special recovery button bypassing every safeguard can undermine an otherwise careful automated path, especially when it is used during pressure to clear a backlog quickly.

Test the situations that produce duplicates

Submit the same event twice sequentially and then concurrently. Interrupt processing between remote storage and local confirmation. Test a lost response, an unavailable destination and a genuine new order revision. Expected outcomes should cover quantity and identity: the intended single destination record, demonstrably connected to the correct source operation, or a clearly explained unresolved case requiring review.

Check notifications too. Repeated delivery should not create another support ticket or customer message when it represents the same intended business action. Keep the trial as regression evidence for later changes. One ordinary order producing one draft does not establish safe repetition. Unfavourable timing, ambiguous responses and controlled recovery provide the evidence needed to evaluate whether the selected protection actually serves its purpose.

Stripe: Receive webhook events ↗

Turn the explanation into a project decision

Record your assumptions, unknowns and acceptance checks in the matching tool. Use the service guides to discuss delivery and scope.

Open the preparation tool ↗

Questions about this topic

Is a webhook identifier enough to prevent duplicate invoices?

It helps recognise the same event, but one business instruction may arise through several events or routes. Protect the intended business operation as well and retain its destination reference. Investigate identity at both levels.

Can every timeout be retried immediately?

Not without considering whether the first attempt already had an effect. Use destination capabilities to investigate uncertain outcomes. Where reliable evidence is unavailable, a controlled human decision may be necessary before proceeding.

Does one unique database column prevent every duplicate?

It can protect local concurrent claims. It does not automatically make an external action and local confirmation one indivisible operation. The design must cover interruptions between systems and the subsequent recovery procedure.

Should every changed order receive a new key?

Only when it represents a new intended operation. Randomly changing a key to bypass an earlier failure can disable protection. Specify the meaning of revisions and corrections before implementing recovery.

Continue with a related question

Sources and technical background

Discuss your project with Ricardo ↗