Multi-location inventory synchronization is not a matter of copying one number into another system. The same SKU may be sellable from a Riyadh warehouse, a Jeddah branch and a point-of-sale location, while an ERP or WMS records receipts, transfers, reservations, damage and cycle counts on a different timeline. A reliable connector needs an explicit identity for every product-location pair and a recovery path when a write succeeds remotely but the response is lost.
Zid's current Merchant API exposes two useful batch orientations. The product stock endpoint updates one product across multiple locations. The location stock endpoint updates multiple products inside one location. Both write absolute `available_quantity` values and return HTTP 204 without a response body. Those contracts make the choice of batch boundary—and the verification after it—part of the integration architecture.
Model inventory as a product-location matrix
Start with a stable key such as `store_id + product_id + location_id`. Do not use a warehouse name as identity: names and addresses can change, while an integration must retain the same mapping. The List Locations endpoint uses the `inventories.read` scope and returns location identifiers. Its page warns that list output should be treated as summary data and directs clients to the location-detail endpoint for complete information. Use the stable ID as the foreign key and fetch details when a decision depends on properties such as whether the location is enabled.
Keep an explicit mapping from the ERP warehouse and SKU or variant to Zid's location and product IDs. Record mapping status, creation source and last verification time. An unknown location, duplicated SKU mapping or disabled warehouse should enter an exception queue; it must not silently fall back to the default location. Silent fallback creates inventory that looks valid while belonging to the wrong fulfillment point.
Define the quantity contract before the first call. Zid's examples use `available_quantity`, not an event delta. Decide whether the ERP supplies on-hand, available-to-sell after reservations, or another derived value. Keep safety stock and cross-channel reservations in exactly one owner. If both the storefront and ERP subtract the same order, the integration will understate inventory even though every API call is technically correct.
Choose product-centric batches for cross-location changes
Use `PATCH /v1/products/{product_id}/stocks/` when one product changes across several locations—for example, when a central planning system redistributes the available quantity of one SKU after a transfer. The request body is an array of records containing `location`, `available_quantity` and `is_infinite`; the endpoint requires `products.read_write`.
[
{ "location": "loc-riyadh", "available_quantity": 18, "is_infinite": false },
{ "location": "loc-jeddah", "available_quantity": 7, "is_infinite": false }
]The product boundary is useful when a replenishment or catalog process owns one SKU and needs to set its complete location view together. It also simplifies a read-after-write check: `GET /v1/products/{product_id}/stocks/` returns that product's stock records with location and availability data using the `products.read` scope. Compare the returned matrix to the intended values rather than marking the operation complete solely because the PATCH returned 204.
Do not send a location's entire catalog through this route by looping over products without control. That creates many independent requests, makes partial progress hard to see and can interleave stale and newer snapshots for the same location. Choose the endpoint around the business operation, not whichever URL was discovered first.
Choose location-centric batches for warehouse snapshots
Use `POST /v1/locations/{location_id}/stock-update/` when a warehouse count, WMS export or receiving job provides many products for one location. Its payload contains `product_id`, `available_quantity` and `is_infinite`, and the documented scope is `inventories.read_write`. A disabled location can produce HTTP 400, so validate location state during onboarding and again when a failure indicates configuration drift.
[
{ "product_id": "product-a", "available_quantity": 34, "is_infinite": false },
{ "product_id": "product-b", "available_quantity": 12, "is_infinite": false }
]This boundary matches a cycle count or end-of-receiving snapshot: one operational unit is either accepted for delivery or placed into a location-specific recovery workflow. Zid's public page does not state a maximum array length, so do not invent one. Establish a conservative batch size through sandbox testing, cap payload bytes and duration, and keep batch membership in your own ledger. Smaller deterministic chunks make timeouts and reconciliation cheaper than one opaque warehouse-sized request.
Product-centric and location-centric writes should not run independently for the same matrix cells. Assign one writer by source and operation type. A transfer can be represented as one ordered workflow with two cells, but a warehouse snapshot must not overtake a newer sale or adjustment. Partition the queue by `store + product + location`, attach a monotonically increasing source version, and reject any job older than the last applied local version.
Treat 204 as transport success, then verify state
Both bulk pages document HTTP 204 with no response body. That response confirms a successful HTTP operation, but it contains no per-record result to store. A network timeout is more ambiguous: the remote service may have applied the absolute values even though the connector never received the response. Repeating the same absolute assignment tends to converge to the same value, but a delayed retry can still overwrite a newer legitimate change. Do not equate absolute updates with safe unordered retries.
Persist every intended snapshot before transmission in a durable outbox. A useful record contains the source version, store, operation boundary, payload hash, matrix cells, attempts, HTTP result and verification state. Serialize changes for each cell. Before retrying an `unknown` outcome, compare its source version to the newest queued and verified version; discard or supersede stale work instead of replaying it blindly.
After a successful or ambiguous write, read back affected product stocks and verify the exact cells. Mark a batch `verified` only when values match, `superseded` when a newer version owns the cell, and `drifted` when remote state disagrees without a known newer operation. This state machine prevents a 204 from becoming an unexamined promise.
Webhooks accelerate detection but do not replace reconciliation
Zid documents a generic `product.update` webhook, but its supported-events page does not list a dedicated stock-changed event. Do not assume the generic event will provide every inventory transition or a complete per-location ledger unless the actual subscribed payload and contract prove it. Webhooks can trigger a targeted refresh; scheduled reads remain the recovery mechanism.
Run incremental reconciliation for cells touched recently and a slower full sweep across active mappings. For each product, compare the stock-list response with the connector's expected matrix. Classify discrepancies as a newer platform-side change, a pending ERP job, an unmapped location, an infinite-stock mode mismatch or an unexplained drift. Do not automatically overwrite every difference: a merchant may have made a valid emergency correction that the ERP must import or an operator must approve.
The `is_infinite` flag deserves explicit handling. When it is true, the documented stock response can show `available_quantity` as null. Never coerce that into zero, and never switch a finite SKU to infinite merely because a source field is missing. Model stock mode separately from numeric quantity and require an intentional transition.
Roll out with ownership, metrics and least privilege
Begin with one store, two locations and a small set of low-risk products. Test a product redistribution, a warehouse snapshot, a disabled location, a timeout after submission, a stale retry and an operator correction made directly in the merchant system. Verify both API state and the downstream selling behavior expected by the merchant.
Separate the scopes used by the connector. Location discovery uses `inventories.read`; location-centric writes use `inventories.read_write`; product stock reads use `products.read`; product-centric writes use `products.read_write`. Request only the paths the application needs, isolate credentials per merchant and avoid logging access tokens, customer data or full authorization headers.
Monitor outbox age, unknown outcomes, verified-write latency, stale jobs suppressed, disabled-location errors, mapping failures, reconciliation drift and the number of cells in infinite mode. Alert on a specific store, product and location so an operator can act. The goal is controlled convergence, not a dashboard that merely reports successful HTTP calls.
Zid supplies the essential primitives: discover locations, read stock records and write absolute availability in batches organized around either a product or a location. A dependable ERP or WMS connector adds stable identities, one writer per matrix cell, ordered versions, durable delivery, read-after-write verification and reconciliation. Choosing the correct batch boundary makes failures small enough to explain—and stock accurate enough to trust.
Official references
These references document the tools discussed. Examples and design decisions are illustrative and should be adapted to the project and its versions.
- Zid Merchant API — List Locations, verified 26 September 2026
- Zid Merchant API — List Product Stock Records, verified 26 September 2026
- Zid Merchant API — Bulk Update Product Stock Records, verified 26 September 2026
- Zid Merchant API — Update Product Stock for Location, verified 26 September 2026
- Zid Webhooks — Supported events, verified 26 September 2026
Prepared by: Noor Yasser
Working through a similar engineering challenge?
I help teams turn architecture decisions into a clear scope and dependable, reviewable implementation.




