11 min de leitura
shopify
erp
ecommerce
integrações
arquitectura

Sincronizar stock entre Shopify e ERP em tempo real

Como sincronizar stock entre Shopify e ERP com webhooks, filas, idempotência, reconciliação e regras claras sobre a fonte de verdade.

Duas pessoas caminham num corredor luminoso junto ao armazém, com papéis de stock na mão
  • O Shopify não deve ser tratado como ERP. Deve ser a montra transaccional, enquanto o ERP continua a ser a fonte de verdade para stock físico, compras, reservas e armazéns.
  • “Tempo real” em stock raramente significa imediato. Um alvo saudável é propagar alterações críticas em 2 a 10 segundos e reconciliar divergências em ciclos de 5 a 15 minutos.
  • Webhooks são obrigatórios, mas não chegam. Tens de assumir eventos duplicados, eventos fora de ordem, falhas temporárias e limites de API.
  • A arquitectura certa passa por fila, idempotência, mapeamento explícito de SKU e localizações, e um job de reconciliação independente.
  • A maior armadilha em produção é sincronizar quantidades absolutas sem perceber reservas, localizações e encomendas ainda não expedidas.

Primeiro decide quem manda no stock

A primeira decisão não é técnica. É operacional.

Numa integração entre Shopify e ERP, alguém tem de ser a fonte de verdade. Se não tomares esta decisão por escrito, vais acabar com duas equipas a corrigir stock manualmente em dois sistemas diferentes, e a integração passa a ser culpada por todos os desvios.

A minha opinião é simples: na maioria dos retalhistas e operações B2B com armazém, o ERP deve mandar no stock disponível. O Shopify deve receber disponibilidade para venda. Não deve tentar calcular stock real a partir de encomendas, devoluções, transferências e recepções de compra.

Há excepções. Uma loja pequena, com poucos SKUs e sem ERP maduro, pode deixar o Shopify como fonte principal durante algum tempo. Mas quando entram múltiplos armazéns, compras, lotes, reservas para marketplace, POS, devoluções e encomendas B2B, o Shopify deixa de ser o sítio certo para decidir disponibilidade.

A distinção mais importante é esta:

  • Stock físico: unidades existentes num armazém.
  • Stock reservado: unidades comprometidas com encomendas ainda não expedidas.
  • Stock disponível para venda: físico menos reservas, menos buffers, menos bloqueios de qualidade.
  • Stock publicado no Shopify: o número que o cliente vê ou que o checkout permite comprar.

Na prática, o Shopify só precisa do último valor. O ERP deve calcular o resto.

Se vendes 10 unidades de um SKU, tens 3 reservadas para uma encomenda wholesale, 2 em controlo de qualidade e queres manter buffer de segurança de 1 unidade, o Shopify não deve receber 10. Deve receber 4.

Parece básico, mas é aqui que muitas integrações falham. Sincronizam “stock on hand” em vez de “available to sell” e depois admiram-se com overselling.

O modelo de dados que evita dores

Antes de falar de webhooks e APIs, é preciso alinhar identificadores.

No Shopify, inventário não é apenas “produto com SKU”. O modelo relevante inclui Product Variant, Inventory Item, Location e Inventory Level. A documentação oficial está em https://shopify.dev/docs/api/admin-graphql e a parte de inventário merece leitura cuidadosa antes de escrever código.

O ERP, por outro lado, pode pensar em artigos, armazéns, lotes, unidades de medida e estados de stock. A ponte entre os dois mundos deve ser explícita.

No mínimo, precisas desta tabela mental:

  • SKU no ERP.
  • Variant ID no Shopify.
  • Inventory Item ID no Shopify.
  • Location ID no Shopify.
  • Armazém ou conjunto de armazéns no ERP.
  • Regra de publicação, por exemplo, “publicar soma de A1 e A2 com buffer de 2 unidades”.
  • Última quantidade publicada.
  • Última data de sincronização.
  • Estado da ligação, activo, suspenso, erro de mapeamento.

Não confies apenas no SKU como chave única. Em produção, SKUs duplicados acontecem. Variantes antigas ficam arquivadas. Produtos são recriados manualmente. Apps de merchandising alteram variantes. Migrações importam produtos com campos inconsistentes.

