Como desduplicar acionadores de webhook „orders/create

Oi a todos,

Temos um workflow usando o trigger de webhook orders/create do nó Shopify. De mais de 1.000 pedidos processados, tivemos dois casos em que o webhook foi acionado duas vezes quase instantaneamente para o mesmo pedido.

Eu abri isso com o suporte da Shopify e eles confirmaram:

  • A Shopify usa um modelo de “entrega pelo menos uma vez”. Se o sistema deles não receber uma resposta HTTP 200 rápida o suficiente, ele tenta novamente a entrega, o que pode fazer com que o mesmo pedido acione o webhook duas vezes.
  • Este é o comportamento esperado do lado da Shopify e não pode ser desativado.
  • Eles descartaram registros de webhook duplicados (temos apenas uma assinatura orders/create ativa).
  • A recomendação deles foi adicionar lógica de deduplicação dentro do workflow do n8n — verificando o ID do pedido ou o header X-Shopify-Webhook-Id antes de o processamento continuar.

O que estou procurando:

  • Qual é a melhor forma de construir esse tipo de deduplicação diretamente no n8n (por exemplo, usando um data store, uma variável estática/global, Redis ou outro nó) para pularmos o processamento se já tivermos visto um determinado ID de pedido ou ID de webhook?
  • Existe um padrão leve que evite a necessidade de um banco de dados externo, considerando que isso acontece raramente (2 em 1.000+ pedidos)?

Qualquer orientação ou exemplos de workflows seria muito apreciado. Obrigado!

Oi @Hadi_Farhat Bem-vindo!
O caminho integrado é um nó Remove Duplicates logo após o Shopify Trigger: Operation “Remove Items Processed in Previous Executions”, Keep Items Where “Value Is New”, Value to Dedupe On {{ $json.id }}. n8n mantém os ids vistos em seu próprio banco de dados, 10.000 por padrão em History Size, então nada externo está envolvido e uma reentrega é descartada antes de suas etapas de pedido.
O único buraco é a forma que você já encontrou. Essa verificação lê a lista armazenada e a escreve de volta sem lock, então duas entregas chegando no mesmo instante podem ambas passar por ela. Cubra esse caso no efeito colateral em vez do portão: dê à escrita que não deve repetir a identificação do pedido como sua chave, uma coluna única com INSERT ON CONFLICT DO NOTHING para um banco de dados, ou o cabeçalho de idempotência da API onde ele existe.

A sugestão de Anshul é a primeira camada leve correta. Para um fluxo de pedidos, eu trataria a porta duplicada e a ação irreversível como dois controles separados:

  1. Coloque Remover Duplicatas imediatamente após o gatilho, usando o ID do pedido Shopify quando a saída do gatilho não expõe o cabeçalho do webhook.
  2. Antes da primeira ação que nunca deve acontecer duas vezes, crie uma chave determinística como shop-domain:orders-create:order-id:action-name e reivindique-a com um insert ou upsert atômico no sistema que possui o efeito colateral. Se a chave já existir, finalize essa execução com sucesso. Se o destino suportar uma chave de idempotência nativa, envie a mesma chave para lá também.

Essa segunda camada é importante porque duas entregas quase simultâneas podem passar por um controle duplicado de leitura-depois-escrita. O efeito colateral em si precisa rejeitar a segunda tentativa.

Eu testaria quatro casos antes de publicar a alteração: uma entrega normal, o mesmo payload duas vezes em sequência, o mesmo payload duas vezes quase simultaneamente e uma falha depois da proteção, mas antes do efeito colateral. Registre o ID do pedido, ID do webhook ou evento quando disponível, chave de idempotência e status final, mas não dados do cliente.

Shopify também recomenda retornar 200 OK rapidamente e mover o processamento lento para fora do caminho da solicitação, porque um reconhecimento lento pode desencadear outra entrega.

Se você prefere que alguém implemente e teste essa proteção em um caminho existente, ofereço um reparo de fluxo de escopo fixo aqui: https://contra.com/s/WOggXWks-i-will-diagnose-and-repair-one-broken-business-automation

