Syncing Customer Master Data from XiaoMan CRM to Lingmao: A Field Guide to One Incremental Strategy
What This Strategy Solves
Pushing customer master data from a CRM to a downstream system looks like simple "搬运," but if codes are not aligned and fields are not properly mapped, the two systems drift apart within three months: sales renames a customer in the CRM while the downstream invoicing address stays stale; the customer source field is a numeric ID in the CRM but a Chinese label downstream. In one real project, we used the Qeasy Data Integration Platform to host this strategy, incrementally syncing customer data from XiaoMan OKKI CRM to Lingmao and unifying code semantics so business teams stop reconciling by hand.
Data Flow and Field Mapping
The flow is one-way: XiaoMan OKKI CRM → Qeasy middleware → Lingmao. The source first calls a list endpoint to fetch customer IDs, then calls the detail endpoint per ID and merges the records, before pushing the mapped payload to the target.
Key field mapping:
| Target Field (Lingmao) | Source Field/Rule (XiaoMan) | Mapping Type | Notes |
|---|---|---|---|
| ApiCode | serial_id | DIRECT | Customer code, taken from company serial |
| Name | name | DIRECT | Full customer name |
| ShortName | name | DIRECT | Short name, mapped from name |
| operator | XiaoMan | CONSTANT | Operator, fixed constant |
| Att01 | origin_name | DIRECT | Customer source display name |
| KPAddress | country_name | DIRECT | Invoicing address (country/region label) |
| Remark | owner.0.name | DIRECT | Sales rep, first element of owner array |
Four mapping types exist: DIRECT, CONSTANT, COLLECTION (code mapping), and TRANSFORM (function-based). A common pattern on Qeasy is centralized code mapping management: keep enums like customer source and country/region in a dedicated code table or a standalone sync strategy, so changes happen in one place instead of being scattered across strategies.
How to Configure on Qeasy
The source uses a list + detail merge pattern: in Qeasy's source configuration, set info_api (here /v1.company/info) and info_key (company_id) under otherRequest, and the platform auto-fetches the detail per primary key and merges it into the list record. Pagination uses start_index=1 and count=20 by default; removed=0 and all=1 control whether deleted records and all customers are returned.
The incremental window uses platform time variables: start_time resolves to {{LAST_SYNC_TIME|datetime}} and end_time to {{CURRENT_TIME|datetime}}, which the platform computes on every scheduled run. The target endpoint has idCheck=true, so writes are deduplicated by primary key.
Implementation Steps
A phased rollout is more reliable:
- Calibrate the incremental watermark: before the first run, do a one-time full export from the source and record each customer's latest update time as the initial "last sync time" in Qeasy. Starting from "now" silently drops historical data.
- Trigger one full sync: use Qeasy's manual execution to push the full history to Lingmao once, validating field mappings and code conversions end to end.
- Enable scheduled runs: source
*/8 6-22 * * *(every 8 minutes, 6am–10pm), target*/10 6-22 * * *(every 10 minutes, 6am–10pm). The 2-minute offset avoids burst pressure on the downstream. Schedules pause at night to leave a maintenance window.
Lessons from the Field
- Treating
origin_nameandcountry_nameas already-displayed labels is a classic pitfall. If the source detail endpoint actually returns IDs or internal codes, you must add COLLECTION mappings or_findCollectionlookups, otherwise the downstream sees a wall of numbers. The safe move is to capture a real response from the detail endpoint on Qeasy first, then decide whether a code table is needed. owner.0.namelooks trivial but is structurally fragile.owneris an array and only the first element is the primary sales rep; if the actual field is nested under a "management info" group in the detail payload, the path differs. Always run a live-data dry run before going to production.- Reusing
namefor ShortName is only a stopgap. Currently ShortName also maps fromname, which means there is effectively no short name. When the business later needs to distinguish full vs. short names, switch the mapping to{{short_name}}and use_functionto null-coalesce so the downstream field is never blank. - Offset frequencies on purpose. Source every 8 minutes, target every 10 minutes — this staggering is deliberate. Identical frequencies make the target idle when the source lags and get hammered when the source bursts.
- Make the incremental watermark auditable. Qeasy stores a "last sync time"; if you change it manually or ignore time zones, you will get duplicates or gaps. Log any change so you can roll back when something breaks.
When to Use and When Not To
Use when: you need one-way customer master sync from a CRM to a downstream ERP/SRM, the source exposes standard REST APIs with an update timestamp, and the target supports upsert by code. Do not use when: you need bi-directional sync, complex customer record merging, the source lacks an update-timestamp batch API, or sub-second latency is required — for those, prefer event-driven integration over scheduled polling.