Saudi commerce · Catalog synchronization

Salla and Zid catalog sync: stop ghost products safely

A practical design for catalog identity, idempotent updates, missing-item detection, tombstones, reconciliation and controlled deletion across Salla and Zid.

TOPIC HUBE-commerce Engineering
Original technical illustration of Salla and Zid catalogs passing through identity, snapshot, tombstone and reconciliation gates before publication.
An editorial interpretation of the topic, followed by a practical execution diagram.

Reliable Salla and Zid catalog synchronization needs an identity ledger, a desired-state snapshot and a reconciliation workflow—not a loop that copies rows by SKU and deletes whatever is missing. Treat each platform product ID as the remote identity, keep SKU as a merchant-controlled business key, and preserve every observed alias. Mark missing products as tombstones, quarantine them for review, then perform a verified delete only when ownership and policy allow it. This design helps ERP, PIM and marketplace integrations avoid duplicate products, stale listings and accidental catalog loss.

The official APIs expose useful primitives, but they do not define your cross-system truth. Salla documents lookup, update and deletion by SKU; Zid documents a paginated product list for full synchronization and bulk updates by IDs or SKUs. Those are transport contracts. The rules for ownership, identity mutation, conflict resolution and deletion remain application responsibilities.

This guide was verified against the official Salla and Zid documentation on 12 October 2026. Documented endpoints, scopes and response shapes are platform facts. The ledger, tombstone, quarantine and rollout design below are engineering recommendations, not claims that either platform implements them automatically.

Separate identity from SKU

A product has several identifiers with different meanings. Your ERP or PIM should own an immutable `catalog_item_id`. Salla and Zid assign their own remote product IDs. SKU, barcode and slug are business attributes that may be edited, reused or reformatted. A robust mapping table therefore keys a connection by tenant, platform and remote ID, while storing the current SKU and its history as attributes.

catalog_item_id   platform   remote_product_id   current_sku   state
item_01HZX...     salla      1672908878          SHIRT-BLU-M   active
item_01HZX...     zid        a497974d-...         SHIRT-BLU-M   active

SKU aliases
item_01HZX...     SHIRT-BLUE-M   valid_to=2026-09-30
item_01HZX...     SHIRT-BLU-M    valid_to=null

Salla's Product Details By SKU endpoint returns a product for `/products/sku/{sku}`, and its update endpoint accepts a SKU in both the path and payload. That makes SKU convenient, but convenience is not immutability. If a merchant edits the SKU between read and write, a job may create a second mapping or update the wrong record. Prefer the remote product ID for stored identity when the relevant endpoint supports it; use SKU lookup as a discovery or repair path and require an unambiguous match.

Reject duplicate active SKUs inside the same merchant and channel unless the business explicitly supports them. Normalize only for comparison—case, whitespace or punctuation rules—while preserving the exact value sent to the platform. A normalization collision should enter review, not silently merge two products.

Define source ownership per field

Catalog sync fails when every system can overwrite every field. Define one owner for price, quantity, title, description, media, tax class, shipping requirement and publication status. An ERP may own stock and cost while a merchant team owns localized content inside Salla or Zid. The integration should update only the fields it owns.

Store a canonical desired-state document and a hash per owned field group. On each run, compare desired state with the last confirmed remote state. Send a patch only when an owned value changed. Do not send a full product object merely because an endpoint accepts it: stale fields in a broad payload can erase a merchant's recent edit.

Zid's Update Product endpoint can change core details, inventory and variants. That flexibility makes the ownership matrix essential. If the integration owns only price and quantity, its payload should contain only those values, and its audit record should say why the write occurred.

Use snapshots for complete scans

Incremental webhooks and updated-since queries reduce latency, but a catalog needs periodic full reconciliation. Events can be delayed, filtered incorrectly or lost during credential and webhook changes. Zid explicitly lists full product synchronization as a use case for its List Products endpoint, which returns paginated results and a next link.

Create a `sync_run` with a fixed start time, connection version and run ID. Follow the platform's pagination contract until completion; do not invent page numbers after the API returns a next link. Upsert every observed remote identity into a staging snapshot and record `last_seen_run_id`. Only after all pages succeed should the run be marked complete and compared with the previous complete snapshot. A partial scan must never authorize deletions.

STARTED -> SCANNING -> COMPLETE -> DIFFED -> APPLIED
              |
              +-------> FAILED (no absence decisions)

If the catalog changes while pages are being read, a scan may observe a mixed point in time. Use provider-supported ordering or cursors when available. Otherwise, accept that a scan is evidence rather than a database snapshot: perform a second targeted read before any destructive action, and let the next run converge.

A safe catalog sync separates identity, desired state, reconciliation and destructive actions so absence never becomes an immediate delete.
A safe catalog sync separates identity, desired state, reconciliation and destructive actions so absence never becomes an immediate delete. Open for a larger view

Turn absence into a tombstone, not a delete

A product missing from one complete scan may have been deleted, unpublished, filtered by a changed scope, moved to another store or hidden by a pagination failure. Record a tombstone candidate with reason, first-missing run, last known remote state and ownership. Keep it out of destructive queues until it passes policy.