Os webhooks do Shopify são at-least-once, então o workflow deve ser idempotente tanto nos efeitos colaterais quanto no gatilho. Mantenha Remove Duplicates como o primeiro filtro usando o ID do pedido, depois reivindique uma chave determinística como shop-domain:orders-create:order-id:action-name com um insert/upsert único atômico no sistema que executa a ação irreversível. Se a chave já existir, finalize essa execução com sucesso. Use a chave de idempotência nativa da API de destino quando disponível e retorne 200 OK rapidamente antes do processamento lento.

Aqui está uma versão concreta do conselho „tornar a escrita atômica", já que essa é a parte que realmente impede a segunda execução:

  1. Chave de deduplicação: para orders/create, o ID do pedido ({{ $json.id }}) funciona bem, porque um pedido é criado apenas uma vez. Para outros tópicos, use o cabeçalho X-Shopify-Event-Id. Ele permanece igual nas tentativas da Shopify do mesmo evento.

  2. Reivindicação atômica: se você tem algum Postgres (o nível gratuito do Supabase funciona), coloque isso logo após o gatilho: INSERT INTO processed_orders (order_id) VALUES ({{ $json.id }}) ON CONFLICT (order_id) DO NOTHING RETURNING order_id; depois um nó IF que continua apenas quando uma linha é retornada. Duas execuções que chegam ao mesmo momento não podem obter uma linha de volta ao mesmo tempo. Sem nenhum banco de dados, torne a ação final em si mesma idempotente: Google Sheets „Adicionar ou Atualizar Linha" correspondente ao ID do pedido, um upsert de Airtable/CRM, ou adicionando uma tag „processado

Eu colocaria a chave de deduplicação na entrega em vez da ordem. O Shopify coloca um cabeçalho X-Shopify-Webhook-Id em cada entrega, e sua própria documentação de webhook diz que esse é o que deve ser usado para ignorar entregas duplicadas. Que é exatamente seu caso, o mesmo evento enviado novamente porque o 200 retornou lentamente. Li essa página hoje, 2026-09-22.

ID do pedido também funciona, mas também elimina um segundo orders/create para o mesmo pedido que era na verdade um evento diferente. Você estaria descartando um real para pegar uma tentativa.

O cenário que me preocupa é 2026-10-16, quando Shopify bloqueia a versão da API 2025-10. Se sua chave for algum campo no corpo e esse campo mudar de forma com a versão, a chave pode ficar nula ou simplesmente parecer diferente, sua verificação de visto para de corresponder, e toda tentativa daquela manhã é tratada como um novo pedido. O cabeçalho não se move quando a versão muda.

Coisas que não verifiquei: por quanto tempo o mesmo webhook id continua aparecendo em toda a janela de tentativa, então eu manteria as chaves vistas por mais tempo do que parece necessário. E não executei isso em uma loja fazendo 1000 pedidos, então a parte do cabeçalho é da documentação, não dos meus próprios registros.

Um aviso rápido a adicionar na verificação de estado externo:

Se você armazenar a chave de reclamação separadamente da ação downstream, tratando “chave existe = saída bem-sucedida” cria uma falha silenciosa desagradável. Se o workflow falhar após reivindicar a chave mas antes do pedido ser processado, a retentativa do Shopify chega, atinge a chave existente e descarta o pedido permanentemente.

Em produção, você precisa dividir o estado em pending vs completed:

  • Ignore apenas se a ação downstream for verificada como concluída.
  • Se uma tentativa ficar presa em pending, não a descarte — reconcilie com a API de destino ou recaia para idempotência nativa.
  • Evite colocar Remove Duplicates logo no gatilho se isso cegar o workflow para este caminho de recuperação.

Escrevi um detalhamento completo e 5 casos de teste para capturar exatamente este edge case aqui: Shopify + n8n: 5 tests for duplicate webhooks and failed-run recovery - Tips & Tricks - n8n Community

Eu também enfrentei o mesmo duplo disparo ocasional em webhooks do Shopify. O que funcionou para mim foi tratar o ID do pedido como uma chave de idempotência: o primeiro nó escreve o ID do pedido em um Data Store (ou planilha) com uma verificação do tipo set-if-not-exists, e o resto do fluxo de trabalho continua apenas quando a escrita é nova. Tentativas e as raras entregas duplicadas do Shopify se tornam operações sem efeito em vez de cumprimento duplo. Também vale a pena confirmar que você não está inscrito duas vezes em orders/create no mesmo aplicativo.