Alerta de dólar no celular com n8n

No tutorial n8n do zero com Docker: instale e receba a cotação do dólar no e-mail, o n8n buscava a cotação e mandava um e-mail todos os dias. Agora vamos dar um passo além: o n8n vai conferir o dólar a cada hora e só vai avisar quando ele passar de um limite que você escolher. O aviso chega como notificação no celular, pelo ntfy, sem cadastro e sem chave.

É a primeira vez nesta série que o workflow toma uma decisão: com o nó If, ele segue por um caminho quando a condição é verdadeira e por outro quando é falsa. Você também vai ver por que é importante converter texto em número antes de comparar valores.

O que você vai construir

Este é o workflow final no editor do n8n:

Editor do n8n com o workflow Alerta de dólar no celular: Schedule Trigger, HTTP Request, Edit Fields e If; a saída true do If liga no HTTP Request1 e a saída false fica livre
O workflow final “Alerta de dólar no celular” no editor do n8n (versão 2.40.7).
  • Schedule Trigger: roda o workflow a cada hora.
  • HTTP Request: busca a cotação do dólar na AwesomeAPI.
  • Edit Fields: transforma a cotação e a variação em números e define o limite.
  • If: pergunta se o dólar passou do limite.
  • HTTP Request1: manda a notificação para o ntfy, só quando a resposta é sim (true).

Versão usada: n8n 2.40.7. Os nomes de botões e campos aparecem em inglês, exatamente como na interface. O tópico do ntfy usado aqui, alerta-dolar-35fadd3dde4ae2cc, é só um exemplo: crie o seu, como explicado no Nó 5.

Pré-requisitos


Montando o workflow, nó por nó

Crie um workflow novo e dê a ele o nome Alerta de dólar no celular. Depois, adicione os nós abaixo, nesta ordem, sempre pelo botão + que aparece à direita do nó anterior.

Nó 1: Schedule Trigger (a cada hora)

Clique em Add first step, escolha On a schedule e preencha:

Tela do nó Schedule Trigger com Trigger Interval Hours, Hours Between Triggers 1 e Trigger at Minute 0; à direita, a saída com timestamp, dia da semana e Timezone America/Sao_Paulo (UTC-03:00)
Schedule Trigger configurado para rodar a cada hora, no minuto 0.
CampoValorPor que assim
Trigger IntervalHoursA repetição é contada em horas.
Hours Between Triggers1Roda de hora em hora.
Trigger at Minute0Sempre na hora cheia: 9h00, 10h00, 11h00…

O aviso no topo do nó lembra que o agendamento só vale depois que o workflow for publicado; para testar, basta clicar em Execute workflow no canvas. Ao clicar em Execute step, a saída mostra a data e a hora da execução e o fuso usado, America/Sao_Paulo (UTC-03:00).

Nó 2: HTTP Request (buscar a cotação)

Usamos a mesma API do primeiro tutorial, a AwesomeAPI, gratuita. Esta é a URL:

https://economia.awesomeapi.com.br/json/last/USD-BRL
Tela do nó HTTP Request com Method GET e a URL da AwesomeAPI; à direita, a resposta com o objeto USDBRL contendo code, codein, name, high, low, varBid, pctChange, bid, ask, timestamp e create_date
HTTP Request buscando a cotação. À direita (OUTPUT), o objeto USDBRL.
CampoValorPor que assim
MethodGETVamos apenas consultar dados.
URLa URL do bloco acimaÚltima cotação do par dólar/real (USD-BRL).
AuthenticationNoneA API é pública.
Send Query Parameters / Headers / BodydesligadosNão são necessários.

A resposta vem dentro de um objeto chamado USDBRL. Os campos que vamos usar são bid (a cotação de compra, "5.1866" no teste), pctChange (a variação no dia, em porcentagem, "-0.096306") e create_date (quando a cotação foi registrada, 2026-09-25 18:30:14). Repare nas aspas: bid e pctChange chegam como texto, não como número. Isso faz diferença no nó If.

Um detalhe do teste: ele foi feito na madrugada de domingo, 27/09, e a cotação era a de sexta-feira, porque o mercado de câmbio não funciona no fim de semana.

