Saudi commerce · Catalog architecture

Salla product variants: migrate the schema, not rows

Changing a Salla option can reshape every sellable variant. Use stable identity, shadow validation and reconciliation before writing prices or stock.

TOPIC HUBE-commerce Engineering
Original conceptual illustration of an ecommerce product variant graph passing through a controlled migration gate; not a real Salla interface.
An editorial interpretation of the topic, followed by a practical execution diagram.

A product option looks like a field in an admin screen, but an integration should treat it as part of a catalog schema. Adding a color or size can create new sellable combinations. Removing an option can remove its related values and variants. A positional join that once mapped “Blue / Medium” to an ERP row can silently point at a different commercial object after the graph changes.

Salla's current Merchant API exposes product options and product variants as separate resources. The List Product Variants endpoint returns each variant with its remote identifier, SKU, prices, stock quantity and related option values. The Create Product Option endpoint notes that creating an option for a physical product generates variants, while the Delete Product Option endpoint explicitly says deletion includes related values and variants.

This is a practical architecture guide based on official Salla documentation verified on 2 October 2026. It is not presented as a breaking release. The goal is to prevent a catalog synchronization job from corrupting SKU identity, price or stock when the merchant changes option structure.

Model the catalog as a graph

Represent a product as a graph: product, option, option value and sellable variant. A variant points to the exact set of option-value identifiers that defines it. Price, sale price, cost, barcode, SKU, weight and stock belong to the variant record, not to the display position of a color or size.

Keep three identities. The platform identity is Salla's immutable resource ID. The business identity is the merchant's SKU or another reviewed external key. The synchronization identity is your tenant-scoped mapping between the two. Never use an array index, translated label or rendered option order as a durable key. Labels change; order changes; IDs and reviewed business keys are what make reconciliation explainable.

Store the source shape before transforming it: product ID, option IDs, option-value IDs, variant IDs, SKU, barcode, prices, stock mode, branch quantities when applicable and the platform update timestamp. This snapshot is evidence for planning, rollback decisions and later support—not a license to replay old values blindly.

Define invariants before planning mutations

A migration needs statements the system can prove. Useful invariants include: every active sellable combination has exactly one remote variant; every managed SKU maps to one tenant and one remote variant; no two target variants claim the same barcode; currency and tax semantics remain unchanged; and exactly one system owns stock for each location.

Separate fields by owner. A PIM may own names, images and option structure. An ERP may own cost and stock. Salla may remain authoritative for storefront status, merchandising and a merchant-edited sale price. Without this field-level contract, a “full sync” becomes a last-writer-wins race between systems.

Classify the proposed change before execution. A label correction is metadata. A price change is a value mutation. Adding an option value expands the variant graph. Removing an option contracts the graph and may be destructive. Changing a SKU changes a business key and needs a mapped transition. Different classes require different validation and approval.

Build an explicit old-to-new identity map

Read the current variants and normalize each combination into a canonical key made from sorted remote option-value IDs. If the target structure introduces new values, assign temporary planning IDs until Salla returns real IDs. Join existing variants first by remote variant ID, then by an approved stable SKU where the ID is unavailable. Never fall back to fuzzy labels automatically.

The migration plan should list `keep`, `create`, `update`, `retire` and `manual_review` operations. For every retiring variant, record its replacement or the reason it has no replacement. If two old variants collapse into one target combination, the tool must stop: prices, stock and order history cannot be merged safely by a generic rule.

Use a versioned plan with a digest of the source snapshot. Immediately before writing, re-read the product and compare that digest. If a merchant changed the catalog after planning, invalidate the plan instead of applying it to a new shape. Optimistic concurrency can live in your application even when the remote endpoint does not expose a version precondition.

A safe catalog migration snapshots the current option graph, maps stable identities, validates the target in shadow mode, writes in small batches and reconciles remote state.
A safe catalog migration snapshots the current option graph, maps stable identities, validates the target in shadow mode, writes in small batches and reconciles remote state. Open for a larger view

Validate the target in shadow mode

Generate the complete target graph without calling Salla. Count expected combinations and compare them with the business rules. Check for duplicate SKU and barcode values, missing prices, impossible stock ownership, untranslated required labels and combinations that the merchant intentionally excludes.

Run representative carts or downstream transformations against the shadow graph. Can the storefront selection resolve one variant? Can the ERP export resolve the same SKU? Does the search index still have one document per sellable object? Do existing orders retain references to historical variants even when those variants are no longer sellable?

