Package Information
Documentation
n8n-nodes-conversor-plataformas
Community node Kokopelli ADTK Convert β um node, duas actions:
- Converter Gateway β normaliza webhooks de plataformas de venda
(Hotmart, Guru, Eduzz, Virtu, Greenn, TMB, Kiwify) para um schema canonico de compra, pronto para
alimentarupsert_purchase_data()sem remapear campos a cada plataforma. - Converter GTM β filtra eventos internos do GTM e normaliza os eventos de
negocio para o schema de eventos (todos os campos em string).
Ambas aceitam o item do Webhook node ({ headers, body }) e o de fila
RabbitMQ/AMQP em qualquer forma: { content: { body } } (envelope), { content: <body> }
(body cru direto no content), com content/body como objeto, array ou string JSON
nao-parseada (RabbitMQ sem "JSON Parse Body"). Em todos os casos o body real e
desembrulhado antes do mapeamento.
Status
π’ 7 plataformas implementadas e testadas: Hotmart, Guru, Eduzz, Virtu, Greenn, TMB
e Kiwify β todas com compra aprovada e reembolso/estorno cobertos. Auto-deteccao por
user-agent + assinatura do body. Os mapas de status seguem o enum oficial de
cada plataforma (developers.hotmart.com Webhook 2.0.0; docs.digitalmanager.guru;
Greenn/pagar.me; Virtu; docs.kiwify.com.br). PagTrust entra quando houver payload real.
Convencao de status: reembolso/chargeback consumado -> rejected; contestacao
ainda em aberto (Hotmart DISPUTE/protesto, Guru dispute/"Reembolso Sol.",
Kiwify pending_refund/refund_requested) -> pending.
Eduzz β dois formatos, ambos suportados (desde 0.9.5, nas DUAS superficies: node
n8n e variavel sGTM conversor-plataformas-gateway.tpl):
| formato | assinatura | evento | status vem de |
|---|---|---|---|
| MyEduzz (atual) | data.{buyer,price,items} |
event: "myeduzz.invoice_paid" |
data.status |
| Legado "sun" (flat) | trans_cod + um de cus_cod/cus_email/product_cod/event_name |
event_name: "invoice_paid" |
event_name, fallback trans_status: 3 |
O legado dispara com user-agent generico (node) e sem o prefixo myeduzz., entao
nao era detectado ate a 0.9.4 β toda venda aprovada saia com "Nao foi possivel
auto-detectar a plataforma". Do trans_status numerico so o 3 (paga) e mapeado:
e o unico codigo com evidencia no payload real, e status errado no banco e pior que
coluna vazia.
Action: Converter Gateway β schema canonico (saida)
| campo | tipo | observacao |
|---|---|---|
nome |
string | null | |
email |
string | null | lowercase/trim |
telefone |
string | null | so digitos |
valor |
number | null | total da compra, na moeda de moeda |
valor_parcela |
number | null | UMA parcela recebida pelo vendedor (valor_total_liquido / parcelas quando ha os dois), na moeda de moeda (0.10.0) β ver "Valores financeiros" |
valor_total_liquido |
number | null | venda INTEIRA apos as taxas da plataforma (0.10.0) β ver "Valores financeiros" |
moeda |
string | null | BRL/EUR/USD (Hotmart internacional pode vir EUR/USD) |
id_transacao |
string | null | id de dedup/upsert |
status |
pending | approved | rejected | null |
|
nome_produto |
string | null | produto principal |
id_produto |
string | null | id do produto principal |
plataforma |
enum | hotmart|guru|eduzz|virtu|greenn|tmb|kiwify |
utm_source |
string | null | Guru source.utm_*, Eduzz data.utm.* (legado: utm_* com fallback em tracker_utm_*), TMB utm_*, Virtu utm/firstTouchUtm, Greenn utm.* (defensivo), Kiwify TrackingParameters.utm_* |
utm_medium |
string | null | |
utm_campaign |
string | null | |
utm_content |
string | null | |
utm_term |
string | null | so Guru envia |
xcod |
string | null | rastreamento Hotmart (data.purchase.origin.xcod) |
sck |
string | null | 2o codigo de rastreamento: Hotmart (data.purchase.origin.sck) e Kiwify (TrackingParameters.sck) |
itens |
ItemCompra[] | detalhamento por produto |
ItemCompra: { nome, id, valor, quantidade, tipo }, onde tipo β principal |order_bump | upsell | downsell | desconhecido. A matriz carrega todos os
produtos do payload (inclusive bump/downsell); nome_produto/id_produto apontam
pro primeiro item principal (fallback: primeiro item, ex.: transacao 100% bump).
Classificacao do tipo, em ordem de precedencia:
- Rotulo nativo do item quando presente e reconhecido (
bump/upsell/downsell/main, ignorando caixa e os separadores espaco/tab/quebra de linha,_e-;
token exato pos-normalizacao). Rotulos genericos de catalogo (product,plan)
NAO classificam. Nenhuma plataforma suportada documenta upsell/downsell no payload
hoje (pesquisa 2026-07-16) β chegam como compra normal separada; o reconhecimento
e defensivo/futuro. - Flag de bump da transacao β classifica todos os itens sem rotulo nativo
reconhecido (rotulo explicito prevalece) nos mappers multi-item: Guruis_order_bumpna raiz (bump = transacao separada), Eduzzdata.orderBump.isMainSale === false(bump = fatura separada), Virtuproducts[].isOrderBumppor produto (defensivo, sem doc publica). Hotmart
(purchase.order_bump.is_order_bump) e Greenn (sale.bump_idpreenchido)
entregam 1 produto por evento β a flag classifica esse unico item. - Heuristica de posicao (duas etapas): o 1ΒΊ item sem tipo explicito =
principal(a menos que outro item ja sejaprincipalpor rotulo), demais =order_bump. Item rotulado bump/downsell na posicao 0 nao rouba o posto do
item nao rotulado. Rotulos sao comparados por token exato pos-normalizacao
(non-bump/upsell-disablednao classificam).
Atribuicao por plataforma β nao ha regra "ou UTM ou codigo"; cada uma manda
o que o payload dela tem (fonte entre parenteses):
| Plataforma | utm_* |
xcod |
sck |
|---|---|---|---|
| Hotmart | β | β (data.purchase.origin.xcod) |
β (data.purchase.origin.sck) |
| Kiwify | β (TrackingParameters.*) |
β | β (TrackingParameters.sck) |
| Guru, Eduzz, TMB, Virtu, Greenn | β | β | β |
Semantica:
xcodescksao campos SEPARADOS e independentes β na Hotmart
moram no mesmo blocoorigine qualquer um pode vir sozinho (venda de funil
costuma trazer so osck); oxcodpode encodar varios valores separados por_. O irmaosrcda Kiwify nao tem campo canonico e fica de fora.
Ausencia, camada a camada (vale pra
scke pra qualquer campo vazio):
- schema canonico (
PurchaseData): a chave existe SEMPRE βemptyPurchase
inicializa emnull.- saida do node: o
nullcai junto com os demais vazios (regra "chaves
vazias omitidas", mais abaixo) β a chave nao aparece no item.- destino: insert que hoje nao tem coluna
scksegue funcionando sem
mudanca; pra PERSISTIR o valor, o destino precisa da coluna correspondente.
Formatos de entrada: o node desembrulha o item do Webhook node
({ headers, body }) e o de fila RabbitMQ/AMQP em qualquer forma βcontent
com envelope ({ content: { body } }) ou body cru ({ content: <body> }), econtent/bodycomo objeto, array ou string JSON. Fila e webhook produzem a
mesma saida. Obs: em fila com body cru nocontentnao ha headers HTTP β e, desde 0.9.3, o
header nao e fonte de IP nem no webhook (a menos que Usar IP Do Header seja ligado), entao
oipvem do payload: o produtor precisa injetarip_overrideno body.
Moeda: o
valorpermanece na moeda original da transacao; quem soma receita
downstream deve agrupar/converter pormoeda. Sem alteracao de banco.
Kiwify β valores em CENTAVOS:
Commissions.charge_amount: 6277= R$ 62,77.
E a unica plataforma suportada com essa escala; o mapper divide por 100
(centavosParaReais), entaovalorsai em reais como o das outras. O contrato
e ESTRITO: centavo e inteiro ou string de digitos β formato monetario
(1.234,56) e fracao de centavo (6277.5) viramnull, nao valor convertido
(pelo parser generico1.234,56daria 12.3456, 100x menor). Campo vazio e
visivel; valor errado e silencioso. A base do valor echarge_amount(total cobrado do cliente), com
fallbackproduct_base_priceβsettlement_amount/my_commissionsao repasse
ao produtor e ficam de fora. A deteccao e so por body (order_id+ objetos
capitalizadosCustomer+Product/Commissions): o user-agent da Kiwify eaxios/<versao>, generico demais pra ser assinatura. A Kiwify entrega 1
produto por evento (webhook configurado POR produto), entao o order bump vem
em evento proprio ecommissioned_stores(split produtor/coprodutor/afiliado)
NAO vira item.
Valores financeiros (0.10.0, sanidade em 0.10.x)
Tres valores distintos, todos na moeda de moeda. valor nao mudou de nome nem de fonte; na sanidade
0.10.x o parser passou a ler "1,234.56" (US) como 1234.56 e numero nao finito como ausente.
| campo | o que e | regra |
|---|---|---|
valor |
bruto total da venda | inalterado |
valor_parcela |
UMA parcela recebida pelo vendedor | com valor_total_liquido e quantidade de parcelas legivel: valor_total_liquido / parcelas a centavo β sem parcelamento (1x, PIX/boleto) e o proprio liquido; liquido > 0 que arredonda a parcela para 0 -> ausente; liquido 0 -> 0. Sem liquido, ou sem quantidade legivel, vale a parcela da origem: explicita, arredondada a centavo e aceita se couber no valor a centavo β e for menor que ele quando a quantidade e 2+ ou veio ilegivel; senao valor / parcelas a centavo. Quantidade AUSENTE vira 1x so em PIX/boleto; quantidade ilegivel ("12x", 0) nao divide. Sem valor conhecido, explicita 0 -> ausente |
valor_total_liquido |
venda INTEIRA apos as taxas da plataforma | campo que a plataforma rotula como liquido, arredondado a centavo e aceito se estiver em (0, valor] a centavo β sem valor conhecido, qualquer liquido > 0 e aceito, sem teto; senao valor - taxa, com taxa em [0, valor] (taxa 0 = isencao, liquido = bruto). Liquido explicito recusado com taxa 0 -> ausente (a conta contradiria a origem) |
Mudancas de comportamento da sanidade 0.10.x: parcela e liquido EXPLICITOS passaram a sair
arredondados a centavo (em 0.10.0 saiam como vieram). Os operandos das contas β taxas, entradas e as
explicitas lidas por Eduzz legado e TMB β passam por um parser ESTRITO: number finito ou texto so com
sinal, prefixo de moeda (R$,US$ou$), digitos e separadores;"0abc",[1]outruedeixam o
campo derivado ausente em vez de virar 0 ou 1. Limitacao conhecida: notacao cientifica em texto
("1e2") nao e lida β emvaloro parser tira a letra e le 12 (o template sGTM faz igual); nos
operandos das contas o campo derivado fica ausente.
Mudanca apos a 0.10.5 β
valor_parcelae a parcela RECEBIDA: a parcela que o comprador paga
carrega os juros do parcelamento, que nao chegam ao vendedor. Guru 12x real:installments.value
120.1 (x 12 = 1441.2, com 244.19 de juros),valor1441.19,valor_total_liquido1197 ->valor_parcela1197 / 12 = 99.75 (antes 120.1). Com taxa, ela sai antes: Guru 1x de 797 commarketplace_value41.05 -> parcela 755.95.valorevalor_total_liquidonao mudaram.
Campo rotulado comissao (Kiwify my_commission, Eduzz pro_value, Hotmart PRODUCER) e a
fatia de UMA conta no rateio com coprodutor/afiliado β nao e o liquido da venda e nao e usado.
Contas saem arredondadas a centavo; operando ausente deixa o campo ausente (nunca NaN).
Na coluna valor_parcela, "recebida" = valor_total_liquido / parcelas (regra acima); o que vem
depois de "sem liquido" e a parcela da origem, usada quando falta o liquido ou a quantidade.
| Plataforma | valor_parcela |
valor_total_liquido |
evidencia |
|---|---|---|---|
| Guru | recebida por installments.qty (fila 12x: 1197 / 12 = 99.75); sem liquido: payment.installments.value (senao valor / installments.qty) |
payment.gross - tax.value - marketplace_value (sem gross: total - installments.interest; 1x: total). payment.net fica de fora: e igual ao total e inclui os juros |
14 webhooks reais + fila 12x com juros |
| Kiwify | recebida por installments (12x: 179.3 / 12 = 14.94); sem liquido: charge_amount / installments (juros inclusos: e o que o comprador paga) |
min(product_base_price, charge_amount) - kiwify_fee (juros nao chegam ao vendedor; cupom baixa o cobrado); parcelado com cupom -> ausente; campo com *_currency diferente de moeda conta como ausente, sem vetar os outros |
6 vendas reais |
| Eduzz legado | β (payload nao traz parcelas; metodo e codigo numerico) | trans_value - eduzz_value - other_values; eduzz_value 0 em fatura com valor = nao calculada -> ausente; other_values ilegivel ou negativo -> ausente |
12 webhooks reais |
| Eduzz novo | sem liquido no evento: valor / data.installments (bankSlipInstallment.totalInstallments so com installments ausente) |
β (o invoice_paid nao traz taxa) |
doc oficial |
| Hotmart | recebida por purchase.payment.installments_number; sem liquido (sem MARKETPLACE na moeda): valor / installments_number (valor = price, sem juros) |
valor - commissions[MARKETPLACE], so na mesma moeda; sem price.value (valor = full_price, com juros), a base e o menor entre full_price e original_offer_price, os dois na moeda de moeda |
doc + fila real (ARS x USD -> ausente) + webhooks reais 3x |
| Greenn | recebida por sale.installments (PIX: 8.6 / 1); sem liquido: valor / sale.installments (total inclui os juros: e o que o comprador paga) |
sale.seller_balance (senao o menor entre total e amount, - fee) |
doc + webhooks reais (PIX e cartao 10x) |
| TMB | sem liquido no webhook: valor_parcela; sem ele, valor / parcelas so sem entrada (entrada ilegivel ou negativa tambem nao divide) |
β (taxa_administracao sem unidade documentada) |
doc oficial |
| Virtu | PIX/boleto (1x): recebida = sellerNetAmount (53.94); cartao (payload sem quantidade): payment.installmentValue |
sellerNetAmount |
webhook real (sem doc publica) |
Destino com insert automatico: as duas chaves entram na saida sempre que ha dado. Node
Postgres em Map Automatically recusa chave sem coluna β antes de atualizar o pacote numa
instancia assim, crie as colunas (valor_parcela numeric,valor_total_liquido numeric) ou
mapeie as colunas explicitamente. O n8n nao atualiza community node sozinho.
Parametros da action Converter Gateway:
- Plataforma β
Auto-detectar(pela estrutura do JSON) ou plataforma fixa.
Fila com plataformas misturadas (ex: purchase_data) exigeAuto-detectarβ
plataforma fixa aplica SEMPRE aquele mapper. Desde 0.8.1, payload cuja assinatura
casa com OUTRA plataforma gera erro acionavel em vez de linha lixo silenciosa no
banco; payload que nenhuma assinatura reconhece segue processando (o override
manual existe pra isso). - Itens Como JSON β serializa
itenscomo string JSON em vez de array.
Ligue quando o destino for uma colunajson/jsonbno node Postgres nativo:
com array JS o driver enviatext[]e o insert falha
(column "itens" is of type jsonb but expression is of type text[]); com a
string JSON o Postgres faz o cast automaticotext -> jsonb. Dispensa o node
Edit Fields intermediario. Desligado (default),itenssegue array de objetos
(consumo em fluxo n8n, ex: Split Out). - Campos A Excluir β campos a remover da saida (lista = campos do schema de
compra). Aplica depois de "Itens Como JSON". Vazio (default, e workflow antigo sem o
parametro) = nao remove nada. Use quando a tabela destino nao tem a coluna: com o node
Postgres emMap Automatically, toda chave da saida precisa de coluna, e desde 0.10.0 a
saida trazvalor_parcelaevalor_total_liquidoβ sem essas colunas o upsert falha por
coluna inexistente. Marque as duas aqui (ou crie as colunas). Parametro separado do
"Campos A Excluir" do GTM: a selecao feita numa action nao vale na outra. - Manter Payload Original β anexa o JSON bruto em
_original.
Action: Converter GTM
Processa eventos vindos do GTM (server-side). Filtra os eventos internos do
GTM (sem event_name) e normaliza os de negocio para a linha de eventos, com
todos os campos em string (numero -> string, objeto -> JSON, ausente -> "").
Parametros:
- Eventos Permitidos β allowlist de
event_nameseparada por virgula. Vazio = todos
os eventos passam (inclusive sem nome). Use para restringir aos eventos de negocio. - Eventos Excluidos β blocklist de
event_nameseparada por virgula. Vazio = nenhum
excluido. Tem prioridade sobre "Eventos Permitidos" (um evento que esteja nas duas
listas e excluido). Use quando for mais facil listar os poucos eventos a descartar
(ex: internos do GTMgtm.dom,gtm.load,gtm.js) do que escolher todos os permitidos. - Campos De Saida β escolha quais campos manter no output. Vazio = todos.
- Campos A Excluir β campos a remover do output. Aplica depois de "Campos De
Saida". Vazio = nao remove nada. Use para descartar os poucos campos que a tabela destino
nao tem (ex:event_name) sem precisar escolher manualmente todos os que ficam β evita o
erroColumn 'event_name' does not exist in selected tableno insert downstream. - Usar IP Do Header (desde 0.9.3, default desligado) β le
x-real-ip/x-forwarded-for
como ultima fonte da colunaip, so quando nenhuma das quatro fontes do payload
(ip_override,ip,user_data.ip_override,user_data.ip) trouxe valor. Ligue apenas se o
n8n recebe o webhook DIRETO, sem proxy na frente. Atras de proxy (sGTM/Cloud Run, Traefik,
Cloudflare) esses headers carregam o endereco da infraestrutura, nao o do lead: gravar isso
contamina geo e atribuicao de todo evento com um IP unico. Desligado, sem IP no payload a
coluna sai vazia (NULL no banco β melhor que IP errado). - IPs/Faixas De Proxy a Ignorar (desde 0.9.2; subordinado a "Usar IP Do Header" desde
0.9.3) β IPs ou faixas CIDR separados por virgula (35.191.0.0/16, 130.211.0.0/22). Aparece
na UI somente com o toggle ligado, porque atua exclusivamente no caminho do header. Faixas
listadas sao puladas na cadeia dox-forwarded-for; se nada sobrar,ipsai vazio.Preferir o toggle desligado a manter esta lista. Foi a tentativa de 0.9.2 de salvar o
caminho do header, mas lista de faixa de nuvem drifta: o provedor troca de bloco e o IP de
proxy volta a entrar em silencio. Se ha proxy na frente do n8n, o caminho duravel e deixar
Usar IP Do Header desligado e mandar o IP do lead no payload viaip_override.
Escopo (desde 0.9.4): SO esta lista e exclusiva do header. A checagem de endereco valido
e o descarte de faixa privada/reservada (10.x,127.x,192.168.x,169.254.x,100.64/10,fe80::) valem para as duas origens β header e payload. Ja oipsProxy
continua so no header, porque nomeia a infra de transporte, que nao tem equivalente do
lado do payload: o mesmo203.0.113.9sobrevive vindo emip_overridee e descartado vindo
emx-real-ip.
Ate 0.9.3 o payload passava sem filtro, sob o argumento de queip_overrideera uma
declaracao EXPLICITA do produtor. O argumento caiu: o produtor e um script do GTM que
interpola variavel e ja mandouundefined(dai o tratamento de placeholder da 0.9.2) e10.0.0.1. Agora um candidato invalido cede a vez ao proximo da cadeia em vez de virar
coluna β mesmo padrao ja usado emgenero. A precedencia entre candidatos validos nao
mudou. - Manter Payload Original β anexa o body bruto em
_original.
Allowlist x blocklist (eventos) e Saida x Excluir (campos): os dois pares sao
complementares e combinaveis. Eventos: passa se(permitidos vazio OU nome β permitidos)
enome β excluidos. Campos: parte de todos β mantem so os de "Campos De Saida" (se
preenchido) β remove os de "Campos A Excluir". Tudo vazio = comportamento original.
Cadeia de fontes (desde 0.9.2):
topo do bodyβuser_dataβuser_data.address/url_parametersβ query dapage_location. O sGTM entrega o bloco
de usuario aninhado emuser_data, e ate 0.9.1 o node lia so o topo do body β id, geo,
user agent, data de nascimento e o IP real eram descartados em silencio. Cada fonte nova
entra depois da que ja existia, entao nenhum payload que ja funcionava muda de resultado.
Um alias presente-porem-vazio deixou de mascarar um preenchido mais adiante na cadeia.
Placeholder do produtor vira NULL (desde 0.9.2): quando a camada de tracking nao resolve
uma variavel, o JavaScript do GTM interpola o textoundefined/null/NaNe ele chega como
valor do campo (payload real: sete colunas de UTM com a palavra"undefined"). Um campo
cujo valor INTEIRO seja um desses (em qualquer caixa) e tratado como ausente β chave omitida
βNULLno banco, em vez de sujeira que mente no relatorio e escapa de umIS NULL.
Mapeamento principal: event_name β event_name;senha_atendimento β senha_de_atendimento;st_user_id β st_user_id e user_id β user_id (id de tracking vs. id do usuario no
sistema β campos distintos desde 0.6.8; fallback user_data.* desde 0.9.2);email β email/email_address/user_data.email_address (normalizado: lowercase + trim);telefone β telefone/phone/phone_number/user_data.phone_number (normalizado: so digitos);nome/sobrenome β first_name/last_name (fallback user_data.address.*);cidade/estado/pais β campo homonimo em pt-BR no topo, fallbackuser_data.address.city/.region/.state/.country (desde 0.9.2);zipcode β zipcode/cep ou user_data.address.postal_code/.zip/.zp (normalizado:
CEP brasileiro fica so com digitos, 26255-000 β 26255000; codigo estrangeiro alfanumerico
e preservado em UPPERCASE);data_nascimento β data_nascimento/birth ou user_data.birth/.birthdate/.db
(normalizado para ISO YYYY-MM-DD; aceita YYYYMMDD do sGTM, ISO e DD/MM/YYYY como
padrao BR; data impossivel e hash SHA-256 β o db do Meta CAPI chega hasheado β sao
descartados, nunca "corrigidos");user_agent β user_agent (fallback user_data.user_agent);language β language;genero β gender/genero (topo ou user_data.gender/user_data.ge β ge e o
campo do Meta Conversions API), decodificado (desde 0.6.9): as plataformas de
tracking enviam o genero hasheado em SHA-256 (lowercase+trim), entao os dois hashes
conhecidos sao revertidos pra letra canonica β sha256("f") β f, sha256("m") β m.
Tambem aceita o valor ja em claro (f/m/female/male/feminino/masculino);
desconhecido/vazio β chave omitida;xcod β xcod e sck β sck (mesma hierarquia das UTMs no payload do GTM);utm_*/ad_id/utm_id β campo homonimo no topo, fallback url_parameters.* (desde 0.9.2);fbc/fbp/ttp β common_cookie._fbc/._fbp/._ttp (o common_cookie bruto nao vai pro
output β so serve de fonte; ttp e o cookie do TikTok Pixel, irmao do fbp, desde 0.9.2);fbclid/gclid/gbraid/wbraid/ttclid/click_id β campo direto, url_parameters ou query
da page_location. O fbclid e guardado cru: derivar fbc dele (fb.1.<ts>.<fbclid>)
produziria um valor que nao casa com o cookie que o pixel gravou no browser β pior que nao ter;pagina_origem β page_location.
ipβ so o payload e fonte por padrao (desde 0.9.3). Ordem exata:ip_override(topo) βip(topo) βuser_data.ip_overrideβuser_data.ip. As duas fontes que ja existiam ficam na
frente das aninhadas, pra que um payload que ja resolvia poripnao mude de resultado. Nenhuma
das quatro trouxe valor βipvazio β NULL, que e melhor que um IP errado.Desde 0.9.4 cada candidato do payload precisa ser utilizavel β endereco valido e fora de
faixa privada/reservada. Um candidato reprovado cede a vez ao proximo da cadeia (mesmo
padrao dogenero), entaoip_override: "10.0.0.1"+ip: "189.4.20.7"grava189.4.20.7,
e nao mais o endereco privado. Reprovados todos β NULL. A precedencia entre candidatos
validos e exatamente a de antes.O header
x-real-ip/x-forwarded-fore opt-in pelo parametro Usar IP Do Header
(desligado por padrao). Atras de sGTM/Cloud Run/Traefik ele carrega o IP do proxy, nao o do
usuario: ate 0.9.1 todos os eventos desse arranjo gravavam o mesmo endereco de datacenter,
contaminando geo e atribuicao; 0.9.2 tentou corrigir filtrando faixas, mas manter lista de bloco
de nuvem drifta. Com o toggle ligado, o node percorre a cadeiacliente, proxy1, proxy2da
esquerda pra direita e usa o primeiro endereco valido que nao seja privado/reservado nem
casar as faixas de "IPs/Faixas De Proxy a Ignorar".Mudanca de comportamento em 0.9.3 (maior que a de 0.9.2): workflow que dependia do header
para preencherippassa a gravar NULL β a chave e omitida do output. Quem recebe o webhook
direto, sem proxy, deve ligar "Usar IP Do Header" ao atualizar; quem esta atras de
proxy deve mandar o IP do lead no payload viaip_override(e o que a camada de tracking
captura do usuario). Uma listaipsProxyherdada de 0.9.2 fica inerte enquanto o toggle
estiver desligado β ela nao reativa o header.O
x-forwarded-fore um header controlado pelo cliente e oip_overridevem do mesmo
payload β a colunaipe dado de atribuicao best-effort, nunca controle de seguranca.
Desde 0.9.3 isso vale ainda mais forte: com o header fora da jogada por padrao, a coluna
passa a ser 100% auto-declarada por quem POSTa o evento (o header ao menos era atribuido
pela infra). Quem alcancar a URL do Webhook node escolhe o valor β logo, autenticacao/validacao
de origem no webhook e responsabilidade da configuracao do n8n, eipnunca deve alimentar
decisao de acesso, antifraude ou rate-limit. O crivo da 0.9.4 melhora a qualidade do dado
(lixo e faixa privada param de entrar); ele nao e controle de seguranca e nao autentica
nada β um endereco publico qualquer, forjado, continua passando por definicao.
Os campos*_entrada(primeiro toque) vem dos campos homonimos do payload
(utm_source_entrada,utm_medium_entrada,utm_campaign_entrada,utm_content_entrada,utm_term_entrada,utm_id_entrada,ad_id_entrada),
persistidos pela camada de tracking (GTM/sGTM). Ficam vazios quando o payload
nao os envia β desde 0.6.10 nao sao mais derivados da query dapage_location
(que carrega o toque ATUAL/last-click, nao o primeiro toque; derivar de la duplicava
os UTMs atuais nas colunas_entrada).
data_criacaoβtimestamp(desde 0.6.7; fuso de Sao Paulo desde 0.6.11): otimestampdo evento (epoch em segundos, ms ou microssegundos β detectado pela
magnitude; ou uma string de data ISO com fuso explicito) e convertido para ISO 8601
no fuso America/Sao_Paulo, com offset explicito (YYYY-MM-DDTHH:MM:SS.sss-03:00),
pronto para inserir numa colunatimestamp/timestamptzdo Postgres. Numa colunatimestamp(sem tz) o Postgres grava a hora local de Sao Paulo; numatimestamptz
grava o instante correto β nos dois casos o valor deixa de aparecer 3h adiantado
(antes saia em UTC). Assim o registro guarda a data do evento, nao a do insert. Se
nao houvertimestampvalido,data_criacaosai vazio β a chave e omitida β oDEFAULTda coluna no banco assume. Oidcontinua gerado pelo banco (auto-id). O
campotimestampcru permanece no output (use Campos A Excluir se quiser so odata_criacao).
Colunas novas em 0.9.2 β o banco precisa acompanhar.
data_nascimento,zipcode,ttp,fbclid,click_idelanguagesao chaves novas no output. No modo
"Map Automatically" do node Postgres, chave sem coluna correspondente derruba o insert
inteiro. DDL pronto emdocs/sql/eventos-alter-0.9.2.sql;
enquanto oALTER TABLEnao for aplicado, liste os campos em "Campos A Excluir".
LGPD β minimizacao na origem. A partir de 0.9.2 o node persiste dado pessoal que antes se
perdia: data de nascimento, CEP, IP real e user agent, alem de geo e ids. Colete so o que tiver
finalidade e base legal β o que nao for usar deve ser cortado em "Campos A Excluir"
(descartar na origem sai mais barato que expurgar depois). A saida carrega PII em claro,
apropriada pro banco analitico do proprio cliente; nao ligue esta saida direto em Meta CAPI /
Google EC / TikTok Events / Stape sem aplicar SHA-256 nos campos de identificacao.
Saida tudo em texto + chaves vazias omitidas: todo campo sai como string
("para facilitar manipulacao") β numero/boolean viram texto, recursivamente
(incl.valor,quantidadee os campos deitensdos gateways). Campo ausente
na entrada nao aparece no output (sem a chave, em vez de""/null), em
qualquer nivel. Vale para GTM e para os 4 gateways. Quando o banco precisar do
campo sempre presente, use COALESCE/?? ''no insert.
Desenvolvimento
npm install
npm run dev # n8n-node dev: sobe n8n com o node + hot-reload
npm test # vitest (fixtures sanitizadas)
npm run smoke -- caminho.json # roda os mappers contra um export real do n8n
npm run smoke:gtm -- evento.json # linha de evento GTM ja na forma final do output
# (IPS_PROXY="35.191.0.0/16" espelha o param do node)
npm run lint
npm run build
npm run release # publica no npm
Instalacao (instancia unica self-hosted)
SettingsβCommunity NodesβInstall- Digitar
n8n-nodes-conversor-plataformas - Aceitar o aviso de risco β
Install
Licenca
MIT