Nó 3: Edit Fields (converter texto em número)

Clique no + à direita do HTTP Request e adicione um nó Edit Fields (Set). Em Mode, deixe Manual Mapping e crie quatro campos com Add Field. O tipo de cada campo é escolhido na lista abaixo do nome:

Tela do nó Edit Fields com os campos dolar Number, variacao Number, limite Number 5.5 e atualizado String; à direita, a saída com dolar 5.1866, variacao -0.096306, limite 5.5 e atualizado 2026-09-25 18:30:14
Edit Fields criando os campos do alerta. À direita (OUTPUT), os números já sem aspas.
CampoValorPor que assim
dolar (Number){{ $json.USDBRL.bid }}A cotação, convertida em número pelo próprio tipo do campo.
variacao (Number){{ $json.USDBRL.pctChange }}A variação do dia, também como número.
limite (Number)5.5O valor que dispara o alerta. Troque pelo seu. Use ponto, e não vírgula, nas casas decimais.
atualizado (String){{ $json.USDBRL.create_date }}A data da cotação, para mostrar na mensagem.
Include Other Input FieldsdesligadoA saída fica só com esses quatro campos.

Clique em Execute step. Na saída, dolar aparece como 5.1866, sem aspas, e variacao como -0.096306. Deixar o limite num campo próprio, logo no começo, facilita a manutenção: para mudar o valor, você mexe num lugar só.

Nó 4: If (passou do limite?)

Clique no + à direita do Edit Fields e adicione um nó If. Ele tem duas saídas: true, quando a condição é verdadeira, e false, quando não é.

Tela do nó If com a condição $json.dolar is greater than $json.limite e Convert types where required desligado; à direita, a aba False Branch com 1 item: dolar 5.1866 e limite 5.5
O nó If. Com o limite em 5.5, o item saiu pela False Branch.
CampoValorPor que assim
Conditions → valor 1{{ $json.dolar }}A cotação atual.
Conditions → operadorNumber → is greater thanCompara como número: “é maior que”.
Conditions → valor 2{{ $json.limite }}O limite definido no nó anterior.
Convert types where requireddesligadoOs campos já são números. Deixar desligado faz o n8n acusar se algum texto chegar por engano.

Para escolher o operador, clique na lista ao lado do primeiro valor, passe o mouse em Number e escolha is greater than. Clique em Execute step: com o dólar em 5,1866 e o limite em 5,5, a condição é falsa, e o item aparece na aba False Branch (1 item).

Nó 5: HTTP Request1 (mandar o alerta para o ntfy)

O ntfy funciona com tópicos: quem está inscrito num tópico recebe tudo o que é enviado para ele. Qualquer pessoa que souber o nome do tópico pode ler as mensagens, por isso use um nome longo e aleatório. O tópico deste tutorial, alerta-dolar-35fadd3dde4ae2cc, é só um exemplo e, como está publicado aqui, deixou de ser secreto: crie o seu, trocando os caracteres depois de “alerta-dolar-” por outros quaisquer (letras sem acento, números e hífens).

No nó If, clique no + da saída true e adicione outro HTTP Request. Como já existe um nó com esse nome, o n8n o chama de HTTP Request1. Esta é a URL, com o tópico no final:

https://ntfy.sh/alerta-dolar-35fadd3dde4ae2cc

E este é o texto da mensagem, que vai no Body:

