Capture Leads e use a API de Leads
Um Lead é uma pessoa que deixou contato em um dos seus sites, guardada no seu próprio Node. Este artigo vai do power-up Leads desligado até um Lead capturado por um formulário e atualizado pelo seu servidor, e mostra como provar cada passo.
Ele é escrito para dois leitores ao mesmo tempo: o operador que configura o Node e o Console, e o desenvolvedor que liga um formulário ou um CRM. Você não precisa saber o que é uma Property nem o que é um identificador do Node antes de começar.
O que é um Lead no seu Node
- Uma linha por pessoa por Property. Uma captura seguinte da mesma pessoa atualiza o mesmo Lead: o contador de capturas sobe e a última vez avança. A mesma pessoa em duas Properties são dois Leads.
- O primeiro toque é escrito uma vez. As UTMs, a página de entrada e o domínio do referrer da primeira captura são gravados na criação do Lead, e nunca mais reescritos. Só o domínio do referrer é guardado, nunca a URL completa de origem.
- A identidade não mora no Lead. Nome, e-mail, telefone, empresa, cargo e endereço ficam com a identidade da pessoa no seu Node e são lidos por um join. O Lead em si carrega os campos de pipeline.
- Sandbox fica separado. Uma captura feita com o Node em modo sandbox nunca altera um Lead real, e um Lead de sandbox nunca aparece na lista.
- É seu. Os Leads moram no seu Node. O navegador manda para o seu Node pelo loader, e o seu servidor posta direto no seu Node. Os servidores da Supreme nunca veem um Lead, um e-mail ou um telefone.
Ative o power-up Leads
Abra Settings no rodapé do menu lateral do Console, vá para a aba Núcleo do sistema e encontre a linha Leads dentro de Power-ups, logo abaixo do cartão de licença.
A linha mostra um de três estados:
| Selo | O que significa | O que a linha diz |
|---|---|---|
| Ativo | o Leads está no seu plano | "Leads está ativo no seu plano." |
| Disponível | o plano não pôde ser confirmado agora, e o Leads segue usável | "Não conseguimos confirmar seu plano agora; o Leads segue disponível." |
| Indisponível | o Leads não está no seu plano | "Leads não faz parte do seu plano.", com Ver planos se você é o dono do Node e Peça ao dono do Node se não é |
Disponível não é um Ativo degradado. Significa que o Console não conseguiu ler o seu plano naquele momento e escolheu manter o Leads usável em vez de te trancar fora dos seus próprios dados.
Se a entrada Leads não aparece na navegação do Console, é o Node que ainda não responde por Leads. O Console diz isso — "Este Node ainda não tem Leads." — e oferece Abrir Saúde do Node, onde você atualiza o Node. O resto do Console continua funcionando enquanto isso.
Se o Leads sair do seu plano
Seus leads, fontes e credenciais continuam intactos e legíveis. Novas configurações ficam bloqueadas até o Leads voltar ao seu plano.
É isso que acontece, e é só isso. Nada é apagado, pausado ou revogado, e o seu Node nunca é desligado. Seus Leads continuam listados e legíveis no Console, e uma credencial que você já emitiu continua sendo aceita pelo seu Node. O que para é configuração nova: os caminhos de captura deixam de ser oferecidos, então você não consegue gerar nem rotacionar credencial até o Leads voltar ao seu plano.
Capturar pelo navegador: um contrato, três caminhos
Os três caminhos produzem o mesmo evento canônico lead, então a forma dos campos, as regras e os códigos de erro são idênticos em qualquer um deles. A referência completa dos campos está na seção Eventos de Lead de O payload canônico de evento.
Os três precisam do loader na página, carregando o id da Property a que o Lead pertence. Veja Instale o Loader.
Três regras valem para os três caminhos:
- Um
leadprecisa de e-mail ou telefone emcontext.user. Sem nenhum dos dois, o evento é recusado. - O conjunto de identidade estendido —
full_name,company,job_titlee os campos de endereço — é lido só em eventoslead. Qualquer outro evento continua lendoemail,phone,first_nameelast_name. - Nunca mande
property_id,channel,action_sourcenemprovider. Esses são do Node: a Property vem do?pid=do loader, e o resto é carimbado pela via que recebeu o evento.
window.supremeSend à mão
Chame quando o seu formulário reportar sucesso, não quando o botão de enviar for clicado.
window.supremeSend({
payload: {
events: [{ name: 'lead', data: { params: {
value: 150.5, currency: 'BRL',
lead: { status: 'qualified', source: 'landing-page', source_lead_id: 'form-2026-0001',
custom_fields: { plan_interest: 'pro', employees: 12, newsletter: true } }
} } }],
context: { user: { email: 'maria@example.com', phone: '+5511987654321',
first_name: 'Maria', last_name: 'Silva',
company: 'Acme', job_title: 'Head of Growth' } }
}
});
Tudo dentro de params.lead é opcional. Um id de evento seu pode ir em id, ao lado de name, com até 50 bytes UTF-8.
Uma versão mínima desse trecho está pronta para copiar no Console, em Leads → Como capturar Leads → Browser/GTM.
O template do Google Tag Manager
Escolha Lead como evento. A tag então mostra um grupo Lead com Lead Status, Lead Source, Source Lead ID, Lead Value e Lead Currency, mais uma tabela Custom Fields com linhas de Key e Value. Os campos de identidade ganharam Full Name, Company, Job Title e Street Address 2.
A tag recusa, antes de enviar, todo Lead que o Node recusaria, e imprime o código da recusa no console de depuração do Preview. Você vê o problema no GTM em vez de descobrir um Lead faltando depois.
Veja Connect Google Tag Manager (em inglês).
A skill de Web Events
A partir da 1.3.0 a skill traz um padrão Contact Form Lead, que liga o envio de um formulário a um evento lead, com custom_fields incluído, e um validador que recusa um Lead com os mesmos códigos que o Node usa.
Veja Instale a skill de Web Events.
Campos canônicos e custom_fields
Os campos do produto têm nome; todo o resto é seu e vai em custom_fields.
| Campo | Regra | Se você não mandar |
|---|---|---|
params.lead.status |
um de new, contacted, qualified, won, lost |
new na criação. Um Lead existente mantém o status que tem: uma captura que o omite nunca o reseta |
params.lead.source |
string que atende ^[a-z0-9][a-z0-9_.-]{0,63}$ |
o Node preenche o default do canal na criação do Lead — website pelo navegador, api pela API de Leads — e nunca reescreve depois |
params.lead.source_lead_id |
1 a 191 caracteres, sem caractere de controle | fica vazio. Uma captura seguinte preenche só enquanto ainda estiver vazio; um valor diferente é ignorado |
params.lead.custom_fields |
objeto raso, regras abaixo | os campos já guardados ficam como estão |
params.value |
número ≥ 0, em unidades maiores, como 150.5 |
o Lead não carrega valor |
params.currency |
código ISO-4217, três letras maiúsculas; obrigatório quando value vem |
— |
source_lead_id é uma referência ao sistema de onde o lead veio. Nunca substitui o identificador que o seu Node tem para aquela pessoa, e nunca vira external_id em um payload enviado a uma plataforma de anúncios.
custom_fields
- Um objeto JSON, nunca uma lista. As chaves atendem
^[a-z][a-z0-9_]{0,63}$, e são no máximo 50. - Os valores são só escalares: string, inteiro, float finito, boolean ou
null. Nenhum array, nenhum objeto aninhado. - Tamanho: o objeto codificado em JSON com Unicode e barras não escapados precisa ter no máximo 16384 bytes.
- Chaves reservadas, recusadas se usadas:
email,phone,first_name,last_name,full_name,company,job_title,street,street2,city,state,zip,country,gender,birth_date,external_id,lead_source,ip_address,user_agent,lead_id,status,source,source_lead_id,value,currency,property_id,stuid,event_id,event_name,event_time,channel,action_source,provider,source_platform,metadata,context,user,params,attribution,utm_source,utm_medium,utm_campaign,utm_content,utm_term,gclid,gbraid,wbraid,gad_source,fbclid,fbc,fbp,msclkid,ttclid,rdt_cid,srsltid,ga_client_id,ga_session_id,ctwa_clid,delivery,session_id,page_url,referrer,created_at,updated_at,first_seen_at,last_seen_at,is_sandbox.
Numa captura seguinte o objeto é mesclado, não substituído: chave presente na nova captura sobrescreve, chave ausente dela é preservada, e chave enviada com null remove. O resultado mesclado também obedece aos dois limites — se não obedecer, a captura inteira é recusada, não sobrando nem evento nem linha.
Códigos de erro
| Código | Quando |
|---|---|
lead_block_not_object |
params.lead presente e não é objeto |
lead_status_invalid |
status fora do conjunto |
lead_source_invalid |
source fora da regex |
source_lead_id_invalid |
vazio, com mais de 191 caracteres, ou com caractere de controle |
custom_fields_not_object |
custom_fields presente e não é objeto |
custom_field_key_invalid |
chave fora da regex |
custom_field_key_reserved |
chave reservada |
custom_field_value_not_scalar |
valor array ou objeto (ou float não finito) |
custom_fields_too_many |
mais de 50 chaves (na captura ou no resultado mesclado) |
custom_fields_too_large |
mais de 16384 bytes (na captura ou no resultado mesclado) |
value_invalid |
value não numérico, negativo ou não finito |
currency_invalid |
currency fora de ^[A-Z]{3}$ |
currency_required |
value presente sem currency |
identity_required |
nem e-mail nem telefone |
event_id_too_long |
event id com mais de 50 bytes UTF-8 |
Severidade por canal
São duas regras diferentes, e há um motivo:
- Um bloco
params.leadinválido — qualquer códigolead_*,source_*oucustom_*acima — recusa o evento inteiro, em todos os canais. Nada legado manda esse bloco, então nada quebra por ser estrito. - Um
valueoucurrencyinválido é recusado pela API de Leads e pelas superfícies (o template do Google Tag Manager, a skill de Web Events). No caminho de navegador do Node, o evento segue como hoje e o Lead simplesmente não recebe valor nem moeda.valueecurrencyjá chegam em produção em payloads delead, então não podem passar a derrubar eventos.
A API de Leads: do seu servidor para o seu Node
Use quando o Lead nasce fora do navegador — um CRM, um back office, uma ligação digitada num formulário pela sua equipe — ou quando precisa atualizar um Lead que o seu site já capturou. A forma é a canônica: o mesmo conteúdo pelas duas portas produz o mesmo Lead.
A credencial
Uma credencial por Property. No Console, abra Leads, escolha a Property em Propriedade, então Como capturar Leads → a aba API → Gerar credencial.
- O segredo aparece uma única vez. O Console avisa: "Guarde agora: este segredo não aparece de novo." Coloque num cofre do seu servidor antes de sair da tela. Ele é guardado no seu Node só como hash — não há recuperação nem segunda cópia.
- Só no servidor. Nunca coloque em código de navegador, em log, em URL, em diagnóstico, em print ou em repositório.
- A Master Key não serve aqui. Apresentada nesta via, ela é respondida com
401, exatamente como um segredo errado. - O cartão então mostra URL do endpoint, ID público, os quatro últimos caracteres do segredo em Segredo, e Criada em, Rotacionada em, Último uso, Requisições e Último resultado.
- Rotacionar segredo emite um segredo novo e pede confirmação antes: "A credencial anterior para de funcionar imediatamente. Atualize o segredo no seu servidor." Não existe janela de graça. A URL do endpoint e o ID público não mudam.
Copie a URL do endpoint que o Console mostra. Não monte ela à mão.
A requisição
POST <a URL do endpoint que o Console mostra>
Authorization: Bearer <SEU_SEGREDO>
Content-Type: application/json
Idempotency-Key: lead-0001
Authorization e Content-Type são obrigatórios; Idempotency-Key é opcional. Uma requisição que não é JSON é respondida com 415, e um corpo acima de 65536 bytes com 413 — as duas antes mesmo de a credencial ser lida, então um segredo correto não salva uma requisição malformada.
O corpo:
{
"user": { "email": "maria@example.com", "phone": "+5511987654321",
"first_name": "Maria", "last_name": "Silva",
"company": "Acme", "job_title": "Head of Growth" },
"params": { "value": 150.5, "currency": "BRL",
"lead": { "status": "qualified", "source": "crm", "source_lead_id": "4821",
"custom_fields": { "plan_interest": "pro" } } },
"event_time": "2026-09-23T12:00:00Z"
}
useraceita sóemail,phone,first_name,last_name,full_name,company,job_title,street,street2,city,state,zipecountry. Um entreemailephoneprecisa estar lá.paramsaceita sóvalue,currencyelead.event_timeé opcional: ISO-8601 ou unix em segundos, com default agora.- Qualquer outra chave, no topo ou dentro de
usereparams, é recusada comounknown_field. Isso incluiproperty_id: a Property vem da credencial, nunca do corpo.
As respostas
| HTTP | Corpo | Quando |
|---|---|---|
201 |
{"ok":true,"result":"created","event_id":"…","lead":LeadWrite} |
o Lead foi criado |
200 |
{"ok":true,"result":"updated","event_id":"…","lead":LeadWrite} |
um evento novo atualizou um Lead existente |
200 |
{"ok":true,"result":"duplicate","event_id":"…","lead":LeadWrite} |
repetição da mesma Idempotency-Key; nenhum evento novo |
400 |
{"ok":false,"error":"invalid_payload","errors":[{"field":"params.lead.custom_fields.foo","code":"custom_field_value_not_scalar"}]} |
qualquer regra acima foi quebrada — nada é gravado |
401 |
{"ok":false,"error":"unauthorized"} |
sem header, segredo errado, id público desconhecido, segredo de outra Property, a Master Key, ou Property inativa |
405 |
{"ok":false,"error":"method_not_allowed"} |
qualquer coisa que não seja POST |
413 |
{"ok":false,"error":"payload_too_large"} |
corpo acima de 65536 bytes |
415 |
{"ok":false,"error":"unsupported_media_type"} |
a requisição não é JSON |
422 |
{"ok":false,"error":"custom_fields_merge_overflow"} |
a mesclagem estouraria 50 chaves ou 16384 bytes — nada é gravado |
LeadWrite carrega lead_id, status, source, source_lead_id, value_cents, currency, custom_fields, first_seen_at, last_seen_at e capture_count. Nenhuma identidade volta na resposta, e nenhuma resposta revela se outra Property, Lead ou credencial existe.
O corpo e o status do 401 são idênticos em todos esses casos de propósito: um id público desconhecido nunca é respondido com 404, então a via não pode ser usada para descobrir quais credenciais existem.
Além dos códigos da tabela acima, errors[] pode trazer três que só esta via produz:
| Código | Quando |
|---|---|
unknown_field |
chave fora do contrato, inclusive property_id no corpo |
invalid_type |
o corpo, user ou params com tipo errado, um campo de user que não é string, ou event_time ilegível |
idempotency_key_invalid |
o header Idempotency-Key fora do formato |
Uma falha inesperada depois da validação é respondida com 500 e {"ok":false,"error":"internal_error"}. Nada é gravado pela metade.
A chamada
curl -X POST '<a URL do endpoint que o Console mostra>' \
-H 'Authorization: Bearer <SEU_SEGREDO>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: lead-0001' \
-d '{"user":{"email":"maria@example.com","first_name":"Maria"},"params":{"lead":{"status":"new","source":"crm"}}}'
O Console mostra esse mesmo comando em Exemplo de requisição, já com a sua URL de endpoint preenchida.
O que o Console conta sobre a via
Toda requisição autenticada move três campos no cartão da credencial: Requisições, Último uso e Último resultado. Último resultado mostra um entre Lead criado, Lead atualizado, Repetição ignorada (mesma Idempotency-Key), Payload recusado, Credencial recusada e Nenhuma requisição ainda.
É o jeito mais rápido de separar "meu servidor nunca chamou" de "meu servidor chamou e foi recusado".
Idempotência
Uma retentativa depois de um timeout não deveria produzir uma segunda captura. É para isso que existe a Idempotency-Key.
- Formato: de 1 a 50 caracteres entre
A–Z,a–z,0–9,.,_,:e-. Uma chave inválida é respondida com400 invalid_payloade{"field":"Idempotency-Key","code":"idempotency_key_invalid"}. - Mesma chave de novo:
200com"result":"duplicate". Nenhum evento novo, nenhum segundo Lead, e o contador de capturas não se move. - Sem chave nenhuma: toda chamada é um evento novo no mesmo Lead —
200com"result":"updated".
Derive a chave do id do lead no sistema de origem mais uma versão da alteração, por exemplo crm-4821:v3. Uma chave reaproveitada em duas atualizações genuinamente diferentes faz a segunda responder duplicate e não fazer nada, em silêncio.
Prove que a captura funcionou
Pelo navegador
- Envie o formulário real do seu site.
- Abra Leads no Console, escolha a Property e clique em Atualizar.
- A linha aparece. Abra para ver Identidade, Dados do Lead, Aquisição (primeiro contato), Campos personalizados e Capturas recentes.
Para olhar o evento em si, e não o Lead, siga Verifique seu primeiro evento.
Isso prova que a página emitiu o evento, que o seu Node aceitou e guardou, e que o Console leu de volta a partir desse Node. Não prova que a Meta, o GA4 ou qualquer outro destino aceitou um evento encaminhado — confira isso na view do próprio destino.
Pela API
- Troque
<SEU_SEGREDO>pelo segredo que você guardou. - Rode o comando: a resposta é
201com"result":"created"e umlead_id. - De volta em Leads, clique em Atualizar: o Lead está na lista.
- Rode o mesmo comando outra vez, com a mesma
Idempotency-Key: a resposta é200com"result":"duplicate", e Capturas não se moveu.
O Console lista esses mesmos passos em Como provar, na aba API.
Troubleshooting sem expor PII
Antes de qualquer coisa: nunca cole segredo, e-mail ou telefone real em ticket, print ou chat. Use os valores de exemplo desta página. O Console nunca mostra um segredo duas vezes, e nenhuma resposta da API de Leads devolve identidade.
| Sintoma | Causa provável | O que fazer |
|---|---|---|
| O Lead não aparece | a captura não levou e-mail nem telefone | adicione um dos dois em context.user ou em user; sem nenhum dos dois, o evento é recusado |
| O Lead não aparece | o bloco params.lead estava inválido, e o evento inteiro foi recusado |
leia o código: no GTM ele está no console de depuração do Preview, pela API ele está em errors[] |
| O Lead aparece no site errado | o loader da página carrega o id de outra Property | corrija o ?pid= daquela página — veja Instale o Loader |
| O Lead não aparece em lugar nenhum, e nada é recusado | o Node está em modo sandbox, e Lead de sandbox nunca é listado | desligue o sandbox e capture de novo |
401 da API |
segredo errado, segredo já rotacionado, segredo de outra Property, ou a Master Key | a resposta é deliberadamente idêntica nos quatro casos. Rotacione o segredo no Console e atualize o seu servidor |
400 com errors[] |
um campo quebrou uma regra | confira cada code nas tabelas acima; nada foi gravado, então corrija e reenvie |
422 custom_fields_merge_overflow |
a mesclagem estouraria 50 chaves ou 16384 bytes | mande com null as chaves que você não precisa mais, para removê-las, ou mande menos chaves |
415 ou 413 |
a requisição não é JSON, ou o corpo passa de 65536 bytes | corrija o header ou o payload; o segredo nunca foi o problema |
| Um Lead capturado pelo navegador não tem valor | currency ausente ou inválida |
o caminho de navegador deixa o evento passar e descarta só valor e moeda. Mande um código válido de três letras junto com o valor |
duplicate quando você esperava uma mudança |
a Idempotency-Key foi reaproveitada |
use uma chave que muda com o conteúdo, como o id de origem mais uma versão |
| Leads não aparece na navegação | o Node ainda não responde por Leads | abra Saúde do Node e atualize o Node |
| Gerar credencial e Rotacionar segredo estão desabilitados | o Leads não está no plano agora | seus Leads e a credencial que você já tem seguem intactos; veja a seção do power-up acima |
Próximos passos
- O payload canônico de evento — a referência completa dos campos de Lead e como um
leadse torna um evento canônico. - Verifique seu primeiro evento — provar a ingestão no nível do evento.
- Instale o Loader — o pré-requisito de todo caminho de navegador.