A regra que costumo aplicar é: SKU serve para descoberta e auditoria, não para identidade definitiva. A identidade definitiva no Shopify deve ser o Inventory Item ID e a Location ID. No ERP, deve ser o ID interno do artigo e do armazém, não apenas o código visível.

Também deves decidir como tratar bundles e kits. Se vendes um “pack de 3” no Shopify, mas o ERP só conhece a unidade individual, não há sincronização simples de 1 para 1. Precisas de uma regra de disponibilidade: o stock do pack é o mínimo inteiro possível a partir dos componentes. Se tens 10 camisolas e 4 bonés, e o pack leva 1 camisola e 1 boné, só podes vender 4 packs.

Esta lógica não deve estar espalhada por webhooks. Deve viver numa camada de disponibilidade, testável e auditável.

Arquitectura recomendada para quase todos os casos

Para sincronização em tempo quase real, a arquitectura que recomendo é esta:

  1. Shopify envia webhooks de encomendas, cancelamentos, devoluções e alterações de inventário.
  2. ERP envia eventos de stock quando há recepções, transferências, ajustes, reservas ou expedições.
  3. Uma camada de integração recebe eventos e valida assinatura.
  4. Os eventos entram numa fila.
  5. Workers processam eventos de forma idempotente.
  6. A camada de disponibilidade calcula o valor publicável.
  7. A integração actualiza o Shopify via Admin GraphQL API.
  8. Um job de reconciliação compara ERP e Shopify em intervalos regulares.

Não ponhas lógica pesada dentro do endpoint do webhook. O endpoint deve validar, persistir e responder depressa. Aponta para p99 abaixo de 500 ms no endpoint. O processamento pode demorar mais, mas deve acontecer fora do request original.

Uma stack simples e suficiente pode ser:

  • Node.js 22 LTS ou Python 3.12 para a integração.
  • PostgreSQL 16 para mapeamentos, idempotência e auditoria.
  • Redis com BullMQ, SQS, Pub/Sub ou uma fila equivalente para processamento assíncrono.
  • Workers separados por tipo de evento.
  • Observabilidade com logs estruturados, métricas e alertas.

Não precisas de microserviços para isto. Precisas de fronteiras claras. Um serviço de integração bem escrito é melhor do que cinco serviços pequenos sem idempotência.

Um padrão de idempotência simples em PostgreSQL ajuda muito:

CREATE TABLE integration_events (
  source text NOT NULL,
  event_id text NOT NULL,
  topic text NOT NULL,
  received_at timestamptz NOT NULL DEFAULT now(),
  processed_at timestamptz,
  status text NOT NULL DEFAULT 'pending',
  payload jsonb NOT NULL,
  PRIMARY KEY (source, event_id)
);

Quando recebes um webhook, tentas inserir. Se já existir, não processas outra vez. Isto evita duplicar ajustes quando o Shopify ou o ERP repetem eventos. Webhooks devem ser tratados como “pelo menos uma vez”, não como “exactamente uma vez”.

Para Shopify, deves validar HMAC nos webhooks. A documentação oficial está em https://shopify.dev/docs/apps/build/webhooks. Sem validação, qualquer pessoa que descubra o endpoint pode simular uma alteração de stock.

Webhooks, API GraphQL e limites que interessam

O Shopify tem webhooks úteis para inventário e encomendas, mas não deves esperar uma narrativa perfeita dos acontecimentos. Eventos podem chegar fora de ordem. Alguns podem falhar temporariamente. O teu endpoint pode estar em baixo. Uma app pode ser removida. Uma subscrição pode mudar.

Os tópicos exactos variam com o tipo de integração, mas normalmente olhas para:

  • Encomendas criadas, pagas, canceladas e actualizadas.
  • Fulfillments criados ou actualizados.
  • Refunds e devoluções.
  • Inventory levels actualizados.
  • Produtos e variantes actualizados, para apanhar alterações de SKU e tracking.