O dólar está em R$ {{ $json.dolar.toFixed(2) }}, acima do seu limite de R$ {{ $json.limite.toFixed(2) }}. Variação no dia: {{ $json.variacao.toFixed(2) }}%. Cotação de {{ $json.atualizado }}.
Tela do nó HTTP Request1 com Method POST, URL do ntfy com o tópico de exemplo, cabeçalho Title Alerta de dolar, Body Content Type Raw, Content Type text/plain e o texto do alerta; à direita, a resposta do ntfy com id, time, expires, event, topic, title e message
HTTP Request1 enviando o alerta. À direita (OUTPUT), a confirmação do ntfy (teste feito com o limite em 5).
CampoValorPor que assim
MethodPOSTEstamos enviando uma mensagem.
URLa URL do primeiro bloco acimaServidor público ntfy.sh com o nome do seu tópico.
AuthenticationNoneO ntfy.sh não exige conta.
Send HeadersligadoPara mandar o título da notificação.
Headers → Name / ValueTitle / Alerta de dolarO ntfy usa o cabeçalho Title como título. Fica sem acento porque cabeçalhos HTTP embaralham acentos, como mostramos no tutorial do lembrete.
Send BodyligadoO texto da notificação vai no corpo.
Body Content TypeRawEnviamos o texto puro.
Content Typetext/plainAvisa que o corpo é texto simples.
Bodyo texto do segundo bloco acimaA mensagem. No corpo, os acentos funcionam normalmente.

No texto, .toFixed(2) arredonda os números para duas casas decimais: 5,1866 vira 5.19. Esse método só existe para números; é mais um motivo para converter os campos no Nó 3. A saída false do If fica sem nenhum nó: quando o dólar está abaixo do limite, o workflow simplesmente termina.


O workflow completo e o teste

Antes de testar, inscreva o celular no seu tópico pelo app ntfy (botão +, nome exato do tópico, servidor padrão ntfy.sh). Depois, teste os dois caminhos.

Caminho false: dólar abaixo do limite

Com o limite em 5.5, clique em Execute workflow. O item passa pelo If e sai pela saída false, que não tem nada ligado. O HTTP Request1 não roda e nenhuma notificação é enviada:

Workflow executado com Schedule Trigger, HTTP Request, Edit Fields e If em verde; o item sai pela saída false e o HTTP Request1 não foi executado
Caminho false: o dólar (5,1866) estava abaixo do limite (5,5), então nada foi enviado.

Caminho true: dólar acima do limite

Para ver o alerta funcionando sem esperar o dólar subir, abra o Edit Fields e troque o limite para 5, temporariamente. Clique em Execute workflow de novo. Agora o item sai pela saída true e o HTTP Request1 roda:

Workflow executado com todos os cinco nós em verde; o item sai pela saída true do If e chega ao HTTP Request1
Caminho true: com o limite em 5, o alerta foi enviado.
Nó If com a aba True Branch (1 item) mostrando dolar 5.1866 e limite 5
No If, o item aparece na aba True Branch.

O ntfy confirmou o envio e a notificação chegou com o título “Alerta de dolar” e o texto “O dólar está em R$ 5.19, acima do seu limite de R$ 5.00. Variação no dia: -0.10%. Cotação de 2026-09-25 18:30:14.”. Abaixo, a mesma mensagem vista no navegador, na página do tópico no ntfy.sh:

Página do tópico de exemplo no ntfy.sh com a mensagem de 27/09/2026, 00:06, título Alerta de dolar e o texto do alerta
O alerta de teste na página do tópico no ntfy.sh.

Depois do teste, volte o limite para o valor que você quer de verdade (no exemplo, 5.5).

Ligar o alerta de verdade

Clique em Publish, no canto superior direito do editor, e confirme. A partir daí, o n8n consulta o dólar a cada hora cheia, enquanto estiver rodando. Atenção: enquanto o dólar continuar acima do limite, chega um alerta por hora. Se isso incomodar, aumente o intervalo no Schedule Trigger (por exemplo, a cada 3 horas) ou suba o limite depois de receber o aviso.

Erros comuns

“Wrong type: ‘5.1866’ is a string but was expecting a number”. Aconteceu quando deixamos o campo dolar como String no Edit Fields. O If recebeu um texto para comparar como número e parou com erro. O próprio n8n sugere duas saídas: ligar Convert types where required ou acrescentar .toNumber(). A solução mais clara é a do Nó 3: escolher o tipo Number no campo.

Nó If com a condição em vermelho e o erro Wrong type: 5.1866 is a string but was expecting a number [condition 0, item 0], com as sugestões Enabling Convert types where required e .toNumber()
Erro real: o dólar chegou ao If como texto.

