No navegador: formulário, pixel, chat e push
Embed de formulário, pixel de analytics, chat, web push e o SDK @asender/pages-sdk: o que colar no site e que chave usar.
No navegador: formulários, pixel, chat e push
Tudo o que o Asender coloca no site do seu cliente roda no navegador do visitante: o formulário incorporado, o pixel de analytics, o chat do atendimento e o Web Push. Este guia mostra cada peça: o código para colar, o que ela envia, onde ela guarda dados no navegador e o que liberar na Content-Security-Policy (CSP) do seu site.
Nenhuma dessas peças usa a sua API key. O código de navegador é público: ele carrega só identificadores públicos (o id do formulário, a credencial assinada do pixel, uma chave de site pk_). Nunca cole uma chave sk_ em página de site. Para enviar leads a partir do seu servidor, veja o guia da API de Páginas.
Hosts públicos
| Host | Para que serve |
|---|---|
https://embed.asender.net | Formulário incorporado: embed.js, envio, carimbo anti-spam, eventos do funil e a consulta de endereço por CEP. É o endereço que o painel gera nos snippets novos. |
https://sites.asender.net | Runtime das páginas: pixel.js, /collect, /replay, chat.js e as rotas do formulário (envio, carimbo, eventos e a consulta de endereço, /f/{formID}/endereco). Snippets antigos de formulário, colados com sites., continuam funcionando. |
https://api.pages.asender.net | Painel e API de Páginas: criar pixels e ler relatórios (exige sessão). |
https://api.atendimento.asender.net | API pública do chat (/public/v1/chat). |
https://api.asender.net | API da plataforma; recebe o registro de Web Push (/v1/push/site/devices). |
O domínio asender.com.br segue respondendo em todos esses nomes (embed.asender.com.br, sites.asender.com.br e assim por diante). Snippet já colado com ele continua valendo. Se você tem CSP, libere os dois domínios enquanto houver snippet antigo.
1. Formulário incorporado
Colar em uma linha
No painel de Páginas, abra Formulários, escolha o formulário e clique em Ver como usar. O painel mostra o código pronto e a aba JavaScript é a recomendada:
<script src="https://embed.asender.net/f/frm_0123456789abcdef/embed.js" async></script>
frm_0123456789abcdef é o id do formulário (formID). Ele aparece no código copiado e na URL de envio do bloco API, na mesma tela. O id é público: quem abre o código-fonte da sua página o vê. A proteção da conta é a lista de sites autorizados (veja abaixo), não o sigilo do id.
O embed.js injeta o formulário no lugar da própria tag <script>, sempre com a definição atual. Edite os campos no painel e o site passa a mostrar a versão nova em até 1 minuto, sem recolar nada. O custo é depender de JavaScript. Quem precisa do formulário sem JavaScript usa a aba HTML completo, que é o mesmo formulário escrito por extenso (e que fica "congelado": cada campo novo exige recolar o código).
O que o HTML gerado contém
O <form> é um formulário nativo que posta direto no Asender; o JavaScript só aprimora. Os atributos que o painel escreve nele:
| Atributo | Valor | Para que serve |
|---|---|---|
action | https://embed.asender.net/f/{formID} | Para onde o envio vai. |
data-asender-form | o formID | Identifica o formulário. É o que o pixel lê (seção 2). |
data-asender-carimbo | https://embed.asender.net/f/{formID}/carimbo | Endereço do carimbo de tempo anti-spam. As rotas /eventos e /endereco são derivadas dele. |
data-asender-sucesso | texto | Mensagem de agradecimento (a configurada no formulário; padrão "Obrigado! Recebemos seus dados."). |
data-asender-destino | URL (opcional) | Para onde mandar o visitante depois do envio. |
Esses atributos são gerados a partir da configuração do formulário no painel (mensagem e destino pós-envio). Você muda o texto e o destino no painel, não no HTML. Na resposta do envio, o servidor manda o pós-envio atual (pos_envio) e o JavaScript o prefere aos atributos; os atributos são o plano B. O redirecionamento só segue destino http:// ou https://.
Como o envio funciona: POST /f/{formID}
Sem credencial. A conta é resolvida pelo próprio formID.
curl -X POST 'https://embed.asender.net/f/frm_0123456789abcdef' \
-H 'Origin: https://www.seusite.com.br' \
-H 'Accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'nome=Fulana' \
--data-urlencode 'email=fulana@exemplo.test' \
--data-urlencode 'consentimento=on'
# → 200 { "ok": true, "lead_id": "…", "duplicada": false,
# "pos_envio": { "destino": "", "mensagem": "Obrigado! Recebemos seus dados." } }
O que vale saber:
- Formato do corpo. O formulário (o JavaScript do snippet e o envio nativo) manda
application/x-www-form-urlencoded, um parnome=valorpor campo. Use os nomes dos campos como estão no formulário. Campo que o formulário não declara é descartado em silêncio, para que um snippet desatualizado ou um bot que anexa lixo não faça você perder um lead legítimo. - Navegação ou JSON. Quem navega (envio nativo,
Accept: text/html) recebe uma página (o agradecimento, ou um redirecionamento 303 para o destino configurado). Quem pedeAccept: application/jsonrecebe JSON. O snippet envia o formulário comfetch(urlencoded) e pede JSON na resposta. - Origem. O navegador manda o cabeçalho
Origin. Se o formulário tem a trava de sites autorizados ligada, só as origens da lista passam; as demais recebem 403 (origem não autorizada para este formulário). A trava é opcional: desligada, qualquer site pode embutir e postar, e o anti-spam e o limite de frequência continuam valendo. Ligue-a em Ver como usar → Sites autorizados: até 20 origens (esquema + host + porta, sem curinga). A comparação é exata, sem sufixo. - Sempre 200 para spam. Uma submissão classificada como spam recebe 200, igual a uma aceita. É de propósito: devolver erro ensinaria o robô a ajustar o envio até passar. Por isso, 200 não prova que virou lead; o sinal de lead criado é o
lead_idna resposta. - Validação. Campo obrigatório vazio ou valor inválido responde 422 com o primeiro problema e a lista completa:
{
"error": { "message": "submissão recusada: [email: obrigatório]" },
"erro": "dados inválidos",
"campo": "email",
"motivo": "obrigatório",
"motivos": ["email: obrigatório"]
}
| Status | Quando |
|---|---|
| 200 | Aceito, deduplicado ou descartado como spam (ok: true). |
| 403 | Origem fora da lista de sites autorizados do formulário. |
| 404 | Formulário inexistente. |
| 422 | Dado inválido ou campo obrigatório ausente (corpo acima). |
| 502 / 503 | Falha nossa ao registrar ou ao resolver o formulário; tente de novo. |
Consentimento (LGPD)
Se o formulário exige consentimento, o HTML inclui uma caixa consentimento obrigatória, com o texto de aceite que você configurou no formulário. O servidor confere: sem a caixa marcada o envio é recusado com 422 (consentimento: obrigatório). A decisão de consentimento e o texto aceito acompanham o lead até o CRM. Se você monta o HTML à mão, mande o campo consentimento com qualquer valor não vazio somente quando a pessoa marcou a caixa.
Anti-spam
Todo formulário gerado leva um campo-isca oculto (_asender_website, que deve ficar vazio) e dois campos de sinal (_asender_carimbo e _asender_interacao). Ao carregar, o script pede GET /f/{formID}/carimbo, que devolve {"carimbo": "…"}: o instante assinado em que o formulário foi aberto. Preencher rápido demais, sem interação, ou tocar na isca soma pontos de suspeita. Não há CAPTCHA. O servidor só descarta quando a soma passa do corte, e um sinal sozinho quase nunca descarta. Sem JavaScript os sinais vão vazios e o servidor os trata como "não medidos".
Preenchimento de endereço por CEP
Se o formulário tem o bloco de endereço, o script consulta GET /f/{formID}/endereco?pais=BR&cep=01310100 e preenche estado, cidade, bairro e rua. A rota só atende formulários que têm endereço e países que eles aceitam; responde 404 (CEP ou campo inexistente), 422 (país ou CEP inválido), 429 (muitas consultas) ou 503. Nenhuma dessas respostas trava o formulário: o visitante digita o endereço à mão.
O hostembed.asender.nettambém encaminha/f/{formID}/endereco, então o preenchimento por CEP funciona com o snippet apontando para ele. Em instalação cuja borda ainda não recebeu essa atualização, o endereço cai em modo manual.
Eventos no navegador
O formulário dispara eventos que você pode escutar na sua página.
| Evento | Onde dispara | detail |
|---|---|---|
asender:submissao | No <form>, subindo (bubbles) até document | { id, formulario_id } |
asender:enviado | No <form> | {} |
asender:erro | No <form> | { campo } (nome do campo com problema, ou vazio) |
asender:submissao sai depois de um envio aceito via fetch que devolveu lead_id, e antes de qualquer redirecionamento. Spam e respostas sem lead_id não disparam o evento. O mesmo valor fica em window.asenderSubmissao. O id é o mesmo identificador que o servidor usa como event_id ao reportar a conversão às APIs de anúncio (Meta, Google Ads, TikTok). Por isso você deduplica o pixel do navegador com a conversão do servidor usando esse id como eventID:
<script>
document.addEventListener("asender:submissao", function (e) {
// Meta Pixel: mesmo event_id que o servidor manda à API de Conversões
fbq("track", "Lead", {}, { eventID: e.detail.id });
});
</script>
asender:enviado e asender:erro não sobem até document; escute no próprio formulário (form.addEventListener(...)). No envio nativo (sem JavaScript) a página não fica aberta, então nenhum desses eventos acontece.
2. Pixel de analytics
O pixel mede o comportamento na página: visita, rolagem, cliques, formulário visto, iniciado e enviado, tempo na página e saída. Ele é opcional; o formulário incorporado funciona e é contado sem ele (veja "Formulário sem pixel").
Criar e instalar
- No painel de Páginas, abra Analytics → Pontos de coleta e crie um ponto de coleta (o pixel). Na API,
POST https://api.pages.asender.net/v1/pixels(com sessão) devolve osnippetpronto. - Em Domínios autorizados do pixel, cadastre o domínio do seu site (por exemplo
www.seusite.com.br). Enquanto ele não estiver na lista, o navegador do visitante recebe 403 e nenhum evento sai. - Cole o snippet antes de
</head>.
Num site que não é do Asender, o snippet deve ficar assim:
<script src="https://sites.asender.net/pixel.js"
data-lz="<credencial-assinada>"
data-lz-collect="https://sites.asender.net/collect"
data-lz-replay="1"
data-lz-replay-url="https://sites.asender.net/replay"
async></script>
Endereço absoluto. O snippet que o painel e a API (POST /v1/pixels) geram já trazsrc,data-lz-collectedata-lz-replay-urlcom o endereço público do runtime. Se você colou um snippet antigo, comsrc="/pixel.js"relativo, ele aponta para o seu próprio domínio e não carrega: troque pelo snippet acima ou gere o código de novo. Como reforço, opixel.jstambém aceitadata-base(a origem do runtime) e, sem ele, usa a origem do própriosrcpara/collecte/replay. Em páginas publicadas pelo próprio Asender nada disso é necessário.
| Atributo | Para que serve |
|---|---|
data-lz | A credencial assinada do ponto de coleta (identifica conta e pixel; não é segredo). Sem ela o script não faz nada. |
data-lz-collect | Endereço de /collect. Padrão: /collect na origem do src do script (ou de data-base). |
data-lz-replay | 1 liga a gravação de jornada (veja abaixo). Ausente, nada é gravado. |
data-lz-replay-url | Endereço de /replay. Padrão: /replay na origem do src do script (ou de data-base). |
data-base | Origem do runtime (ex.: https://sites.asender.net) para /collect e /replay. Padrão: a origem do src. |
data-lz-click | Em qualquer elemento seu: o texto que vira o nome do clique no relatório. |
O que ele coleta
O script manda lotes de eventos para POST /collect (JSON, com a credencial no corpo). Eventos: page_view, scroll_depth (25, 50, 75 e 100%), click (apenas em links, botões e elementos com data-lz-click, com posição em porcentagem da página), form_view, form_start, form_submit, time_on_page e exit. A sessão é um id aleatório guardado em sessionStorage (chave _lz_s), que morre quando a aba fecha. Sem cookie, sem e-mail, sem nome, sem fingerprint.
Formulários: o atributo data-asender-form
O pixel procura na página os <form data-asender-form="…"> e emite:
form_view, uma vez por formulário, quando metade dele fica visível (ou no carregamento, em navegador semIntersectionObserver);form_startno primeiro foco em um campo do formulário;form_submitno envio.
Cada evento leva o formID, e é isso que agrupa os números por formulário no relatório. Como o HTML gerado já traz data-asender-form, nenhum passo extra é necessário. Se você monta o HTML à mão, escreva o atributo com o id do formulário (o nome antigo data-form-id ainda é aceito).
Formulário sem pixel
Um formulário incorporado em site sem o pixel também aparece no Analytics geral. O script do próprio formulário manda o funil dele para POST /f/{formID}/eventos, e o servidor espelha três eventos (form_view, form_start, form_submit) no Analytics geral, marcados com origem embed e o host do site. Se o script detecta que o pixel está na página (script[data-lz]), o espelho não acontece, para não contar em dobro.
Gravação de jornada (/replay)
Com data-lz-replay="1", o pixel grava a sequência de interações: clique (seletor do elemento e posição), campo tocado, rolagem, navegação e tamanho da janela. Não é gravação de tela nem de DOM. Em campos de formulário, registra apenas o tipo do campo e o comprimento do valor em faixas (0, 3, 8, 16, 32, 64), nunca o texto digitado, e só quando o campo perde o foco, nunca por tecla. Campos de senha, cartão, CPF/CNPJ e semelhantes (pelo nome ou id) saem como "sensível" e sem comprimento. O seletor traz só tag, id e duas classes. Para ler as jornadas: GET /v1/replay/sessions e .../sessions/{id}/events na API de Páginas (exige sessão).
Como o snippet do painel já inclui data-lz-replay="1", a gravação de jornada vem ligada. Para desligar, remova o atributo. Veja "Privacidade" no fim.
3. Chat do atendimento
O widget de chat abre uma conversa do visitante direto na caixa de entrada do Atendimento.
Instalar
- No Atendimento, abra Bot com IA, seção Widget de chat, informe as origens do site (até 10, como
https://www.seusite.com.br) e copie o snippet. O painel gera a chave de sitepk_chat_…a partir das origens. - Cole antes de
</body>:
<script type="module" async
src="https://sites.asender.net/chat.js"
data-site-key="pk_chat_…"
data-base="https://api.atendimento.asender.net"></script>
| Atributo | Obrigatório | Para que serve |
|---|---|---|
src | sim | O widget, servido por sites.asender.net/chat.js. |
data-site-key | sim | A chave de site pk_chat_…. Sem ela o widget não monta e escreve um aviso no console. |
data-base | sim | A API do chat (https://api.atendimento.asender.net). Sem ele o widget não monta e escreve um aviso no console: a origem do src é o host de sites, que não é a API do chat. |
data-titulo | não | Título do painel do chat. |
data-saudacao | não | Primeira mensagem de boas-vindas. |
data-intervalo-ms | não | Intervalo (ms) da busca de novas mensagens quando a conexão ao vivo (SSE) não está disponível. Padrão: 4000. |
data-manual | não | Presente, desliga a montagem automática (para quem chama montarChat por conta própria). |
data-asender-chat | não | Marca a tag como a do widget quando o src não é idêntico ao do snippet (por exemplo, atrás de proxy). |
Cor, título, saudação e posição do widget vêm da aparência salva na tela do Widget, que o script lê ao montar. Os atributos data-titulo e data-saudacao sobrescrevem texto na instalação.
Segurança da chave de site
A chave de site é pública: ela está no código-fonte da página. A defesa é a lista de origens assinada dentro dela, conferida no servidor a cada chamada, mais o limite de frequência por chave. Ela só abre o chat; não vale para nenhuma outra API. Mudou o domínio do site? Gere a chave de novo com as origens novas.
Cookie da conversa
Na primeira mensagem, o servidor grava o cookie asender_chat: um token aleatório, HttpOnly, Secure, SameSite=None, válido por 30 dias e restrito ao caminho /public/v1/chat (é enviado só às rotas do chat, nunca a outras). É ele que reencontra a conversa quando a pessoa volta ao site. Como o widget fala com um domínio diferente do seu site, é um cookie de terceiro; navegadores configurados para bloquear cookies de terceiros podem impedir que a conversa seja retomada.
4. Web Push
Web Push envia notificações a quem aceitou receber, mesmo com a aba fechada. O seu site registra o navegador do visitante no Asender; as campanhas saem depois, pela ferramenta de Push.
Passo a passo
- No app de Push, abra Coletar inscrições, informe as origens do site (de 1 a 10) e gere o pacote. Ele traz quatro itens: a chave de site
pk_push_…, o service workerasender-sw.js, o manifest e o snippet de inscrição. A chave de site vale só para as origens informadas. - Salve o service worker como
/asender-sw.jsna raiz do site. Um service worker só controla o diretório em que é servido; num subdiretório ele não recebe push do site inteiro. - Salve o manifest como
/manifest.jsone referencie com<link rel="manifest" href="/manifest.json">. Coloque também os ícones/icon-192.pnge/icon-512.png, que o manifest e as notificações usam. - Cole o snippet em uma tag
<script>e chameasenderSubscribePush()a partir de um clique do visitante. Os navegadores recusam o pedido de permissão disparado no carregamento da página. - O site precisa estar em HTTPS (
localhosté a única exceção).
O snippet faz, em ordem: verifica suporte a service worker e a PushManager; pede a permissão de notificação (Notification.requestPermission()); registra /asender-sw.js; assina o push com a chave pública VAPID da sua conta; e envia a inscrição ao Asender:
curl -X POST 'https://api.asender.net/v1/push/site/devices?site_key=pk_push_…' \
-H 'Origin: https://www.seusite.com.br' \
-H 'Content-Type: application/json' \
-d '{ "Endpoint": "https://…", "P256dh": "…", "Auth": "…" }'
# → 200 { "Status": "registered" }
A chave de site vai na query porque o preflight do navegador (a requisição OPTIONS prévia) não leva cabeçalhos próprios: assim a chave e a origem já são conferidas antes do POST existir.
| Status | Quando |
|---|---|
| 200 | Inscrição registrada (Status: "registered"). |
| 400 | Corpo inválido (o endpoint precisa ser https; P256dh e Auth obrigatórios; campo extra é recusado). |
| 401 | Chave ausente ou inválida. Uma chave de servidor sk_ apresentada aqui também é recusada. |
| 403 | Origin ausente ou não declarada na chave; ou conta suspensa. |
| 429 | Limite de frequência por chave e por IP. |
Permissões e o que esperar do navegador
- O visitante vê o diálogo nativo do navegador e pode permitir, bloquear ou ignorar. Se ele bloquear, o site não pode perguntar de novo; só o próprio visitante reativa nas configurações do navegador. O snippet registra o motivo no console (
permissão de notificação negada pelo usuário) e não faz nada além disso. - Peça a permissão depois de uma ação clara, com um botão ou convite explicando o que a pessoa vai receber. Pedir no carregamento é bloqueado pelos navegadores e reduz a aceitação.
- Navegadores sem suporte a Web Push mostram
este browser não suporta Web Pushno console e seguem sem notificações. - A notificação mostra título, texto, ícone (o da campanha, ou
/icon-192.png) e, se houver, imagem. O clique abre a URL da campanha, reaproveitando uma aba do seu site já aberta.
Em páginas publicadas pelo próprio Asender com a coleta de push ligada, o convite de inscrição e o service worker (/asender-sw.js) já vêm no host da página; nada disso é preciso colar.
5. SDK @asender/pages-sdk
O repositório contém o SDK JavaScript @asender/pages-sdk (versão 0.2.1), com três pontos de entrada: . (servidor, com API key), ./client (navegador, sem credencial, com createBrowserClient e rastrearFormulario para o funil de um formulário seu) e ./chat (o widget de chat, o mesmo que chat.js entrega).
Ele ainda não está publicado no npm. Não há npm install @asender/pages-sdk funcionando hoje. Para o navegador, use os caminhos desta página, que não dependem de pacote:
- formulário: o snippet
embed.jsou oPOST /f/{formID}direto (application/x-www-form-urlencoded; osubmitFormdo SDK faz o mesmo); - chat: o
chat.jspor CDN, como na seção 3; - do seu servidor:
POST /v1/leadscom API key, no guia da API de Páginas.
A publicação do pacote está planejada. Quando sair, este guia passa a mostrar a instalação.
6. CSP e privacidade
Content-Security-Policy
Se o seu site usa CSP, libere o que for usar. Os domínios abaixo valem para o host canônico; acrescente os equivalentes .com.br se houver snippet antigo.
| Recurso | Diretiva | O que liberar |
|---|---|---|
| Formulário incorporado | script-src | https://embed.asender.net e 'unsafe-inline' (veja a nota) |
| Formulário incorporado | style-src | 'unsafe-inline' |
| Formulário incorporado | connect-src | https://embed.asender.net (envio, carimbo, eventos e endereço); https://sites.asender.net só para snippets antigos |
| Formulário incorporado, envio sem JavaScript | frame-src, form-action | https://embed.asender.net |
| Pixel | script-src, connect-src | https://sites.asender.net |
| Chat | script-src | https://sites.asender.net |
| Chat | connect-src | https://api.atendimento.asender.net |
| Chat | style-src | 'unsafe-inline' (o widget injeta seu estilo) |
| Web Push | connect-src | https://api.asender.net |
| Web Push | worker-src | 'self' (o service worker mora no seu domínio) |
Exemplo para um site que usa tudo:
Content-Security-Policy:
script-src 'self' 'unsafe-inline' https://embed.asender.net https://sites.asender.net;
style-src 'self' 'unsafe-inline';
connect-src 'self' https://embed.asender.net https://sites.asender.net
https://api.atendimento.asender.net https://api.asender.net;
frame-src https://embed.asender.net;
form-action 'self' https://embed.asender.net;
worker-src 'self';
Por que'unsafe-inline'no formulário. O formulário incorporado vem com<style>e vários<script>embutidos no próprio HTML (máscaras, anti-spam, funil). Oembed.jsrecria esses scripts na sua página, e eles precisam descript-src 'unsafe-inline'(umnoncedo seu site não é herdado por scripts criados dessa forma). Se a sua política não admite'unsafe-inline', use o formulário só em uma página com política própria, ou abra o formulário publicado pelo Asender em vez de incorporá-lo. Pixel, chat e Web Push não exigem'unsafe-inline'emscript-src.
O que fica no navegador do visitante
| Peça | Onde | O quê | Quanto dura |
|---|---|---|---|
| Pixel | sessionStorage _lz_s | id aleatório da visita | até a aba fechar |
| Formulário | localStorage asf_d | id aleatório do dispositivo | até o visitante apagar |
| Formulário | localStorage asf_v, asf_ft | contagem de visitas ao formulário e a origem da primeira visita | até o visitante apagar |
| Formulário | cookies _fbp, _fbc | somente leitura, para a conversão de anúncio | não escrevemos esses cookies |
| Chat | cookie asender_chat | token da conversa, HttpOnly | 30 dias |
| Web Push | service worker e assinatura de push | inscrição do navegador | até o visitante cancelar |
O pixel e o formulário não gravam cookie próprio.
Dados enviados junto do formulário
Além dos campos que a pessoa preencheu, o formulário envia um contexto técnico: página, idioma, fuso, tamanho de tela, tipo de dispositivo, parâmetros utm_* e identificadores de clique de anúncio presentes na URL (como gclid e fbclid), origem da primeira visita, número de visitas, id do dispositivo, tempo no formulário e a sessão do pixel, quando há. Não coleta impressão digital de canvas ou áudio, nem geolocalização do navegador. O IP e a geolocalização por IP são resolvidos no servidor. Os eventos do funil do formulário (/eventos) carregam só o nome dos campos tocados, nunca o valor.
Consentimento: o que é com você
O Asender não traz um banner de cookies. Você define a política do seu site. Pontos práticos:
- Para o formulário, use a opção de exigir consentimento com o texto de aceite que sua base legal pede; o aceite fica registrado junto do lead.
- O pixel e a gravação de jornada são analytics. Se a sua política exige opt-in para analytics, carregue o
pixel.jssó depois do aceite. Removadata-lz-replayse não quiser a jornada. - O Web Push e o chat dependem de ação do visitante (aceitar a notificação, abrir o chat), mas informe o uso na sua política de privacidade.
- Em todos os casos, não coloque
sk_nem outro segredo em código de página.