Skip to Content

Monday.com ↔ Retail Express Sync

Speaking REST and SOAP to the Same ERP: Two-Way Monday.com ↔ Retail Express Sync
25 September 2026 by

Client: International Salon Supplies

Role: Integration Engineer

Period: Nov 2025

Status: Production

Tech stack: Node.js 22, TypeScript, Express, RabbitMQ, Drizzle ORM, SQLite (libSQL), Monday.com GraphQL API, Retail Express REST API, Retail Express SOAP API, fast-xml-parser, Docker, Caddy

The purchasing team tracks imports on a Monday.com board, but purchase orders live in Retail Express. This service keeps both in sync in both directions: ERP changes flow to Monday over RabbitMQ, and edits on the board are written back to the ERP through its legacy SOAP API, with snapshot diffing to prevent sync loops.

The problem

Import purchase orders move through many stages: ordered, sailed, in a container, arrived, received. The purchasing team manages that journey on a Monday.com board, but the purchase orders themselves live in Retail Express (REX). Keeping status, ETA, supplier invoice numbers, sailing dates and container numbers in step between the two was manual double entry.

Architecture

(A) ERP → Monday
  ERP change stream → RabbitMQ (durable, prefetch 1)
     ORDERS           → upsert order snapshot
     PURCHASE_ORDERS  → only if the PO is tracked on the board:
                        upsert PO snapshot → Monday GraphQL change_multiple_column_values
     ack on success · nack (no requeue) + remote error log on failure

(B) Monday → ERP
  Monday webhook (with challenge handshake)
     PO number entered      → fetch PO from ERP REST → fill 9 columns on the board
     ETA / invoice / sailed / container changed
         → load full Monday item → compare with stored snapshot (skip if unchanged)
         → fetch PO + items from ERP → build XML → ERP SOAP CreateUpdatePurchaseOrders

(C) Supplier lookup webhook → page through ERP suppliers → write details back to Monday
(D) Backfill endpoint → cursor-paginated board scan → seed snapshots

Engineering highlights

A hybrid REST + SOAP integration

The ERP's modern REST API is good for reading but can't update the shipping fields on a purchase order. Those writes have to go through the legacy SOAP web service. The service reads over REST with a bearer token, builds nested, escaped XML for CreateUpdatePurchaseOrders, and parses the SOAP envelope to confirm the result. Most real ERP integrations end up with a split like this, and it's better to embrace it than fight it.

Preventing ping-pong

Two-way syncs can loop: A updates B, B's webhook updates A, and so on. Every Monday change is compared against a stored snapshot of that board item. If nothing meaningful changed, the ERP write is skipped. All snapshot tables use idempotent upserts keyed on ERP IDs.

Only sync what matters

ERP purchase-order events are applied only to POs already tracked on the board. A sync flag column is switched off automatically when the ERP marks a PO as received, so completed POs stop generating traffic.

Resilience

  • RabbitMQ: 60 s heartbeats, a 30 s connect timeout, exponential-backoff reconnects (5 s doubling, capped at 30 s, up to 10 attempts), prefetch(1) for ordered processing, and graceful shutdown.
  • The ERP access token is cached in SQLite with its expiry, so it survives container restarts.
  • Errors are sanitised (circular references, Error objects) and shipped to a central error-logging service.

Supplier auto-fill

Typing a supplier code on a second board triggers a webhook that pages through the ERP's supplier list and fills in the supplier's details automatically.

By the numbers

  • ~930 lines of TypeScript
  • 4 HTTP endpoints plus a queue consumer handling two event types
  • 9 mapped Monday columns, 6 SQLite tables

What I'd add next

Verify Monday webhook signatures, and wire up the retry helper for transient ERP failures. Both are small changes that turn a working integration into a hardened one.