API

Fluxo n8n: API, WhatsApp, Log (com dedupe e tratamento de erro)

O cenário (realista)

Você quer receber um evento via API (Webhook), enviar uma mensagem no WhatsApp via Notifish e registrar o resultado (sucesso/erro) em log.

Esse fluxo é útil para:

  • alertas operacionais
  • notificações de deploy
  • falhas de integração
  • notificações de sistema para grupos

Pré-requisitos

  1. n8n rodando e acessível
  2. endpoint do Notifish (o seu padrão):
    POST https://meu-dominio.notifish.com/api/v2/{INSTANCE_UUID}/whatsapp/message/groups
    com header Authorization: Bearer ...
  3. um lugar para log
    Vou te dar 2 opções:
  • A) log em arquivo via HTTP (endpoint seu) — mais profissional
  • B) log no próprio n8n (Execution data) + uma planilha/banco (se preferir)

Aqui vou usar a opção A (mais “produção”).


Parte 1 — Contrato da API (Webhook)

Você vai expor um endpoint no n8n, por exemplo:

POST https://SEU_N8N/webhook/notify

Payload recomendado (simples e suficiente):

{
  "title": "API 5xx alto",
  "message": "Erros 5xx acima do normal em /api/orders",
  "url": "https://seu-dashboard",
  "identifier": "prod:api:5xx",
  "delayMessage": 0
}

Repare que já mandamos identifier. Isso permite deduplicar e evitar spam.


Parte 2 — Dedupe (anti-spam) usando Redis (opcional, mas recomendado)

Se você tiver Redis (muito comum), dá para usar um passo “check” antes do envio.

  • chave: dedupe:{identifier}
  • TTL: 300s (5 minutos) por exemplo
    Se existir, você não envia.

Se você não tiver Redis, dá para deduplicar com “Data Store” do n8n ou com um endpoint seu.

Vou te passar o fluxo com Data Store do n8n (não depende de infra extra).


Parte 3 — Enviar WhatsApp via Notifish

O Notifish recebe:

{
  "message": "...",
  "identifier": "...",
  "link": true,
  "typing": "composing",
  "delayMessage": 1200
}

A gente vai montar isso no n8n.


Parte 4 — Registrar Log

Depois do envio (sucesso ou falha), registramos em um endpoint de log. Exemplo:

POST https://seu-sistema.com/api/logs/notifish

Log recomendado:

{
  "identifier": "prod:api:5xx",
  "status": "success",
  "http_status": 200,
  "provider": "notifish",
  "payload": { ... },
  "response": { ... },
  "created_at": "2026-01-22T00:00:00-03:00"
}

NODES DO WORKFLOW (passo a passo)

1) Webhook (Trigger)

  • Method: POST
  • Path: notify
  • Response: JSON (200)

2) Function (Normalize)

Cria um formato consistente e monta a mensagem final do WhatsApp:

  • título + mensagem + url
  • garante identifier
  • define delayMessage

3) Data Store (Get)

Busca identifier na datastore (dedupe).

4) IF (Já enviou?)

Se já existe registro recente, responde 200 “ignored” e não envia.

5) HTTP Request (Notifish)

POST no endpoint do Notifish com Bearer Token.

6) Data Store (Set)

Salva identifier com TTL (ex.: 300s) para dedupe.

7) HTTP Request (Log Success)

Envia log para seu endpoint.

8) Error Trigger / Catch (Log Error)

Se Notifish falhar, manda log com status error.


JSON do workflow para importar no n8n

Abaixo um workflow base. Você só precisa trocar:

  • NOTIFISH_INSTANCE_UUID
  • NOTIFISH_TOKEN
  • LOG_ENDPOINT_URL

Import: n8n → Workflows → Import from file/clipboard

