Hoe voorkom je fouten wanneer je productdata naar meerdere marketplaces stuurt?
Wij verkopen producten via onze eigen webshop en willen daarnaast uitbreiden naar meerdere marketplaces. Op dit moment worden productinformatie, prijzen en voorraad nog grotendeels handmatig bijgewerkt.
We merken dat dit steeds meer tijd kost en dat er fouten ontstaan. Bijvoorbeeld een verkeerde voorraad, dubbele producten, ontbrekende specificaties of een prijs die op het ene platform wel is aangepast en op het andere niet.
Daarom onderzoeken we of we dit centraal kunnen beheren met een eigen koppeling of SaaS-oplossing. Het idee is dat productdata uit Shopify wordt opgehaald, centraal wordt opgeslagen en vervolgens automatisch naar verschillende marketplaces wordt doorgestuurd.
Bestellingen moeten daarna weer naar Shopify of ons fulfilmentsysteem worden teruggestuurd.
Ik ben vooral benieuwd naar ervaringen van ondernemers of developers die dit al hebben gebouwd of geïmplementeerd.
- Waar ging het bij jullie in de praktijk mis?
- Welke productgegevens slaan jullie centraal op?
- Hoe gaan jullie om met verschillende categorieën en verplichte attributen per marketplace?
- Gebruiken jullie webhooks, periodieke synchronisatie of een combinatie?
- Is een bestaande oplossing uiteindelijk voordeliger dan zelf ontwikkelen?
Ik zoek niet direct een specifiek softwarepakket, maar vooral praktijkervaringen en aandachtspunten voordat we hier veel tijd en geld in investeren.
De belangrijkste beslissing vooraf: wie is eigenaar van welk veld?
Iedereen roept "single source of truth", maar dat wordt vaak verkeerd begrepen. SSOT betekent níét dat alle data van één systeem komt. Het betekent dat elk veld precies één eigenaar/schrijver heeft. Zodra twee systemen hetzelfde veld mogen aanpassen (prijs in Shopify én op de marketplace) krijg je conflicten en eindeloze ping-pong.
- SKU als sleutel gebruiken. Grootste fout. SKU's veranderen (iemand hernoemt een variant) en elke marketplace verzint z'n eigen interne ID. De enige echt stabiele koppel-sleutel is de EAN/GTIN. Sla die op en match daar overal op. Wij hadden orders die niet te matchen waren omdat de SKU na het aanbieden was gewijzigd — via de EAN losten we dat op.
- Scoping/tenancy. Eén verkeerd gekoppeld ID (welke data hoort bij welke shop/koppeling) en je bent een dag kwijt. Bouw dat vanaf dag één goed.
- Het verschil tussen catalogus en offer niet snappen. Bij sommige marktplaatsen (Mirakl-based bv.) mag je niet zomaar producten via de API aanmaken — daar zijn speciale rechten of een goedkeuringstraject voor. Je "offer" (prijs/voorraad) plakt op een bestaand catalogus-product via de EAN.
- Snap je dat verschil niet, dan krijg je duplicaten of geblokkeerde uploads. Wij zijn deels overgestapt op: catalogus laten beheren door de operator/portal, en zelf alleen offers pushen. Veel minder gedoe.
- Geen idempotentie op orders. Zonder een idempotency-key maak je bij elke sync-run opnieuw dezelfde (concept-)order aan. Sla per externe order-ID op wat je al hebt aangemaakt.
- API-eigenaardigheden per marketplace. Velden die net anders heten, filters die genegeerd worden, statussen die je moet pollen. Reken op verrassingen per platform.
- Productdata inbound: periodieke bulk-sync (Shopify heeft bulk export). Webhooks zijn leuk als versneller, maar ze missen events — je hébt een periodieke reconciliation nodig als waarheid.
- Orders: kort interval (elke paar minuten) of webhooks — tijdgevoelig.
- Offers outbound: change-detection met een hash/fingerprint per offer, zodat je alleen wijzigingen pusht en niet elke run je hele catalogus. Dat scheelt enorm in rate limits.
fulfilment-flows, of als deze koppeling je kernproduct ís.
Dank voor je uitgebreide antwoord, super dit!
Je geeft aan dat jullie EAN/GTIN als stabiele sleutel gebruiken in plaats van SKU, wat heel logisch is voor losse producten. Maar hoe hebben jullie de structuur van productvarianten (zoals een kledingstuk in verschillende maten en kleuren) opgevangen in de centrale laag? Shopify werkt met een Parent-Child structuur (Product vs. Variant), maar sommige marktplaatsen (zoals Amazon of eBay) vereisen complexe 'variation themes', terwijl andere platformen elke variant gewoon als een los uniek product zien met een relatie. Hoe mappen jullie die hiërarchie zonder dat de data-architectuur vastloopt?
Jullie gebruiken webhooks als versneller en een periodieke sync als vangnet voor de voorraad. Toch zit er altijd een (kleine) vertraging tussen het moment dat een product op platform A wordt verkocht, en de voorraad op platform B is bijgewerkt. Hoe lossen jullie potentiële 'race conditions' of 'overselling' op bij producten met een lage voorraad die opeens hard lopen? Werken jullie bijvoorbeeld met een 'veiligheidsmarge' (buffer) in de centrale laag, of vangen jullie dit op een andere manier op?
Het cruciale stuk is dat je die variatie-assen expliciet en gestructureerd opslaat: de parent kent de assen, elk kind heeft z'n as-waarden (maat=M, kleur=blauw) plus z'n EAN/SKU/prijs/voorraad/variant-foto's. Dat is de abstractie waarmee je álle marktplaats-modellen kunt bedienen zonder dat je architectuur vastloopt.
De projectie doet je adapter per marktplaats
- Marktplaatsen met een variation theme (Amazon, eBay): je stuurt de parent als relatie-listing met de kinderen eronder, en je mapt jouw assen naar hun theme (maat+kleur → Amazons SizeColor). Let op: welke variation themes geldig zijn hangt bij Amazon af van het product type/categorie, dus die as-naar-theme-mapping hoort in je categorie-config (JSONB), niet globaal hardcoden.
- Marktplaatsen die elke variant als los product zien (bol, veel Mirakl-based, Marktplaats): je flattent en pusht elk kind als eigen offer op EAN. De parent-relatie negeer je, of je stuurt hooguit een group-id mee als ze soft-grouping ondersteunen.
Zo blijft je centrale model stabiel (parent 1—* variant, variant = EAN-keyed sellable) en verplaatst alle complexiteit naar de per-marketplace-adapter. Precies hetzelfde principe als eerder: abstraheer per marktplaats, forceer geen generiek model.
Twee valkuilen die ons tijd kostten: sommige platformen accepteren een parent alleen als je alle varianten in één keer aanbiedt (een kind los toevoegen aan een bestaande parent kan niet altijd), dus batch per parent. En een ontbrekende verplichte as-waarde laat de hele parent afketsen — valideer je variant-set compleet vóór je pusht.
Overselling, je verslaat de latency niet, dus je managet het risico
Met meerdere kanalen krijg je nooit nul-latency, perfecte voorraad. Er zit altijd propagatietijd tussen een verkoop op A en de update op B. Je doel is dus niet mechanisch "0 oversell" (dat kost je gestrande voorraad), maar het risico klein houden én snel reconciliëren. Wij stapelen daarvoor een paar dingen:
- Atomair, conditioneel afboeken in de centrale laag. Dit is de kern, belangrijker dan welke buffer dan ook. Nooit read-then-write, maar UPDATE ... SET stock = stock - 1 WHERE stock >= 1 (of een version-kolom / optimistic locking). De rowcount vertelt je of het lukte. Zo kunnen twee gelijktijdige orders niet allebei "nog 1" lezen.
- Direct fan-out op order-ingest. Zodra een order binnenkomt (webhook of korte poll), boek je centraal af en push je de nieuwe voorraad meteen naar álle andere kanalen. De snelheid van díe lus bepaalt je oversell, veel meer dan de buffer.
- Buffer/veiligheidsmarge, velocity-afhankelijk. Ja, we publiceren beschikbaar = werkelijk - buffer. Maar een vaste buffer is te bot: hardlopers met lage voorraad krijgen een grotere marge of we cappen de geadverteerde hoeveelheid (adverteer nooit 3 stuks over 5 kanalen als "15 verkoopbaar" — cap wat elk kanaal ziet).
- De-listen op drempel. Voor die schaarse hardlopers: raakt de centrale voorraad de buffer, dan zetten we hem proactief op 0 op de marktplaatsen. Liever even out-of-stock dan een oversell — een annulering telt op Amazon/bol zwaar mee in je seller-metrics/account health. Dat is de echte kostenpost, niet de gemiste verkoop.
- Reconciliation als vangnet. De periodieke sync blijft de bron van waarheid en corrigeert drift.
Kijk, dit is waarom ik deze vraag hier stelde. Wat een fantastisch antwoord, dankjewel! Je slaat de spijker op z'n kop met de EAN op variant-niveau als de enige echte sellable eenheid; dat houdt het centrale model inderdaad clean.
Die tip over conditioneel afboeken in de database in plaats van read-then-write is een absolute lifesaver om race conditions te voorkomen. Ook het inzicht dat een annulering wegens overselling je via account-metrics harder straft dan een proactieve de-listing is een heel scherpe business-nuance.
Dit bespaart ons weken aan trial-and-error. Heel erg bedankt!