Integrating external applications with Odoo via API allows sharing data and transferring information; however, this process can also lead to problems when the same request is sent several times. For instance, a user may click the button two times; the automated action may attempt to perform the request again after an earlier request had already been processed, or there could be a network delay. As a result of these complications, users may end up having duplicate records of orders, payments, and customers.
To prevent recurring requests, presence verification is required. Odoo developers need to examine how the requests will be identified, how the status of requests will be tracked, and how the logical errors will be handled in case the integration breaks or restarts.
Why Do Duplicate API Requests Happen?
Duplicate API calls typically happen because the integration is not able to determine whether the request was successfully processed. The issue may arise not always due to code errors. Sometimes it stems from common situations, including network delays, user actions, scheduled tasks, or retries.
For instance, an Odoo sales order could be sent to an external system. Odoo sends out the request, but the reply is delayed for too long. Therefore, Odoo thinks that the request is not successful and makes another call. If the external system receives the first request before the timeout, the repeat request will lead to duplicates.
Some cases that can be the reason for duplicate requests include:
- A user pushes the same synchronization button several times.
- One scheduled action deals with the same record on several occasions.
- Automatic retrying after timeout.
- Temporary failure in the network after the external server processed the request.
- Several Odoo workers making the same operation.
- Integration being triggered manually while the automated process is running.
Therefore, simply making sure that the API call received an error is not enough.
Use a Unique Reference for Every Request
One of the simple methods to solve the problem regarding duplication of records is by providing each record to be synchronized with a unique reference. For example, Odoo does not just send the name and number of a customer but also a unique identifier for the record that has to be synchronized.
For example:
payload = {
"external_reference": sale_order.name,
"customer": sale_order.partner_id.name,
"amount": sale_order.amount_total,
} This allows an external system to identify whether the record is already created or not using external_reference. In case the same request comes in again, the external system can either update the address in the record or return the same result as before.
The procedure works ideally when both organizations accept the means for the identification of records.
Idempotency Keys for Safer API Requests
In the case of integrations involving duplication that could have a disastrous impact, an idempotency key is an even better solution.
An idempotency key is a unique number that is associated with a specific process. Odoo sends this number with the request, which is stored by the recipient. If the same request comes in again with the same number, the outside system recognizes that it has already completed the process.
A request might look like this:
import uuid
import requests
idempotency_key = str(uuid.uuid4())
headers = {
"Authorization": f"Bearer {access_token}",
"Idempotency-Key": idempotency_key,
}
response = requests.post(
api_url,
json=payload,
headers=headers,
)
Implementation details vary from API to API, as some APIs have idempotency key support built right in, while others require the developers to come up with their own method to prevent duplication.
A key point here is that the key should be tied to the business process being executed, rather than being regenerated for each retry. The generation of a new key for every retry will lead the external system to treat each retry attempt as a new event.
Track the Synchronization Status in Odoo
Odoo is capable of keeping track of whether or not the record has been dispatched. The typical features that the integration may implement include:
sync_state = fields.Selection([
("pending", "Pending"),
("processing", "Processing"),
("done", "Done"),
("failed", "Failed"),
])
external_id = fields.Char()
sync_attempts = fields.Integer(default=0)
Before the API call has been made, the current status of the record can be verified.
if record.sync_state == "done":
return
record.sync_state = "processing"
Once the process is successful, the status of the record can be changed to indicate that the process has been completed.
record.write({
"sync_state": "done",
"external_id": response_data.get("id"),
})Using this feature, Odoo is now able to receive the complete history of the synchronization process, which ensures that the record will not have to be sent again.
Nonetheless, having only the status field is insufficient to achieve duplicate prevention. It is possible for two processes to read the pending status at almost the same moment in time and process the record. This is where the usage of database constraints and proper concurrency management becomes critical.
Use Database Constraints When Necessary
An SQL constraint can add an extra level of security when it comes to preventing duplication of data.
For instance, if there is an external reference that should never repeat:
_sql_constraints = [
(
"unique_external_reference",
"unique(external_id)",
"External ID must be unique."
),
]
Database constraints are handy because they implement the restriction at the database level and do not fully rely on code from the application. Even if the two transactions make it to the database almost simultaneously, the uniqueness of the record would still work.
Be Cautious with Automatic Re-attempts
Although automatic attempts are helpful in case of API failures, if a system blindly retries every failure, it can lead to duplicates. The integration must be capable of telling secure failures from failures that need to be checked again.
As an example, retrying a timeout is just fine, while retrying a request or an incorrect authentication might need different handling.
A basic re-attempting system might look as follows:
for attempt in range(3):
response = requests.post(api_url, json=payload)
if response.ok:
break
if response.status_code in (500, 502, 503):
continue
response.raise_for_status()
The system should also take into account whether the external service could process the request before sending back an error code. Using a combination of re-attempts and an idempotency key or unique business reference makes the approach much more secure.
Do Not Depend Solely on Button Protection
Disabling an icon following its first click enhances usability, but it must not be viewed as a total duplicate prevention measure.
Buttons can be activated by means of several other methods, for example:
- Scheduled procedures.
- Code on the server side.
- Automation processes.
- Integration procedures.
- Multiple browser sessions.
Consequently, duplicate prevention should take place on the server side rather than be solely dependent on the user interface methods.
An Effective Technique for Integrating with Odoo 19
There is no single method that can be applied to every integration. The best solution is the application of several different levels.
Below are the typical steps of the process:
Create a unique reference > Check synchronization status > Mark the request as being processed > Make an API call > Store the external id > Mark the process as complete.
When the request fails, Odoo is able to recognize this failure and make a decision about the possibility of a retry. Should a retry be required, the same business reference/idempotency key must be used wherever it is supported by the external API.
With this approach, integration becomes easier to manage, and it becomes simpler to troubleshoot, as it is known what happened at each stage of the synchronization process.
In the end, the essence of duplicate prevention is the ability to turn API calls into idempotent actions whenever possible. When the same request yields the same effect and does not create duplicate entries, it becomes easier to deal with temporary failures of requests and retrying.
Duplicate API requests may seem like a small integration issue, but they can quickly lead to incorrect data and difficult reconciliation problems. In Odoo 19, developers can reduce this risk by combining unique references, idempotency keys, synchronization status fields, database constraints, and carefully designed retry logic. The key is not to assume that a failed response means the external system did nothing. A request may have been processed even when Odoo did not receive the expected response. By designing integrations with duplicate handling and safe retries in mind, Odoo applications can communicate with external systems more reliably and keep data consistent.
To read more about Overview of API Integration in Odoo 19, refer to our blog Overview of API Integration in Odoo 19.