externalId. Reenviar
o mesmo payload não cria duplicatas e não consome writes se nada mudou.
A unique key
catalogIdé resolvido implicitamente pela API key (cada chave pertence a 1 catálogo)externalSystemidentifica a origem (ex:"shopify","totvs","meu-erp")externalIdé o ID do seu sistema (SKU, ID do Shopify, ID de produto interno)
ExternalProductSync e mapeia para o voopId interno.
Você nunca precisa saber o voopId — basta seu externalId.
O contentHash
Cada upsert calcula umcontentHash SHA-256 sobre os campos canônicos do payload
(SKU, nome, preço, dimensões, mídia, etc., normalizados).
Por que isso importa
Re-sync seguro
Pode rodar
bulk-upsert do catálogo inteiro toda noite sem custo extra. A
Voop processa apenas o que mudou.Recuperação de falha
Se o cliente cair durante um sync, basta rodar de novo. Itens já enviados retornam
unchanged; novos são processados.Webhook reentrante
Se você recebe webhook do Shopify e a Voop não responde a tempo, o Shopify retenta —
sem efeito colateral.
Mídia preservada
unchanged não re-baixa as imagens. Só URLs novas/alteradas entram na fila de ingest.Mídia: dedup duplo
Imagens são deduplicadas em duas dimensões:-
Por URL normalizada (
CatalogMediaSource.sourceUrlHash):- Mesma URL usada em 100 produtos = 1 download, 1 asset, 100 referências
- Tracking params (
utm_*,fbclid,gclid) são removidos antes do hash - Query params são ordenados —
?a=1&b=2e?b=2&a=1são iguais
-
Por ETag (HTTP) (
CatalogMediaSource.etag):- Antes de re-baixar, a Voop faz
HEADcomIf-None-Match - 304 → skip (zero download, zero storage cost)
- 200 → re-baixa só se conteúdo mudou
- Antes de re-baixar, a Voop faz
Quando NÃO é idempotente
Os endpoints abaixo modificam estado independente do payload:POST /imports— sempre cria um novo jobPOST /webhooks— sempre cria um novo webhook (com novo secret)DELETE /items/:externalSystem/:externalId— uma deleção é ação destrutiva