An ERP integration Services can appear healthy until the same event arrives twice.
A payment gateway retries a webhook after a timeout. An order service republishes a message after a worker restart. The ERP receives two requests and creates duplicate invoices, stock movements, or customer records.
This is where ERP Integration Services become more than API-to-API connectivity. Production integrations need idempotency, retry handling, observability, and clear ownership of transaction state.
For developers building enterprise ERP integration Services, the important question is not simply, "Can the ERP receive this payload?" It is, "What happens when the same payload arrives again, arrives late, or fails halfway through processing?"
Before implementation, it helps to understand how ERP Integration Services are designed for enterprise systems.
This article demonstrates a practical pattern for processing ERP events safely using Node.js, PostgreSQL, and idempotency keys.
Context and Setup
The architecture below assumes an external application sends order events to an integration API, which then synchronizes the relevant data with an ERP.
A simplified flow looks like this:
External Application → Webhook API → Idempotency Store → ERP Processing → Audit Log
The main risk is duplicate delivery.
Most webhook providers use at-least-once delivery semantics. That means your application should expect the same event more than once. Network failures can also create uncertainty. The sender may not know whether your API processed a request successfully and may retry it.
Gartner predicts that by 2027, more than 70% of recently implemented ERP initiatives will fail to fully meet their original business case goals, with as many as 25% failing catastrophically. A documented ERP strategy and architecture are therefore important before integration complexity expands.
For this example, you need:
- Node.js 20+
- PostgreSQL
- An Express API
- A unique event ID supplied by the source system
- An ERP API endpoint or adapter
Building ERP Integration Services with Idempotent Event Processing
The simplest reliable pattern is to persist the event identifier before performing the ERP operation.
Step 1: Define a Stable Event Identity
An event must have an identifier that remains unchanged when the sender retries delivery.
For example:
{
"eventId": "order_10293_created",
"eventType": "ORDER_CREATED",
"orderId": "10293",
"timestamp": "2026-09-01T10:30:00Z"
}
The eventId should represent the business event rather than the HTTP request itself.
Why?
A request ID generated by your API changes every time the sender retries. An event ID generated by the source system remains stable across retries.
Store this ID in a database with a unique constraint:
CREATE TABLE integration_events (
event_id VARCHAR(255) PRIMARY KEY,
event_type VARCHAR(100) NOT NULL,
status VARCHAR(30) NOT NULL,
processed_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
The database constraint is important because application-level checks alone can fail under concurrent requests.
Step 2: Process the Webhook Atomically
The next step is to reserve the event before calling the ERP.
Here is a simplified Express implementation:
import express from "express";
import pg from "pg";
const app = express();
app.use(express.json());
const db = new pg.Pool({
connectionString: process.env.DATABASE_URL
});
app.post("/webhooks/orders", async (req, res) => {
const { eventId, orderId } = req.body;
try {
// Why: the unique database key prevents duplicate processing
await db.query(
`INSERT INTO integration_events (event_id, event_type, status)
VALUES ($1, $2, $3)`,
[eventId, "ORDER_CREATED", "PROCESSING"]
);
// Why: ERP processing happens only after reserving the event
await sendOrderToERP(orderId);
await db.query(
`UPDATE integration_events
SET status = $1, processed_at = NOW()
WHERE event_id = $2`,
["COMPLETED", eventId]
);
return res.status(200).json({
success: true
});
} catch (error) {
// PostgreSQL unique violation code
if (error.code === "23505") {
// Why: duplicate events should not create duplicate ERP records
return res.status(200).json({
success: true,
duplicate: true
});
}
return res.status(500).json({
success: false
});
}
});
async function sendOrderToERP(orderId) {
// Replace with your ERP API adapter
console.log(`Syncing order ${orderId}`);
}
This pattern prevents two concurrent requests from processing the same event successfully.
However, there is another failure scenario.
What happens if the database stores PROCESSING, but the application crashes before updating the ERP?
That requires a recovery strategy.
Step 3: Add Retry and Recovery Logic
A production integration should treat processing as a state machine.
A useful model is:
RECEIVEDPROCESSINGCOMPLETEDFAILEDRETRYING
Unlike a simple synchronous API call, a state-based workflow allows operators and background workers to understand exactly where processing stopped.
For failed events, use controlled retries:
async function retryFailedEvent(event) {
try {
// Why: retry only events marked as failed
await sendOrderToERP(event.orderId);
await db.query(
`UPDATE integration_events
SET status = $1, processed_at = NOW()
WHERE event_id = $2`,
["COMPLETED", event.eventId]
);
} catch (error) {
// Why: preserve failure state for monitoring and later retry
await db.query(
`UPDATE integration_events
SET status = $1
WHERE event_id = $2`,
["FAILED", event.eventId]
);
}
}
For high-volume workloads, move retries into a queue such as RabbitMQ, Kafka, AWS SQS, or another message-processing platform.
The trade-off is operational complexity.
A direct API integration is easier to deploy but can become difficult to recover when transaction volume grows. A message-driven architecture adds infrastructure overhead but provides better isolation between the source application and ERP processing.
Gartner's 2025 research on ERP event-driven integration specifically addresses challenges including event loss, duplicate processing, and database-event inconsistency.
Real-World Application
In one of our ERP integration-related projects at Oodles, Fulfillment Hub USA needed to connect Odoo ERP with ShipHero so orders could synchronize and delivery and pickup costs could be automatically recorded.
The implementation used custom APIs to synchronize order data between ShipHero and Odoo while supporting inventory, order processing, and tracking workflows. The measurable operational result documented in the project was real-time order synchronization and automated cost addition for each order, replacing manual handoffs in the fulfillment workflow.
The important engineering lesson was that the integration layer needed to represent business events clearly rather than simply forwarding raw API requests.
For enterprise projects, Oodles approaches integration architecture by evaluating system ownership, APIs, workflow dependencies, and the operational failure paths that appear after deployment.
Conclusion: Key Takeaways
- Treat every ERP integration event as potentially duplicated.
- Use a database-level unique constraint instead of relying only on in-memory duplicate checks.
- Persist processing states so failed transactions can be investigated and replayed.
- Separate synchronous API acknowledgement from long-running ERP processing when workload volume increases.
- Choose queues and event-driven processing when reliability requirements justify the additional infrastructure.
Building reliable integrations requires more than connecting two APIs. If you are working through duplicate events, synchronization failures, middleware design, or ERP API architecture, share your technical questions in the comments or explore our ERP Integration Services.
Q: What are ERP Integration Services?
A: ERP Integration Services connect ERP platforms with applications such as CRM, e-commerce, warehouse systems, payment platforms, and external APIs. A production implementation should handle authentication, data mapping, retries, duplicate events, monitoring, and transaction failures.
Q: How do you prevent duplicate records in an ERP integration?
A: Prevent duplicate records by assigning a stable event identifier and enforcing uniqueness at the database level. The integration service should reject or safely acknowledge repeated events before sending the same business transaction to the ERP again.
Q: Should ERP integrations use synchronous APIs or message queues?
A: Synchronous APIs work well for immediate request-response workflows. Message queues are better for asynchronous processing, retries, workload spikes, and failure isolation. The correct choice depends on latency requirements, transaction volume, and operational recovery needs.
Q: What is idempotency in an ERP Integration Services?
A: Idempotency means processing the same business request multiple times produces the same final result as processing it once. It is essential when webhook providers, APIs, or message brokers can retry requests after network or application failures.
Q: How should failed ERP Integration Services events be handled?
A: Failed events should be stored with a processing status, error details, retry count, and timestamp. Automated retries should handle temporary failures, while persistent errors should enter a monitored exception workflow for investigation and replay.
Top comments (0)