A practical policy can require two successful full scans separated by time, a fresh credential check, no conflicting recent webhook, and confirmation that the upstream item is intentionally retired. High-value or recently ordered products should require manual approval. Products still referenced by variants, bundles or active orders may need unpublishing instead of deletion.

Quarantine provides reversibility: stop outbound updates, optionally set the listing to draft or unavailable when the platform and ownership contract allow it, and retain the mapping. A hard delete removes a recovery path and may break historical joins. Use it only when retention, legal and merchant policies explicitly require removal.

Make writes idempotent and observable

Every outbound operation needs an operation ID derived from tenant, item, desired version and action. Persist the request hash, target, attempt, response status and verification result. A retry with the same operation ID must not create a new logical action. Serialize writes per product mapping so a slow older update cannot overwrite a newer one.

For Salla, Update Product By SKU uses `PUT /products/sku/{sku}` with `products.read_write`. Before retrying an ambiguous timeout, read the product and compare the owned fields with the desired version. If they already match, mark the operation confirmed. If the SKU no longer resolves, search the mapping history before considering a create.

For Zid bulk changes, the official Bulk Update Products documentation recommends keeping a request at 50 IDs/SKUs or fewer because larger submissions may cause processing issues. Treat 50 as a documented upper bound, not a target batch size. Choose a smaller concurrency and batch size through testing, isolate failures, and verify each returned product rather than marking the whole source batch successful.

Reconcile before and after deletion

Salla documents `DELETE /products/sku/{sku}` and shows a `202` success response. A 202-class response is acceptance evidence, not sufficient proof that every downstream surface is already absent. Record the response, then verify with a read after an appropriate delay. If the product remains, move the operation to investigation instead of repeatedly issuing deletes.

The deletion worker should re-resolve the immutable catalog item, current remote ID, current SKU and ownership immediately before execution. Abort when the SKU now maps to another item, the mapping changed after approval, the upstream item became active, or the snapshot that created the tombstone was not complete. Write an immutable audit receipt containing who or what approved the action and which policy version was applied.

For platforms or product classes where hard delete behavior is unclear, use a safer state transition supported by the current contract, such as draft or unavailable, and surface the case to the merchant. Never infer a destructive endpoint or payload from another platform's semantics.

Handle conflicts as business decisions

A conflict exists when remote state changed after the integration's last confirmed read and the changed field is one the integration plans to overwrite. Detect it with field hashes or version timestamps when available. Do not resolve every conflict with “ERP wins.” Stock may need the ERP, while merchandising content belongs to the storefront team.

Classify conflicts: safe merge for independent fields, automatic owner-wins for an explicit contract, and review for identity, variants, tax or publication changes. Store both versions and the chosen resolution. This creates an explanation for merchants and prevents repeated oscillation between systems.

SKU changes need a dedicated workflow. Add the new alias, verify the remote identity, update dependent references, then retire the old alias after a grace period. Never create a new canonical item solely because a familiar SKU disappeared.

Test invariants, not just API responses

Build fixtures for renamed SKUs, duplicate normalized SKUs, deleted and recreated products, reordered pagination, transient 500 errors, expired credentials, partial scans and edits during reconciliation. Assert that no incomplete scan emits a delete, one remote ID maps to at most one active canonical item, and one approved operation produces one logical effect.

Measure snapshot completion time, items seen, create/update/quarantine/delete counts, unresolved identities, conflict rate, retry age and verification lag. Alert on sudden catalog shrinkage, page-count changes, authentication failures and a high ratio of tombstones. A circuit breaker should stop destructive operations when a run differs radically from the recent baseline.

Run the new design in shadow mode first. Compare proposed writes with actual remote state without applying them. Then enable non-destructive fields for a small merchant cohort, followed by quarantine. Hard deletion should be the final capability, guarded by a separate permission and kill switch.

When not to build bidirectional sync

Do not build a two-way catalog engine when a nightly CSV import with human review satisfies the business need. Do not use SKU-based upserts when merchants frequently rename SKUs and no stable mapping exists. Do not let both platforms own the same price or quantity field without a deterministic conflict contract. And do not synchronize every attribute merely because it is available.

A single authoritative PIM with one-way publication is easier to reason about. Platform-native management may be best for a small catalog. Event-driven updates are useful for speed, but periodic full scans remain necessary for repair. The architecture should match the cost of a stale or deleted product, not the maximum complexity an API permits.

Executive implementation plan

Create an immutable canonical item ID and a mapping for every remote product ID. Track SKU aliases instead of treating SKU as identity. Declare field ownership. Store desired state and confirmed remote state separately. Run complete, resumable snapshots. Convert absence to a tombstone and quarantine before deletion. Make writes idempotent, serialize them per product and verify the result. Add circuit breakers, audit receipts and a kill switch.

The central rule is simple: absence is evidence to investigate, not permission to delete. With that boundary, Salla and Zid integrations can converge reliably while preserving merchant control and a recoverable history.

Official references

These references document the tools discussed. Examples and design decisions are illustrative and should be adapted to the project and its versions.

Prepared by: Noor Yasser

FROM DECISION TO DELIVERY

Working through a similar engineering challenge?

I help teams turn architecture decisions into a clear scope and dependable, reviewable implementation.

Book a 30-minute callRelated serviceSalla & Zid apps and merchant toolsRelevant projectZeedly