Para escrever stock no Shopify, usa a Admin GraphQL API. A REST Admin API foi marcada como legacy para novas apps públicas a partir de 2024, por isso não começaria um projecto novo assente em REST se a operação for crescer.

Fixa uma versão da API, por exemplo 2025-10, e agenda revisões trimestrais. O Shopify versiona a API por trimestre e remove versões antigas. Não deixes isto entregue ao acaso.

Nos limites, há dois pontos práticos:

  • A GraphQL Admin API usa um modelo por custo de query, não apenas número de requests.
  • Em lojas standard, o bucket e a taxa de reposição podem limitar updates em massa. Consulta https://shopify.dev/docs/api/usage/rate-limits antes de desenhar jobs agressivos.

Isto muda decisões. Se tens 50 000 SKUs e recebes uma importação completa do ERP a cada 5 minutos, não podes simplesmente disparar 50 000 updates para o Shopify sem controlo. Tens de comparar diferenças e só publicar alterações reais.

Regra prática: nunca envies para o Shopify uma quantidade igual à última quantidade publicada. Parece detalhe, mas reduz carga, ruído, custo e risco de rate limit.

Outra decisão importante é escolher entre ajustar e definir quantidades absolutas. Para sincronização a partir de ERP, prefiro definir quantidade absoluta disponível por location, calculada pelo ERP. Ajustes incrementais são perigosos quando há eventos perdidos ou fora de ordem. Se o evento “menos 2” chega duas vezes, estás errado. Se defines “agora são 18”, a operação é mais fácil de reconciliar.

Atenção: isto não significa ignorar concorrência. Se Shopify também altera stock por vendas, devoluções ou apps, tens de perceber se essas alterações devem voltar ao ERP ou se o ERP reescreve a disponibilidade. Mais uma vez, fonte de verdade.

Comparação das abordagens possíveis

Há três caminhos típicos. Nenhum é universal.

  1. Polling simples

O polling lê stock do ERP ou do Shopify de X em X minutos e actualiza o outro lado.

Prós:

  • Mais fácil de implementar.
  • Não depende de webhooks bem configurados.
  • Útil como primeira versão para catálogos pequenos.

Contras:

  • Latência pior. Se corres a cada 10 minutos, podes vender stock que já não existe.
  • Carga desnecessária.
  • Fraco para picos, campanhas e flash sales.
  • Tende a esconder erros até ser tarde.

Eu só usaria polling como mecanismo principal em operações pequenas, com baixo volume e baixa criticidade. Mesmo assim, manteria reconciliação e logs.

  1. Webhooks com fila

Esta é a opção equilibrada para a maioria das operações.

Prós:

  • Latência baixa. 2 a 10 segundos é realista quando ERP e Shopify colaboram.
  • Melhor uso de API.
  • Recupera melhor de falhas temporárias.
  • Permite retries, dead letter queue e auditoria.

Contras:

  • Mais engenharia.
  • Exige idempotência.
  • Exige monitorização.
  • Obriga a pensar no modelo de dados.

Para uma loja séria, esta é a base certa.

  1. Middleware iPaaS ou low-code

Ferramentas como Celigo, Make, Zapier, n8n ou conectores específicos de ERP podem acelerar bastante.

Prós:

  • Arranque rápido.
  • Menos código próprio.
  • Boa opção quando o fluxo é simples.
  • Pode ser suficiente para equipas sem capacidade técnica interna.

Contras:

  • Lógica de stock complexa fica difícil de manter.
  • Observabilidade limitada ou cara.
  • Reprocessamento e idempotência nem sempre são claros.
  • Custos sobem com volume.
  • Debug em casos limite pode ser penoso.

A minha posição: iPaaS é bom para validar fluxo e para integrações administrativas. Para disponibilidade de stock crítica, com múltiplos armazéns e impacto directo no checkout, prefiro uma integração própria ou pelo menos uma camada própria de decisão.

Armadilhas comuns em produção

A primeira armadilha é assumir que “stock” significa a mesma coisa nos dois sistemas. Não significa. O ERP pode mostrar stock físico. O Shopify precisa de stock disponível para vender. O financeiro pode querer stock valorizado. A operação pode falar de stock expedível. São números diferentes.

