Two-Way Product Catalog Synchronization with ERP
Imagine: the website price differs from the ERP price. The customer orders at an outdated price, you lose margin or cancel the order. Financial losses from such desynchronizations can be significant — for example, for a catalog of 10,000 SKUs, the annual loss can be up to $50,000. This is especially critical for large catalogs where manual reconciliation is impossible. Such discrepancies arise when the product catalog lives separately from the ERP (SAP, Oracle NetSuite, Microsoft Dynamics, Odoo). We solve this problem with two-way synchronization: any change in ERP is instantly reflected on the website and vice versa. This ensures seamless product catalog integration with your ERP system. According to Odoo documentation, properly configured synchronization reduces errors to 0.1%.
How Catalog Synchronization with ERP Solves the Discrepancy Problem
ERP systems have mature REST APIs or SOAP/OData interfaces, but each has its own data model, versioning logic, and limitations. Without proper integration, data quickly diverges. Below are protocol options for popular ERPs. SAP OData, Oracle NetSuite API, Odoo XML-RPC, and CommerceML are commonly used.
| ERP |
Protocol |
Format |
| SAP S/4HANA |
OData v4, REST |
JSON/XML |
| Oracle NetSuite |
REST (SuiteQL) |
JSON |
| Microsoft Dynamics 365 |
OData v4 |
JSON |
| Odoo |
JSON-RPC / REST |
JSON |
| 1С:ERP |
CommerceML + REST |
XML/JSON |
What to Choose: Event-Driven or Polling?
The event-driven vs polling debate is resolved with a hybrid approach.
Polling — the website periodically requests changes from the ERP. Simpler to implement but creates a delay (5–15 minutes on average before update) and unnecessary load on the ERP.
Webhooks (Change Data Capture) — the ERP notifies the website of each change. Minimal delay (seconds), but requires ERP support and handling of outages. This is also called ERP webhooks or change data capture.
Hybrid approach (recommended) — webhooks for critical data (prices, stock), polling once an hour for less urgent data (descriptions, attributes). This gives you speed without sacrificing reliability. Event-driven is 10 times faster than polling for price and stock synchronization. Method comparison:
| Method |
Latency |
ERP Load |
Complexity |
| Polling |
5-15 min |
High |
Low |
| Webhooks |
1-5 sec |
Low |
Medium |
| Hybrid |
1-15 min |
Medium |
High |
| The hybrid approach is 3 times more efficient than pure polling in terms of critical data update speed and reduces ERP load by 40%. |
|
|
|
Deliverables
We don't just connect an API — we design the architecture, handle errors, and guarantee consistency. Deliverables include:
- Documentation: data schemas, field mapping, sequence diagrams.
- Code: synchronization module with logs, retries, and monitoring.
- Testing: unit tests, integration tests, load testing (we guarantee processing 10,000 SKUs in 2 minutes).
- Training: how to run, update, and debug the integration.
- Support: one month after launch.
A typical integration takes 12–20 working days. The investment pays off on average in 3–4 months due to reduced data errors. With 10+ years of experience and 50+ successful integrations, we deliver reliable solutions. Average savings from implementation: $24,000 per year on a large catalog.
Step-by-Step Implementation Plan
- ERP audit — study documentation, test environment availability, API version.
- Architecture design — choose protocols, define field mapping.
- Implement synchronization module — write code with retry logic and idempotency.
- Integration testing — simulate real scenarios (price change, product creation).
- Load testing — check with 10,000+ records.
- Deployment and monitoring — set up alerts, logging, dashboards.
- Team training — hand over documentation, conduct a workshop.
Example Integration with Odoo
For Odoo, we use Odoo XML-RPC via xmlrpc.client (Python). Example connector:
import xmlrpc.client
class OdooConnector:
def __init__(self, url, db, username, password):
self.url = url
self.db = db
# Authentication
common = xmlrpc.client.ServerProxy(f'{url}/xmlrpc/2/common')
self.uid = common.authenticate(db, username, password, {})
self.models = xmlrpc.client.ServerProxy(f'{url}/xmlrpc/2/object')
def get_products(self, since: datetime = None):
domain = [['active', '=', True]]
if since:
domain.append(['write_date', '>', since.isoformat()])
return self.models.execute_kw(
self.db, self.uid, self.password,
'product.template', 'search_read',
[domain],
{'fields': ['id', 'name', 'default_code', 'list_price',
'qty_available', 'categ_id', 'description_sale']}
)
Handling Changes
class ERPSyncService:
def sync_products(self):
last_sync = SyncState.get_last_sync('erp_products')
products = self.erp.get_products(since=last_sync)
updated = 0
for erp_product in products:
product, created = Product.objects.update_or_create(
erp_id=erp_product['id'],
defaults={
'name': erp_product['name'],
'sku': erp_product.get('default_code', ''),
'price': erp_product['list_price'],
'stock': erp_product['qty_available'],
'category': self.map_category(erp_product['categ_id']),
}
)
updated += 1
SyncState.update_last_sync('erp_products', datetime.now())
return updated
Error Handling and Retries
For connection interruptions, we use exponential backoff: first retry after 1 s, then 2 s, 4 s, up to 5 attempts. All errors are logged with ERROR level, and critical ones (incorrect mapping) send an alert to Telegram. For idempotency, each update checks write_date — if the website record is newer, we skip it.
Typical Synchronization Mistakes
- Ignoring cache. After a price update in ERP, the website shows the old one — check cache at HTTP or CDN level. We recommend invalidating cache via a purge request.
- Lack of idempotency. A repeated request should not create duplicates — use
update_or_create and unique keys.
- Connection interruption. Set up a retry mechanism with exponential backoff (see block above).
Timelines and ROI
Integration with a specific ERP via API with two-way synchronization: 12–20 working days, depending on documentation quality and ERP test environment availability. Cost is calculated individually after an audit. Over 10 years, we have completed more than 50 such integrations — this allowed us to optimize the process and reduce testing-stage errors by 95%. Average savings from implementation: $24,000 per year on a large catalog. The cost of a typical integration ranges from $15,000 to $30,000 depending on complexity.
Ready to evaluate your ERP in one day? Contact us — we'll find the optimal architecture. Get a consultation from an engineer who has already implemented synchronization for SAP, Odoo, and 1С.
Website CRM Integration: Bitrix24, amoCRM, Salesforce, HubSpot
A sales manager manually copies leads from email into the CRM. Half of them never make it. Follow‑up calls are missed. This isn’t a people problem — it’s an architectural gap between the website and the company’s core system. We close that gap with a direct site‑to‑CRM connection: leads land in the pipeline within 30 seconds after form submission, duplication is blocked, and status changes flow both ways automatically. Request a free integration audit to identify the bottlenecks in your current flow.
Integration isn’t just a POST to an API endpoint. It’s a battle against timeouts, duplicate records, data loss, and desynchronised states. We handle three core problems at once: asynchronous delivery (so the user never waits for the CRM), deduplication by email (one address – one lead), and two‑way feedback (a status change in the CRM instantly appears on the site). Below is how we tackle each.
Bitrix24: REST API and Event Handlers
Bitrix24 dominates the Russian B2B space. Its REST API works via OAuth 2.0 or an incoming webhook (webhook is simpler but less secure for production). Main entities are lead, deal, contact, and company.
Creating a lead requires POST /rest/crm.lead.add with the correct field set. Attaching it to a funnel means passing SOURCE_ID. Adding a timeline comment uses crm.timeline.comment.add. Real‑time tracking is done through Event Handlers: register a hook with event.bind; Bitrix24 pushes a POST to your endpoint when any deal status changes.
The real complexity lies in custom fields. Every Bitrix24 installation has its own set, and their IDs must be fetched via crm.lead.fields. Mapping those fields between the site and the CRM can be done manually or automatically — we use an automatic detection mechanism that works even in non‑standard configurations (proven on 20+ projects). We guarantee correct matching, so no lead arrives without the right pipeline stage or source tag.
amoCRM: Clean REST with Predictable Endpoints
amoCRM (now Kommo for international markets) offers a cleaner API. OAuth 2.0 with refresh token, JSON API, and well‑structured endpoints. Pipelines are pipelines, deals are leads, contacts are contacts.
A common mistake: when creating a deal you must supply pipeline_id and status_id explicitly. Without them the deal lands in the default pipeline – often the wrong one. Tags for source classification go into _embedded.tags. Incoming webhooks are configured in the admin panel; they support add, update, delete, status, and note events. We always verify the webhook signature using the API key and make sure the endpoint responds with 200 OK in under 5 seconds – otherwise the CRM marks delivery as failed.
Salesforce and HubSpot: Enterprise‑Grade Integration
Salesforce is the enterprise standard. It offers REST API, SOQL for complex queries, and Apex for server‑side logic. Integration can be direct via Salesforce REST API or through middleware like Zapier or MuleSoft. For PHP projects we use phpforce/soap-client or the Force.com‑Toolkit. The main challenge is mapping hundreds of custom objects and fields; we solve it with Describe Global to collect metadata automatically – cutting setup time by three‑quarters compared to reading documentation manually (Salesforce Developer Guide).
HubSpot is popular among SaaS companies and international B2B. Its API v3 provides a REST interface with solid SDKs for PHP and Node.js (@hubspot/api-client). Contacts, Companies, Deals are standard objects. The Forms API lets you send data from any custom form directly to HubSpot without using the native widget. One pitfall: the access_token must include the right scopes; a misconfigured token returns 403 Forbidden with a vague message. We include error_logging that captures the error code – debugging takes minutes instead of hours.
Which CRM fits your business: Bitrix24, amoCRM, or HubSpot?
| Criteria |
Bitrix24 |
amoCRM |
HubSpot |
| API complexity |
Medium (REST + webhooks, custom fields) |
Low (clean JSON API) |
Medium (REST + SDK, OAuth 2.0) |
| Typical synchronous latency |
200‑600 ms |
100‑300 ms |
150‑400 ms |
| Built‑in deduplication by email |
crm.duplicate.findByComm |
Contact search |
contacts/search |
| Webhook events |
Event Handlers (push) |
Admin panel configuration |
Webhook + Automations |
| Best suited for |
Russian B2B, government, custom fields |
Small‑ to medium‑sized business |
International B2B, SaaS |
Why is asynchronous sending important?
Calling a CRM API synchronously from the form handler is a mistake. The API may respond in 2 seconds – or time out. The user sits waiting. The correct pattern: form submission → save to database → queue a job → return 200 to the user immediately. A background worker then pushes the lead to the CRM. If the CRM is down, the worker retries with exponential backoff. We use Redis + Bull on Node.js or Laravel Queue on PHP – this guarantees delivery even during temporary outages.
Deduplication – how we stop duplicate leads
The same contact may fill the form twice. Without deduplication the CRM ends up with two identical leads. Before creating a new lead we search by email: for Bitrix24 we call crm.duplicate.findByComm, for HubSpot we use contacts/search. If a match is found we attach a task or comment to the existing lead instead of creating a new one. In our projects this cuts duplicate entries by 95%.
Two‑way synchronization – what happens when a manager changes a deal status
If a manager updates a deal status in the CRM, the website needs to reflect that change – especially if the client has a personal account. We configure webhooks from the CRM to an endpoint on the site, then update the local database and notify the client. Critical details: verify the webhook signature and respond with 200 OK within 5 seconds, otherwise the CRM assumes delivery failed. We guarantee that the delay between a status change in the CRM and its appearance on the site never exceeds 3 seconds.
How do we conduct integration in 5 steps?
- Audit of data flows – analyse current lead transfer, CRM field structure, and performance bottlenecks. Deliverable: “as‑is” and “to‑be” data flow diagrams.
- Architecture design – choose the queue mechanism (Redis Bull or Laravel Queue), define the deduplication method, and prepare a field mapping specification.
- Implementation on staging – write code on Laravel or Node.js, configure webhooks, and test with real data: lead creation, status updates, and error handling.
- Load testing – simulate peak traffic (e.g. 500 requests per minute) and adjust retry policies and timeout settings.
- Deployment and documentation – push to production, train the team on monitoring and retry cleanup, and deliver full endpoint documentation.
What is included in the work
- Audit report with current data flow diagrams and typical error patterns.
- Architecture design document specifying queue, deduplication, and mapping.
- Production‑ready integration code on Laravel or Node.js.
- Webhook configuration and signature verification.
- Team training on support tasks and retry cleanup.
- 30‑day warranty support for bug fixes and mapping adjustments.
Real‑world case: real‑estate agency with 400 leads per month
Click to expand
A real‑estate agency processed every incoming lead manually – 400 leads per month. Each lead took 3 minutes to enter, and 15% were lost because emails were missed. We integrated their site with amoCRM using asynchronous queue delivery and automatic deduplication. Leads now appear in the pipeline within 5 seconds, and leftover tasks are automatically assigned to the next available agent. Result: 30% increase in conversion and $12,000 saved annually in administrative overhead.
Timelines
| Scenario |
Duration |
| One CRM, lead transfer from forms |
1‑2 weeks |
| Two‑way synchronization + statuses |
3‑5 weeks |
| Multiple CRM + custom field mapping |
4‑8 weeks |
The exact cost is calculated after an audit of your current processes and CRM data structure. Contact us for a project estimate – we will send a commercial proposal within one business day. With 5+ years of experience and more than 20 completed integrations, you get a solution that works from day one. Get an engineer consultation to see how your sales funnel can run without manual lead transfer.
Additional sources: Customer relationship management (Wikipedia) · REST API (Wikipedia)