O alerta nunca dispara, sem nenhum erro. Este é o mais traiçoeiro. Aconteceu quando escrevemos {{ $json.bid }} em vez de {{ $json.USDBRL.bid }}: o campo bid fica dentro de USDBRL, então a expressão não achou nada e dolar ficou null (vazio).

Edit Fields com a expressão $json.bid e a saída dolar null
Erro real: caminho errado na expressão deixa o dólar vazio.

O If não reclamou: comparou o vazio com o limite, concluiu que não era maior e mandou o item para a saída false. Na prática, o workflow roda toda hora e nunca avisa. Por isso, teste sempre o caminho true, como fizemos acima, e confira se dolar tem um número na saída do Edit Fields.

Nó If sem erro, com o item saindo pela False Branch e dolar null
Com o dólar vazio, o If vai para false em silêncio.

JSON do workflow para importar

Se preferir, importe o workflow pronto. Baixe o arquivo abaixo ou copie o JSON. No n8n, crie um workflow novo e escolha uma das opções: abra o menu … (três pontinhos) no topo do editor, vá em Import e depois em From file; ou cole o JSON direto no canvas com Ctrl+V.

O download é um arquivo .zip com o alerta-dolar-celular.json dentro. Descompacte antes de importar (ou copie o JSON do bloco abaixo).

{
  "name": "Alerta de dólar no celular",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "id": "6d2f8a41-1111-4e3b-a106-120000000001",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "url": "https://economia.awesomeapi.com.br/json/last/USD-BRL",
        "options": {}
      },
      "id": "6d2f8a41-2222-4e3b-a106-120000000002",
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "6d2f8a41-a001-4e3b-a106-1200000000a1",
              "name": "dolar",
              "value": "={{ $json.USDBRL.bid }}",
              "type": "number"
            },
            {
              "id": "6d2f8a41-a002-4e3b-a106-1200000000a2",
              "name": "variacao",
              "value": "={{ $json.USDBRL.pctChange }}",
              "type": "number"
            },
            {
              "id": "6d2f8a41-a003-4e3b-a106-1200000000a3",
              "name": "limite",
              "value": 5.5,
              "type": "number"
            },
            {
              "id": "6d2f8a41-a004-4e3b-a106-1200000000a4",
              "name": "atualizado",
              "value": "={{ $json.USDBRL.create_date }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "6d2f8a41-3333-4e3b-a106-120000000003",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "6d2f8a41-c001-4e3b-a106-1200000000c1",
              "leftValue": "={{ $json.dolar }}",
              "rightValue": "={{ $json.limite }}",
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "6d2f8a41-4444-4e3b-a106-120000000004",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://ntfy.sh/alerta-dolar-35fadd3dde4ae2cc",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Title",
              "value": "Alerta de dolar"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "text/plain",
        "body": "=O dólar está em R$ {{ $json.dolar.toFixed(2) }}, acima do seu limite de R$ {{ $json.limite.toFixed(2) }}. Variação no dia: {{ $json.variacao.toFixed(2) }}%. Cotação de {{ $json.atualizado }}.",
        "options": {}
      },
      "id": "6d2f8a41-5555-4e3b-a106-120000000005",
      "name": "HTTP Request1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        900,
        -100
      ]
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "HTTP Request1",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  }
}

Depois de importar, troque o tópico de exemplo na URL do HTTP Request1 pelo seu e ajuste o limite no Edit Fields. O workflow não usa credenciais.

Recapitulando

  • O Schedule Trigger roda o workflow a cada hora, depois de publicado.
  • A AwesomeAPI manda a cotação como texto; o tipo Number no Edit Fields converte em número.
  • O If divide o caminho: true envia o alerta, false termina sem fazer nada.
  • Teste os dois caminhos mudando o limite, e volte o valor ao normal depois.
  • Um caminho errado na expressão não dá erro, mas deixa o alerta mudo: confira sempre os dados na saída de cada nó.

Com o If, seus workflows passam a decidir sozinhos. A mesma estrutura serve para avisar quando qualquer valor passa de um limite: outra moeda, a temperatura, o preço de um produto.