A segunda é esquecer localizações. O Shopify trabalha com inventory levels por location. Se publicas tudo numa location genérica, perdes a capacidade de representar armazéns, lojas físicas e regras de fulfillment. Se tens lojas com POS, isto fica ainda mais sensível.

A terceira é tratar webhooks como sequência ordenada. Não são uma fila perfeita de eventos de negócio. São notificações. Tens de conseguir recalcular estado actual quando recebes um evento suspeito. Por isso é que a reconciliação é obrigatória.

A quarta é não lidar com devoluções e cancelamentos. Uma encomenda cancelada pode ou não devolver stock, dependendo do estado operacional. Uma devolução pode chegar danificada e não voltar a ficar disponível. Um refund financeiro não é o mesmo que uma recepção física em armazém.

A quinta é ignorar buffers. Se vendes em Shopify, marketplace, wholesale e loja física, publicar 100 por cento do disponível no Shopify é pedir overselling. Um buffer fixo de 1 ou 2 unidades pode chegar em catálogos pequenos. Em operações maiores, o buffer deve variar por velocidade de venda, lead time e risco de ruptura.

A sexta é não ter ecrã de auditoria. Quando alguém pergunta “porque é que este SKU ficou com 0 no Shopify?”, não podes ir procurar em logs crus durante uma hora. Precisas de histórico por SKU: evento recebido, cálculo feito, quantidade anterior, quantidade nova, resposta da API e erro se existiu.

Métricas mínimas que eu colocaria desde o primeiro dia:

  • Latência entre evento ERP e update Shopify, p50, p95 e p99.
  • Número de eventos pendentes na fila.
  • Número de falhas por tópico.
  • Número de SKUs sem mapeamento.
  • Diferenças encontradas na reconciliação.
  • Rate limit consumido na API Shopify.
  • Updates ignorados por não haver alteração de quantidade.

Sem isto, a integração parece funcionar até ao primeiro pico de vendas.

Reconciliação é obrigatória, não é plano B

Mesmo com webhooks, filas e boa engenharia, vais ter divergências. A pergunta não é “se”. É “quando” e “como descobres”.

Um job de reconciliação deve correr em background e comparar a disponibilidade calculada pelo ERP com o que está publicado no Shopify. Para catálogos pequenos, pode correr a cada 5 ou 15 minutos. Para catálogos grandes, segmenta por SKUs alterados recentemente, produtos activos e produtos de alta rotação. Um full scan diário também é saudável.

O processo deve ser conservador:

  1. Ler disponibilidade do ERP.
  2. Ler inventory levels relevantes do Shopify.
  3. Comparar com tolerância zero para produtos simples.
  4. Gerar correcção apenas quando a diferença é real.
  5. Guardar auditoria.
  6. Alertar se a diferença for recorrente ou acima de um limite.

Em campanhas, eu reduziria o intervalo de reconciliação para SKUs críticos. Se tens produtos com 300 pedidos por minuto durante uma promoção, esperar uma hora para descobrir desvio é tarde demais.

Também deves ter modo degradado. Se o ERP estiver indisponível durante 20 minutos, o que fazes? Continuas a vender com último stock conhecido? Metes produtos críticos a 0? Aumentas buffer? Isto é decisão de negócio, mas tem de estar implementada antes da falha.

Para muitos negócios, a regra certa é: se o ERP não responde e o stock é baixo, reduzir disponibilidade no Shopify. É melhor perder algumas vendas do que vender o que não consegues expedir.

Conclusão

Sincronizar stock entre Shopify e ERP em tempo real não é ligar dois endpoints. É definir fonte de verdade, modelar disponibilidade, processar eventos com idempotência e reconciliar continuamente.

A integração boa é a que falha de forma visível, recupera sem intervenção manual e explica cada alteração de stock.

Se estás a enfrentar um problema parecido, marca uma conversa em https://impact-origin.com/agendamento.

Impact OriginGostaste deste artigo?Na Impact Origin ajudamos fundadores e equipas a construir e escalar software à medida, do MVP da startup ao próximo passo. Se tens um projeto em mente, vamos falar.