The visual diff should show the merchant what changes: four variants kept, two created, one retired, three prices updated and no stock overwritten, for example. Approval must bind to the exact plan digest. If the source snapshot changes, the approval expires.

Write in phases and respect Salla's contracts

Create structural resources before applying variant values. Salla's Create Product Option documentation warns that, for a new physical product with variants, price must be set after options are created; including it during that step can produce a `422` validation error. That is evidence that structure creation and commercial values are separate phases.

After Salla returns the new graph, refresh the variant list and bind real IDs. Then issue narrow variant updates. The Update Product Variant endpoint accepts optional fields and requires at least one field, so send only values owned by this integration. Do not include stock in a price-only patch simply because the local model has a stock field.

Execute small, resumable batches per store. The rate-limit documentation says limits vary by store plan and exposes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `Retry-After` and `X-RateLimit-Reset`. Use those headers to pace one tenant queue. A higher worker count must not multiply calls against the same merchant budget.

Make each operation idempotent in your ledger even if the remote API does not accept an idempotency key: store plan ID, operation ID, target resource, payload digest, attempt and verified result. After a timeout, read the variant before retrying. A lost response is an unknown outcome, not proof that the write failed.

Treat deletion as an irreversible boundary

Deleting an option is not a cleanup detail. Salla documents that its related data, values and variants are deleted too. Put destructive operations in a separate phase after all creates and updates have been verified. Require an explicit list of remote IDs, a recent source snapshot and a merchant-visible impact summary.

Prefer retiring or hiding obsolete combinations when the business and platform workflow permits it. Historical orders, analytics, returns and support tooling may still need the old identity even when the combination is no longer offered. Your local catalog should keep a tombstone with the last known mapping rather than recycling the SKU or deleting the audit trail.

A rollback cannot always recreate the same remote IDs. Define rollback as restoring business behavior—correct sellable combinations, prices and stock ownership—not pretending the catalog never changed. If the migration crosses a destructive boundary, take a fresh export and require a second approval.

Reconcile webhooks with current state

Webhooks are change signals, not complete catalog snapshots. Salla's webhook documentation lists granular product events such as price, status, image, category, brand and tag updates, alongside create and delete events. Build only against events currently available to the installed app; do not assume one generic event covers every option or variant mutation.

Verify the signature on the raw request, persist the event, return success quickly and process asynchronously. Deduplicate delivery, but still fetch current product and variant state before changing your local graph. Multiple merchant edits can occur between the event and the worker. The latest authoritative read decides the target state.

Run periodic reconciliation by store and checkpoint. Compare remote variant IDs, canonical option-value keys, SKU, prices and stock ownership with the last verified snapshot. Drift should create a repair plan, not an automatic full overwrite. If identity is ambiguous, mark the product for review and pause writes for that product only.

Test the failure modes that change money

Test option creation followed by a timeout, duplicate workers, a merchant edit after approval, a `422` during value application, rate limiting mid-batch, two old variants collapsing into one, missing SKU, duplicate barcode and deletion requested while a new order references an old variant. Verify that a retry never creates a second logical variant or overwrites merchant-owned stock.

Measure migrations planned, invalidated and completed; variants created, retained and retired; ambiguous mappings; read-after-write mismatches; `422` responses; throttling time; and products paused for review. Track the age of the last complete reconciliation per store. A green job status is not enough if the catalog graph is drifting.

Start in report-only mode. Snapshot catalogs, build plans and compare expected results with the merchant's view without writing. Then enable a small cohort, forbid destructive operations initially and keep a tenant-level kill switch. The safe implementation is intentionally boring: evidence, explicit identity, narrow writes and verified state.

The practical decision

If a Salla integration changes product options, it is performing a schema migration over commercial data. Treating variants as rows invites silent identity errors; treating them as a graph makes the impact visible.

Anchor mappings to remote IDs and reviewed SKUs, declare field ownership, validate the full target in shadow mode, re-check the source before writing, pace small batches from live rate-limit headers and read after uncertain outcomes. Deletion belongs behind a separate approval because it can remove the very variants that prices, stock and historical workflows depend on.

The success criterion is not “the API returned 201.” It is that every sellable combination still has one explainable identity, the correct price and one clear stock owner—and that the system can prove why.

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