WF1 — Envio de Fotos de Pedidos Faturados¶
ID: YvGAoK7CrZOBVSj2 · Status: ✅ ATIVO · Trigger: Webhook POST
Objetivo¶
Ao receber o XML de uma NF-e de venda, busca automaticamente as fotos dos produtos faturados no Backblaze B2, gera um link seguro com token temporário e envia para o cliente via e-mail (RD Station) e WhatsApp (Evolution API).
Endpoint do Webhook¶
O corpo da requisição deve conter o XML da NF-e como anexo binário (attachments).
Fluxo Resumido¶
- Recebe XML da NF-e via webhook
- Extrai o XML do anexo (suporta base64 e texto puro) e converte para JSON
- Filtra somente notas de venda com CNPJ de destinatário preenchido
- Extrai dados do cliente (nome, e-mail, telefone 1, CNPJ formatado) e referências dos produtos (primeiros 10 chars do
cProd) - Busca fotos na tabela
materiais_telegrampor referência — apenas imagens (jpg, jpeg, png, gif, webp), sem duplicatas pornome_arquivo - Gera token alfanumérico de 24 caracteres, monta lista de arquivos com nome do produto formatado e URLs de acesso
- Grava token no Cloudflare KV (TTL 45 dias)
- Salva registro na tabela
pedidos_midias(UPSERT pornumero_nf) - Autentica no RD Station e dispara e-mail ao cliente
- Aguarda 5 minutos
- Verifica se o telefone 1 (NF-e) existe no WhatsApp via Evolution API
- Se válido: envia WhatsApp para o telefone 1 → grava
whatsapp_enviado_parano Postgres - Se inválido: consulta telefone 2 na API Sisplan (por CNPJ), trata e verifica no WhatsApp
- Se telefone 2 válido: envia WhatsApp → grava
whatsapp_enviado_parano Postgres - Se telefone 2 também inválido: grava
whatsapp_enviado_para = NULLno Postgres
Lógica de Validação de Telefone (Fallback WhatsApp)¶
- Telefone 1: extraído diretamente da NF-e (campo
fonedo destinatário). Formatado como55+ dígitos. - Telefone 2: consultado via API Sisplan (
api.turmadabia.com.br/clientes?cnpj=XX.XXX.XXX/XXXX-XX). Campotelefone2do retorno. Formatado como55+ dígitos.
A verificação usa o nó Evolution API (chat-api / checkWhatsapp). O campo avaliado é data[0].exists = true.
| Etapa | Ação | Resultado |
|---|---|---|
| 1 | Verificar Telefone 1 no WhatsApp | exists = true → envia WA para fone 1 → grava Tel 1 no banco ✅ |
| 2 (fallback) | Consultar fone 2 na API Sisplan | Busca por CNPJ formatado (XX.XXX.XXX/XXXX-XX) |
| 3 (fallback) | Verificar Telefone 2 no WhatsApp | exists = true → envia WA para fone 2 → grava Tel 2 no banco ✅ |
| Fim | Se fone 2 também inválido | Grava whatsapp_enviado_para = NULL no banco |
Descrição dos Nós¶
| # | Nome do Nó | Tipo | Função |
|---|---|---|---|
| 1 | [IN] Receber XML Nota |
Webhook POST | Ponto de entrada. Recebe o XML da NF-e como anexo binário |
| 2 | Gera xml_puro |
Code (JS) | Extrai o XML do anexo — suporta base64 e texto puro. Lança erro se nenhum XML for encontrado |
| 3 | [CONVERT] XML para JSON |
XML Node | Converte xml_puro em objeto JSON (com mergeAttrs: true) |
| 4 | [FILTER] Validar E-mail Destino |
IF | Filtra por CNPJ preenchido e natureza de operação válida |
| 5 | [JS] Extrair Dados e Itens |
Code (JS) | Extrai número da NF, itens únicos, nome/e-mail/telefone 1/CNPJ formatado |
| 6 | [DB] Buscar Imagens por Referência |
PostgreSQL | Busca imagens na tabela materiais_telegram — apenas arquivos de imagem, sem duplicatas por nome_arquivo |
| 7 | [JS] Gerar Token e Links |
Code (JS) | Gera token de 24 chars, monta lista de arquivos com nome do produto formatado (Title Case), define expiração |
| 8 | [API] Gravar no Cloudflare KV |
HTTP PUT | Grava o JSON completo do pedido no Cloudflare KV com TTL de 45 dias |
| 9 | [DB] Salvar no Postgres |
PostgreSQL | UPSERT na tabela pedidos_midias — inclui campo criado_em |
| 10 | [API] Autenticar no RD Station |
HTTP POST | Autentica no RD Station via refresh_token e obtém access_token |
| 11 | [EMAIL] Disparar E-mail pelo RD |
HTTP POST | Envia evento de conversão ao RD Station (veggi_connect_hub) com NF, URL do portal e data de expiração |
| 12 | Espera de 5 Minutos |
Wait | Aguarda antes de verificar WhatsApp |
| 13 | Verificar Telefone 1 no whatsapp |
Evolution API | Verifica se o telefone da NF-e existe no WhatsApp (instância Veggi - 984107067) |
| 14 | Se Nº 1 Whatsapp Válido |
IF | data[0].exists = true → envia WA / false → fallback |
| 15 | [Whatsapp] Envia Whatsapp para o cliente |
Evolution API | Envia link das fotos via WhatsApp para o telefone 1 (delay 2s) |
| 16 | [DB] Gravar WhatsApp Enviado - Tel 1 |
PostgreSQL | UPDATE em pedidos_midias gravando whatsapp_enviado_para e whatsapp_enviado_em |
| 17 | Consulta Telefone 2 - Sisplan |
HTTP Request | GET api.turmadabia.com.br/clientes?cnpj={cnpj_formatado} |
| 18 | Tratamento para Nº2 |
Code (JS) | Extrai telefone2 do retorno da API e formata como 55+dígitos. Mantém todos os dados anteriores via spread |
| 19 | Verificar Telefone 2 no whatsapp |
Evolution API | Verifica se o telefone 2 (Sisplan) existe no WhatsApp |
| 20 | Se Nº 2 Whatsapp Válido |
IF | data[0].exists = true → envia WA / false → marca sem WhatsApp |
| 21 | [Whatsapp] Envia Whatsapp para o cliente nº2 |
Evolution API | Envia link das fotos via WhatsApp para o telefone 2 (delay 2s) |
| 22 | [DB] Gravar WhatsApp Enviado - Tel 2 |
PostgreSQL | UPDATE em pedidos_midias gravando whatsapp_enviado_para (Tel 2) e whatsapp_enviado_em |
| 23 | [DB] Marcar Sem WhatsApp Enviado |
PostgreSQL | UPDATE em pedidos_midias com whatsapp_enviado_para = NULL e whatsapp_enviado_em = NOW() |
Detalhes dos Nós Principais¶
Gera xml_puro¶
Extrai o XML do anexo do e-mail. Suporta dois formatos:
- Texto puro: conteúdo já começa com
<— usado diretamente - Base64: detectado automaticamente quando o conteúdo não começa com
<e passa pelo regex/^[A-Za-z0-9+/=\r\n]+$/— decodificado viaBuffer.from(trimmed, 'base64')
Remove BOM (\uFEFF) e espaços antes de retornar.
Warning
Se nenhum XML for encontrado em body.attachments[].content, o nó lança erro e o fluxo para.
[DB] Buscar Imagens por Referência¶
SELECT DISTINCT ON (nome_arquivo)
backblaze_file_id,
caminho_completo,
nome_arquivo
FROM materiais_telegram
WHERE referencia = $1
AND nome_arquivo ~* '\.(jpg|jpeg|png|gif|webp)$'
ORDER BY nome_arquivo, id DESC;
- Filtra apenas arquivos de imagem (não inclui vídeos nem PDFs)
DISTINCT ON (nome_arquivo)evita duplicatas — em caso de conflito, mantém o registro mais recente (id DESC)- Parâmetro
$1=$json.referencia(primeiros 10 chars docProd)
[JS] Gerar Token e Links¶
Além de gerar o token, este nó monta a lista de arquivos cruzando os resultados do Postgres com os dados do XML:
- Cria um mapa
referencia → nome_produtoa partir do nó[JS] Extrair Dados e Itens - Para cada arquivo do Postgres, verifica se
nome_arquivocontém alguma referência do mapa - Formata o nome do produto em Title Case (com exceções: de, da, do, das, dos, e) e remove o texto após barra (
/) - Monta o campo
nome_produtono formato:Nome Do Produto (referencia)
// Exemplo de saída:
{
nome: "verão 2026_linha noite_still_12.34.5678-001.jpg",
path: "pasta/verão 2026_linha noite_still_12.34.5678-001.jpg",
nome_produto: "Pijama Turma da Bia (12.34.5678)"
}
[DB] Salvar no Postgres¶
UPSERT na tabela pedidos_midias. Em caso de conflito por numero_nf, atualiza todos os campos exceto criado_em:
INSERT INTO public.pedidos_midias
(token, arquivos, numero_nf, expires_at, cliente_nome, cliente_email, zip_url, view_url, criado_em)
VALUES (...)
ON CONFLICT (numero_nf)
DO UPDATE SET
token = EXCLUDED.token,
arquivos = EXCLUDED.arquivos,
expires_at = EXCLUDED.expires_at,
cliente_nome = EXCLUDED.cliente_nome,
cliente_email = EXCLUDED.cliente_email,
zip_url = EXCLUDED.zip_url,
view_url = EXCLUDED.view_url
RETURNING *;
Info
O campo expires_at é convertido para o fuso America/Sao_Paulo na inserção.
[API] Autenticar no RD Station¶
Body enviado:
{
"client_id": "0cdfad70-d4db-40f4-beb7-d077d1b8acd6",
"client_secret": "{ valor atual }",
"refresh_token": "{ valor atual }",
"grant_type": "refresh_token"
}
Warning
O client_secret e o refresh_token estão hardcoded no body deste nó. Se o e-mail parar de funcionar, o refresh_token pode ter expirado — atualizar diretamente no nó [API] Autenticar no RD Station.
[EMAIL] Disparar E-mail pelo RD¶
Campos enviados no payload:
| Campo | Valor |
|---|---|
conversion_identifier |
veggi_connect_hub |
email |
E-mail do cliente (do Postgres) |
name |
Nome do cliente (do Postgres) |
cf_cf_nf_hub |
Número da NF |
cf_cf_view_url |
URL do portal do cliente |
cf_cf_expires_at |
Data de expiração formatada em pt-BR |
Nós de rastreamento de WhatsApp¶
Após cada envio (ou falha), o workflow grava o resultado em pedidos_midias:
| Situação | Campo whatsapp_enviado_para |
Campo whatsapp_enviado_em |
|---|---|---|
| Tel 1 enviado | Número do Tel 1 | NOW() |
| Tel 2 enviado | Número do Tel 2 | NOW() |
| Nenhum válido | NULL |
NOW() |
Filtro de Natureza de Operação (natOp)¶
O nó [FILTER] Validar E-mail Destino só processa notas com as seguintes naturezas:
- VENDA DE MERCADORIA
- VENDA DE MERCADORIA DO ESTABELECIMENTO
- VENDA DE MERCADORIA ADQUIRIDA OU RECEBIDAS DE TERCEIROS
- VENDA DE PRODUÇÃO DO ESTABELECIMENTO ORIGINADA DE ENCOMENDA
- VENDA ADQUIRIDA DE TERCEIROS ORIGINADA DE ENCOMENDA PARA ENTREGA FUTURA
- VENDA DE PRODUTO DO ESTABELECIMENTO DESTINADA A NÃO CONTRIBUINTE
- VENDA DE MERCADORIA ADQUIRIDA OU RECEBIDA DE TERCEIROS, DESTINADA A NÃO CONTRIBUINTE
- VENDA DE PRODUÇÃO DO ESTABELECIMENTO DESTINADA A ZONA FRANCA
- VENDA DE MERCADORIA ADQUIRIDA DE TERCEIRO DESTINADA A ZONA FRANCA DE MANAUS
Notas sem CNPJ de destinatário ou com outras naturezas são descartadas silenciosamente.
Lógica de Token e URLs¶
- Token: string alfanumérica de 24 caracteres (minúsculas + números)
- Expira em 45 dias (TTL no KV: 3.888.000 segundos; campo
expires_atno PostgreSQL) view_url:https://hub.grupoveggi.com.br/?token={token}zip_url:https://drive.grupoveggi.com.br/{token}
Banco de Dados — Campos de pedidos_midias gravados por este workflow¶
| Campo | Origem | Observação |
|---|---|---|
token |
[JS] Gerar Token e Links |
24 chars alfanuméricos |
arquivos |
[JS] Gerar Token e Links |
JSON com nome, path, nome_produto |
numero_nf |
[JS] Extrair Dados e Itens |
Chave única — UPSERT |
expires_at |
[JS] Gerar Token e Links |
NOW + 45 dias, fuso SP |
cliente_nome |
[JS] Extrair Dados e Itens |
Formatado em Title Case |
cliente_email |
[JS] Extrair Dados e Itens |
Do campo email da NF-e |
zip_url |
[JS] Gerar Token e Links |
drive.grupoveggi.com.br/{token} |
view_url |
[JS] Gerar Token e Links |
hub.grupoveggi.com.br/?token={token} |
criado_em |
Banco (NOW()) |
Fuso SP — não é atualizado no UPSERT |
whatsapp_enviado_para |
Nós de gravação WhatsApp | Número enviado ou NULL |
whatsapp_enviado_em |
Nós de gravação WhatsApp | NOW() após tentativa |