Introduction
Integration failures rarely happen at the most convenient moment.
An API request may reach Odoo but fail to return a response. A payment provider may process a transaction while the network connection times out. A webhook may be delivered more than once. An external system may retry the same request because it cannot determine whether the first request succeeded.
This creates one of the most dangerous problems in ERP integration:
The system cannot tell whether a request is new or a retry.
Without proper controls, the same customer order can be created twice, a payment can be recorded multiple times, inventory can be updated repeatedly, or an external transaction can be processed more than once.
For businesses integrating Odoo with payment gateways, eCommerce platforms, marketplaces, logistics systems, banking platforms, or custom applications, retries and idempotency should be treated as core integration architecture not as error-handling details.
What Are Retries in an Odoo Integration?
A retry occurs when an integration attempts the same operation again after the first attempt appears to have failed.
For example:
External System → Odoo
The external application sends a sales order.
Odoo processes the request successfully.
Before the external system receives the response, the network connection fails.
The external system assumes the request failed and sends it again.
If Odoo treats the second request as a new transaction, two sales orders may be created.
The original request was successful.
The response was what failed.
This distinction is critical.
What Is Idempotency?
Idempotency means that processing the same request multiple times produces the same business result as processing it once.
For example:
Request ID: ORD-84721
The integration sends the request three times because of network failures.
With idempotent processing:
First request → Order created
Second request → Existing order identified
Third request → Existing order returned
The result remains:
One business transaction.
Idempotency therefore provides protection when retries are necessary.
Retries improve reliability.
Idempotency makes retries safe.
Why Odoo Integrations Need Idempotency
Odoo commonly participates in integrations involving:
- eCommerce orders
- Payment gateways
- Marketplaces
- Shipping platforms
- Banking systems
- CRM systems
- Manufacturing systems
- Subscription platforms
- Customer portals
- External applications
- Webhooks
- Custom APIs
Any integration that creates or updates business records can potentially encounter duplicate requests.
The risk becomes particularly serious for operations such as:
Create Sale Order → Create Invoice → Register Payment
or:
Payment Request → Payment Confirmation → Accounting Entry
or:
Order → Delivery → Stock Movement
A duplicate read operation may be inconvenient.
A duplicate financial or inventory transaction can become a business problem.
1. Identify the Business Transaction Before Designing the API
The first architectural question should not be:
“Which endpoint should we call?”
Instead ask:
“What business transaction are we protecting?”
For example:
| Integration | Business Transaction | Duplicate Risk |
|---|---|---|
| eCommerce → Odoo | Sales Order | High |
| Payment Gateway → Odoo | Payment Confirmation | Very High |
| Marketplace → Odoo | Customer Order | High |
| Shipping → Odoo | Delivery Status | Medium |
| Bank → Odoo | Transaction Import | Very High |
| CRM → Odoo | Lead Creation | Medium |
| Product System → Odoo | Product Update | Medium |
The integration design should then identify the unique business reference that represents each transaction.
2. Use a Stable Idempotency Key
| Integration Component | Recommended Approach | Purpose |
|---|---|---|
| Transaction ID | Stable external reference | Identify business transaction |
| Idempotency Key | Unique per transaction | Detect repeated requests |
| Odoo Record | Store external reference | Link systems |
| Duplicate Check | Search before creation | Prevent duplicate records |
| Processing Status | Pending/Success/Failed | Track transaction state |
| Response Storage | Store successful result | Reuse result on retry |
| Error Log | Store failure details | Support troubleshooting |
| Retry Counter | Track attempts | Control repeated failures |
An idempotency key is a unique identifier representing one business operation.
For example:
payment_20260915_84721
or:
shopify_order_100928
or:
warehouse_transfer_EXT-47281
The key should come from the business transaction—not from the individual HTTP attempt.
That distinction is important.
If a request fails and is retried, the retry should use:
The same idempotency key.
It should not generate a new key for every attempt.
The basic flow
Generate Business ID
↓
Send Request
↓
Process Transaction
↓
Store Idempotency Key
↓
Retry if Necessary
↓
Detect Existing Key
↓
Return Existing Result
This allows the receiving system to distinguish a new operation from a retry.
3. Store the External Reference in Odoo
For integrations that create important Odoo records, store the external transaction identifier.
For example:
External Order ID: SHOP-100928
External Payment ID: PAY-78341
External Shipment ID: SHIP-55421
This creates a relationship between the external transaction and the Odoo record.
Before creating a new record, the integration can check whether the external reference already exists.
Conceptually:
External ID exists?
→ Yes: Return or update the existing record.
→ No: Create the new record.
This simple control can prevent many duplicate-record scenarios.
4. Do Not Generate Idempotency Keys From Request Time
A common design mistake is generating a new identifier every time the integration sends a request.
For example:
Attempt 1 → ID 1001
Attempt 2 → ID 1002
The system now believes these are two different transactions.
Instead:
Business Transaction → ID 1001
Attempt 1 → ID 1001
Attempt 2 → ID 1001
Attempt 3 → ID 1001
The identifier belongs to the transaction, not the network request.
5. Design Retries Around Failure Types
| Failure Type | Example | Retry? | Recommended Action |
|---|---|---|---|
| Network Timeout | External API does not respond | Yes | Retry with controlled backoff |
| Temporary Server Error | HTTP 500 | Yes | Retry after a delay |
| Rate Limit | HTTP 429 | Yes | Respect retry-after or backoff |
| Authentication Failure | Invalid API key | No | Fix credentials first |
| Validation Error | Missing required field | No | Correct the request |
| Duplicate Request | Same transaction sent again | No | Return or reuse existing result |
| Business Rule Error | Invalid order state | Usually No | Resolve business condition |
| Service Unavailable | External system temporarily offline | Yes | Retry within defined limits |
Not every error should trigger a retry.
A useful classification is:
Temporary Failure
Examples:
- Network timeout
- Temporary service unavailable
- Connection reset
- HTTP 5xx response
These may be retryable.
Permanent Failure
Examples:
- Invalid customer
- Invalid product
- Missing required field
- Authentication failure
- Business-rule validation error
These normally require correction rather than repeated retries.
Unknown Outcome
This is the most dangerous category.
The external system does not know whether Odoo completed the transaction.
For example:
Request sent → Odoo processes → Network timeout → No response
The correct response is not necessarily to create a new transaction.
The system should first use the idempotency key or external reference to determine whether the original operation already succeeded.
6. Use Controlled Retry Strategies
Retrying immediately and continuously can make an outage worse.
A better strategy uses:
Limited Attempts + Increasing Delay + Clear Failure State
For example:
Attempt 1 → Immediate
Attempt 2 → 30 seconds
Attempt 3 → 2 minutes
Attempt 4 → 10 minutes
The exact timing depends on the integration.
The important principle is to avoid uncontrolled retry loops.
Retries should also have:
- Maximum attempts
- Maximum execution time
- Error classification
- Retry status
- Final failure state
- Monitoring
7. Make Webhook Processing Idempotent
Webhooks are particularly sensitive to duplicate delivery.
Odoo 19 supports webhook-based automation where an external system sends a payload to an Odoo webhook URL and a predefined action is performed in Odoo. Odoo recommends testing webhooks carefully before using them in a live database.
Consider:
Payment Provider → Webhook → Odoo
The payment provider sends:
PAY-88391 = Paid
If the webhook is delivered twice, Odoo should not create two payment records.
Instead:
Receive PAY-88391
↓
Check Existing Payment Reference
↓
Already Processed?
→ Yes: Return success.
→ No: Process payment.
This makes webhook handling safe against repeated delivery.
8. Protect Payment Integrations With Stronger Controls
Payment integrations require additional care because duplicates can have direct financial consequences.
A payment transaction should normally have a provider-side reference that can be associated with the Odoo payment transaction.
Odoo's payment transaction framework itself includes mechanisms for computing and searching transactions by provider references, which illustrates the importance of stable transaction identifiers in payment processing.
A payment workflow should therefore distinguish:
Payment Requested
from:
Payment Successfully Confirmed
and:
Payment Already Processed
The integration should never assume that a timeout means the payment failed.
The provider's transaction status should be checked when the outcome is uncertain.
9. Understand Odoo Transaction Boundaries
Transaction boundaries are another important part of integration design.
Odoo's JSON-2 API runs each call in its own SQL transaction. A successful call is committed, while an error causes that transaction to be discarded. Odoo's documentation also warns that multiple separate calls cannot be chained into one transaction through JSON-2.
This matters when an integration performs multiple related operations.
For example:
Create Order
then:
Confirm Order
then:
Create Delivery
then:
Create Invoice
If these are implemented as separate API calls, another transaction or concurrent process can change the state between calls.
Where multiple related operations need to remain atomic, Odoo recommends using a single method that performs the related operations in one transaction.
This can be particularly important for reservations, payments and other sensitive workflows.
10. Avoid Partial Integration Transactions
Consider this process:
External Order
↓
Create Odoo Order - Success
↓
Create Odoo Invoice - Failure
↓
Retry Entire Request
If the retry blindly creates everything again, the integration may create a duplicate order.
Instead, each business transaction should have a clearly defined state.
For example:
| Integration State | Meaning |
|---|---|
| Received | Request arrived |
| Processing | Transaction is being handled |
| Completed | Business operation succeeded |
| Retry Pending | Temporary failure occurred |
| Failed | Manual intervention required |
| Duplicate | Existing transaction detected |
This makes failures visible and recoverable.
11. Keep an Integration Log
A reliable integration should provide enough information to answer:
What happened to this transaction?
For every important request, consider recording:
- External transaction ID
- Idempotency key
- Odoo record ID
- Request timestamp
- Response timestamp
- Attempt number
- Request status
- Error message
- Last retry time
- Final result
This makes troubleshooting significantly easier.
Instead of asking:
“Why did this order duplicate?”
the team can investigate:
“Order EXT-84721 was received three times. The first request created sale.order 12541, while attempts two and three matched the existing external reference.”
That is actionable information.
12. Make Duplicate Detection Explicit
Duplicate prevention should not depend on developers remembering to check for duplicates.
The business rule should be explicit.
For example:
External Payment ID must be unique.
External Order ID must be unique.
External Shipment ID must be unique.
Where appropriate, enforce uniqueness at the database/model level rather than relying only on application logic.
This provides stronger protection against concurrent requests.
13. Design for Concurrent Requests
Two requests can arrive at almost the same time.
For example:
Request A → Check external ID → Not Found
Request B → Check external ID → Not Found
Both requests may then attempt to create the same Odoo record.
This is why:
Check → Then Create
alone may not always be sufficient.
For high-value transactions, the design should also consider database-level uniqueness or another concurrency-safe mechanism.
The goal is to ensure that simultaneous requests cannot create multiple records for the same business transaction.
14. Make Updates Idempotent Too
Idempotency is not only for record creation.
Updates can also be repeated.
For example:
Shipment Status = Delivered
If the same webhook arrives five times, the result should remain:
Delivered
rather than triggering five downstream actions.
For updates, consider:
- External reference
- Event ID
- Event timestamp
- Current state
- Previous state
- Version number
The integration should understand whether the incoming event is new, repeated, or outdated.
15. Test Failure Scenarios Before Go-Live
Normal success testing is not enough.
Integration testing should deliberately simulate failures.
Test scenarios such as:
- Request timeout
- Network failure
- Duplicate webhook
- Duplicate API request
- Odoo validation error
- External API failure
- Payment confirmation received twice
- Two requests arriving simultaneously
- Partial transaction failure
- Retry after successful processing
- Delayed webhook
- Out-of-order events
For each scenario, ask:
What business records exist after the failure?
The expected answer should be clearly defined before production deployment.
16. Build an Odoo Integration Retry and Idempotency Framework
A practical architecture can follow:
Business Transaction ID
↓
Idempotency Check
↓
Validate Request
↓
Process Transaction
↓
Commit Successful Result
↓
Store Result
↓
Return Response
If the same request arrives again:
Receive Request
↓
Find Existing Idempotency Key
↓
Existing Result Found
↓
Return Existing Result
No duplicate transaction is created.
Common Integration Mistakes
Retrying Every Error
Permanent business errors should not be endlessly retried.
Creating a New ID on Every Retry
The same business transaction needs the same idempotency identity.
Assuming Timeout Means Failure
A timeout only means the caller did not receive the response.
Relying Only on Application-Level Duplicate Checks
Concurrent requests can bypass a simple check-before-create pattern.
Ignoring Webhook Duplicates
Event-driven integrations must expect repeated delivery.
Not Logging Attempts
Without integration logs, duplicate transactions become difficult to investigate.
Splitting Atomic Operations Into Multiple Calls
Related operations may require a single transactional method in Odoo.
Testing Only Successful Scenarios
Production failures rarely behave like the happy-path test case.
Odoo Integration Retry & Idempotency Checklist
Before launching an integration, confirm that you have:
Defined the business transaction
Created stable external references
Defined idempotency keys
Stored external transaction IDs in Odoo
Classified retryable and non-retryable errors
Configured limited retry attempts
Added appropriate retry delays
Protected webhook processing
Protected payment transactions
Defined transaction boundaries
Added duplicate detection
Considered concurrent requests
Added integration logs
Defined failure states
Tested duplicate requests
Tested timeout scenarios
Tested partial failures
Tested webhook re-delivery
Defined monitoring and alerting
Frequently Asked Questions
1. What is idempotency in Odoo integrations?
Idempotency means processing the same integration request multiple times produces the same business result instead of creating duplicates.
It is especially important for orders, payments, invoices, inventory transactions and webhook-driven workflows.
2. Why are retries important in Odoo integrations?
Retries help recover from temporary failures such as network timeouts, unavailable services, or temporary API errors.
However, retries should be combined with duplicate detection so the same transaction is not processed twice.
3. How can Odoo integrations prevent duplicate transactions?
Use stable business references or idempotency keys and check whether the transaction has already been processed before creating a new record.
The integration should store the external reference and return the existing result when the same request is received again.
4. What is an idempotency key?
An idempotency key is a stable identifier associated with a specific business transaction or request.
The receiving system uses it to recognize repeated requests and prevent duplicate processing.
5. Can webhooks create duplicate records in Odoo?
Yes, duplicate webhook deliveries can create duplicate records if the receiving workflow does not identify previously processed events.
Webhook handlers should validate the event reference and make processing safe for repeated delivery. Odoo recommends testing webhooks before implementation.
6. Why should integrations store external transaction references?
External references create a reliable link between the transaction in Odoo and the source system.
They make duplicate detection, reconciliation, troubleshooting and support much easier.
7. How does Odoo 19 JSON-2 affect integration design?
Odoo 19's JSON-2 API executes each API call within its own SQL transaction, committing successful calls and rolling back failed ones.
Related operations that must succeed or fail together should therefore be handled within a single Odoo method where appropriate.
8. Should every integration error trigger a retry?
No. Temporary failures may be retried, but validation errors, authentication failures and permanent business-rule errors usually require correction instead.
A retry strategy should classify errors before deciding whether another attempt is appropriate.
Conclusion
Odoo integrations should be designed with the assumption that requests can fail, time out, or be delivered more than once. Retries are important for reliability, but without idempotency and stable transaction references, they can create duplicate orders, payments, stock movements, or other business transactions.
A reliable integration therefore needs more than API connectivity. It needs stable identifiers, duplicate detection, controlled retries, clear transaction boundaries, proper logging and testing for failure and concurrency scenarios. Odoo 19's JSON-2 API also treats each API call as its own database transaction, making transaction design particularly important when related operations need to remain consistent.
The goal is to build integrations that remain predictable even when something goes wrong. BrowseInfo can help businesses design and implement scalable Odoo integrations with stronger retry handling, idempotency controls, integration architecture, monitoring and long-term maintainability.