{
  "name": "API → Notifish WhatsApp → Log (com dedupe)",
  "nodes": [
    {
      "parameters": {
        "path": "notify",
        "httpMethod": "POST",
        "responseMode": "lastNode",
        "options": {}
      },
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [260, 300]
    },
    {
      "parameters": {
        "functionCode": "const body = $json;\n\nconst title = body.title || 'Notificação';\nconst msg = body.message || '';\nconst url = body.url || '';\n\nconst identifier = body.identifier || `generic:${Date.now()}`;\nconst delayMessage = body.delayMessage ?? 0;\n\nconst whatsappMessage = `*${title}*\\n\\n${msg}${url ? `\\n\\n${url}` : ''}`;\n\nreturn [{\n  identifier,\n  delayMessage,\n  notifishPayload: {\n    message: whatsappMessage,\n    identifier,\n    link: true,\n    typing: 'composing',\n    delayMessage\n  },\n  original: body\n}];"
      },
      "name": "Normalize",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [520, 300]
    },
    {
      "parameters": {
        "operation": "get",
        "key": "={{$json.identifier}}"
      },
      "name": "Dedupe Get",
      "type": "n8n-nodes-base.dataStore",
      "typeVersion": 1,
      "position": [760, 300]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.value !== undefined && $json.value !== null}}",
              "value2": true
            }
          ]
        }
      },
      "name": "Já enviou?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [980, 300]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\"status\":\"ignored\",\"reason\":\"dedupe\",\"identifier\":\"{{$json.identifier}}\"}",
        "options": {}
      },
      "name": "Return Ignored",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [1210, 200]
    },
    {
      "parameters": {
        "url": "https://meu-dominio.notifish.com/api/v2/NOTIFISH_INSTANCE_UUID/whatsapp/message/groups",
        "method": "POST",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer NOTIFISH_TOKEN"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{$json.notifishPayload}}",
        "options": {
          "timeout": 8000
        }
      },
      "name": "Send Notifish",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [1210, 380]
    },
    {
      "parameters": {
        "operation": "set",
        "key": "={{$json.identifier}}",
        "value": "={{JSON.stringify({sentAt: new Date().toISOString()})}}"
      },
      "name": "Dedupe Set",
      "type": "n8n-nodes-base.dataStore",
      "typeVersion": 1,
      "position": [1450, 380]
    },
    {
      "parameters": {
        "url": "LOG_ENDPOINT_URL",
        "method": "POST",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ {\n  identifier: $json.identifier,\n  status: 'success',\n  provider: 'notifish',\n  response: $json,\n  created_at: new Date().toISOString()\n} }}"
      },
      "name": "Log Success",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [1690, 380]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\"status\":\"sent\",\"identifier\":\"{{$json.identifier}}\"}",
        "options": {}
      },
      "name": "Return OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [1910, 380]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Normalize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize": {
      "main": [
        [
          {
            "node": "Dedupe Get",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedupe Get": {
      "main": [
        [
          {
            "node": "Já enviou?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Já enviou?": {
      "main": [
        [
          {
            "node": "Return Ignored",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Notifish",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Notifish": {
      "main": [
        [
          {
            "node": "Dedupe Set",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedupe Set": {
      "main": [
        [
          {
            "node": "Log Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Success": {
      "main": [
        [
          {
            "node": "Return OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false
}

Observações importantes (produção)

1) Dedupe com Data Store não tem TTL nativo

Ele salva “para sempre” se você não limpar. Em produção, o ideal é:

  • usar Redis para TTL real, ou
  • incluir timestamp e limpar por job diário

Se você quiser, eu te mando a versão com Redis (TTL real).

2) Log endpoint

Se você não tiver endpoint pronto, você pode logar em:

ou até em um arquivo via API sua

PostgreSQL/MySQL via node de banco

Google Sheets (rápido)

Leonardo

Recent Posts

Como monitorar aplicação e servidor pelo WhatsApp (logs, erros e alertas)

WhatsApp é um canal excelente para alerta porque ele tem uma característica que e-mail e…

22 horas ago

AMP: o que é, para que serve, ainda vale a pena usar?

Durante alguns anos, o AMP foi tratado como solução quase obrigatória para quem queria desempenho…

1 dia ago

O que é Lighthouse e como usar para melhorar seu site

Performance, acessibilidade e boas práticas deixaram de ser “detalhes técnicos” e passaram a impactar diretamente…

2 dias ago

N8N: o que é, como funciona e quando faz sentido usar

Automação deixou de ser algo exclusivo de grandes sistemas. Hoje, boa parte das aplicações depende…

4 dias ago

Quando o WhatsApp vira canal operacional (e não só meio de envio)

Durante muito tempo, o WhatsApp foi visto apenas como um canal de comunicação direta: mensagens,…

5 dias ago

Como fazer o primeiro disparo na Evolution API

Depois de instalar a Evolution API (via Orion), muita gente trava no mesmo ponto: “ok,…

6 dias ago