Meu Decorado nos leads da imobiliária
Arquiteto de Bolso, design de vida

Configuração · Imobiliárias

Meu Decorado nos leads da imobiliária

Uma tag no Gerenciador de Tags e um campo no CRM, cerca de 20 minutos. Ao final, cada lead seu carrega um identificador da sessão, e nós cruzamos esse identificador com quem usou o Meu Decorado.

Antes de começar

O seu site passa a dar a cada visitante um identificador anônimo: uma sequência aleatória, sem nome, e-mail nem telefone. Ele chega ao Meu Decorado e ao lead dos seus formulários, e é isso que liga um ao outro.

Como a conta fecha: o identificador viaja com o lead até o seu CRM e também com os eventos do decorado, que chegam até nós. Nós cruzamos os dois lados e dizemos quais leads seus passaram pelo decorado.

Isto entrega atribuição no seu CRM, não um funil no seu GA4. O decorado de imobiliária não publica passos de funil para a página em volta, em endereço nenhum. Se você quiser o funil no GA4, fale com a gente: depende de mudança do nosso lado.

O que você precisa ter em mãos

  • Acesso de editor ao container do Gerenciador de Tags
  • Os seletores dos formulários de lead do seu site
  • Como o seu banner de cookies registra o aceite
  • Contato de quem mexe no CRM e no sistema do formulário

Prefere que a gente configure?

Dê acesso de editor no Gerenciador de Tags. Nós configuramos e testamos, e a publicação continua com você: acesso de editor não publica.

Aqui não é preciso cadastrar domínio do nosso lado. Esse passo existe no formato de incorporadora, que funciona de outro jeito.

Passo a passo

  1. Preencher os dados do seu site

    Preencha abaixo e o código da Etapa 2 se ajusta sozinho, pronto para copiar. Só quem escolher "Outro" em como o site registra o aceite precisa trocar uma linha dentro do código, e a página indica qual.

    Seletor CSS dos formulários de lead: contato do imóvel, agendar visita, contato geral. Não inclua currículo, área do cliente, login nem busca.

    Como descobrir: botão direito no formulário, Inspecionar. Ou liste os da página no console com [...document.querySelectorAll("form")].map(f => f.className || f.id).

    Precisa casar o <form>, não a <div> em volta, onde a classe costuma estar: fora do formulário, o campo nunca é enviado, sem erro. Formulário dentro de iframe de terceiro, como alguns do HubSpot ou RD Station, não é alcançável: avise a gente.

    Deixe em branco se o seu banner não tiver um seletor estável: a tag ainda cobre o aceite por cerca de três minutos após a página carregar. O lead continua levando o campo.

    Define quando o identificador pode ser criado. Sem ela, o código sai fechado e nada é gravado.

    Deixe em branco se o decorado e os formulários estão no mesmo endereço. Valor errado faz o navegador recusar o cookie sem avisar.

    O que você preencher fica salvo neste navegador, para não se perder ao voltar. Se for configurar outro site, limpe antes.

  2. Colar a tag no Gerenciador de Tags

    Nova tag de HTML personalizado, gatilho Inicialização, todas as páginas, consentimento nenhum adicional. A checagem está dentro do código, porque a do Gerenciador de Tags só vale no carregamento e perderia quem aceita depois.

    Já está com os seus dados. Copie o conteúdo inteiro, comentários incluídos

    <!--
      Arquiteto de Bolso · Meu Decorado — identificador de jornada (abclid)
    
      Cria o identificador no SEU domínio depois do aceite de cookies, entrega-o ao
      Meu Decorado quando ele pergunta, e põe o mesmo valor num campo oculto
      "abclid" nos formulários de LEAD, para chegar ao CRM junto com o lead.
    -->
    <script>
    (function () {
      if (window.__upikAbclid) return; // a tag disparou duas vezes: não duplica listener
    
      // ---------------------------------------------------------------
      // Os dados do seu site, preenchidos pelo formulário da Etapa 1.
      // ---------------------------------------------------------------
    
      // Os formulários de LEAD do seu site.
      var FORMULARIOS = 'PREENCHA O FORMULÁRIO ACIMA';
    
      // O botão "Aceitar" do seu aviso de cookies. "" se não houver um estável.
      var BOTAO_ACEITAR = '';
    
      // true só se a pessoa aceitou analytics AGORA. Fechada por padrão: enquanto
      // esta regra devolver false, nenhum cookie é gravado.
      function consentiu() {
        try {
          return false;
        } catch (e) {
          return false; // storage bloqueado conta como "não consentiu"
        }
      }
    
      // A chave onde o seu banner guarda o aceite, para o aceite dado em OUTRA aba
      // valer também nesta. Cookiebot e OneTrust usam cookie e não emitem o sinal.
      var CHAVE_CONSENTIMENTO = '';
    
      // Preenchido só se os formulários estiverem em outro subdomínio. Depois de
      // instalado, não mude sem apagar o cookie antigo: passariam a existir dois
      // cookies de mesmo nome e o errado pode ser lido.
      var DOMINIO = '';
    
      // ---------------------------------------------------------------
      // Daqui para baixo é igual para todos os sites.
      // ---------------------------------------------------------------
    
      var ORIGEM_DECORADO = "https://match-b2b.arquitetodebolso.com.br";
      var COOKIE = "upik_abclid";
      var NOVENTA_DIAS = 60 * 60 * 24 * 90;
      var CAMPO = "abclid";
      var FORMATO = /^abc_[0-9a-f]{32}$/;
      var LEITOR = new RegExp("(?:^|;\\s*)" + COOKIE + "=(abc_[0-9a-f]{32})(?:;|$)");
    
      // Seletor vazio ou malformado faz `querySelectorAll` e `closest` LANÇAREM.
      // Como isto roda em todo clique da página, a exceção apareceria em cada
      // clique do site inteiro. Protegido: um seletor errado custa a medição, e não
      // o funcionamento do site.
      function buscar(seletor) {
        if (!seletor) return [];
        try {
          return document.querySelectorAll(seletor);
        } catch (e) {
          return [];
        }
      }
    
      function maisProximo(elemento, seletor) {
        if (!seletor || !elemento || !elemento.closest) return null;
        try {
          return elemento.closest(seletor);
        } catch (e) {
          return null;
        }
      }
    
      function lerCookie() {
        var m = document.cookie.match(LEITOR);
        return m ? m[1] : "";
      }
    
      function gravarCookie(valor, validade) {
        document.cookie = COOKIE + "=" + valor + ";path=/;max-age=" + validade +
          (DOMINIO ? ";domain=" + DOMINIO : "") +
          ";SameSite=Lax" + (location.protocol === "https:" ? ";Secure" : "");
      }
    
      // Apaga nas DUAS formas, com e sem `domain`: o cookie só é removido pelo par
      // (domínio, caminho) com que foi gravado. Se o domínio mudou depois da
      // instalação, apagar só um deixaria o outro ressuscitar o identificador.
      function apagarCookie() {
        document.cookie = COOKIE + "=;path=/;max-age=0;SameSite=Lax";
        if (DOMINIO) {
          document.cookie = COOKIE + "=;path=/;max-age=0;domain=" + DOMINIO + ";SameSite=Lax";
        }
      }
    
      function novoAbclid() {
        var c = window.crypto || window.msCrypto;
        if (!c || !c.getRandomValues) return ""; // sem gerador seguro, não inventa um
        var bytes = new Uint8Array(16);
        c.getRandomValues(bytes);
        var hex = "";
        for (var i = 0; i < bytes.length; i++) {
          hex += (bytes[i] < 16 ? "0" : "") + bytes[i].toString(16);
        }
        return "abc_" + hex;
      }
    
      // Cópia desta página: se o navegador bloquear o cookie, o valor ainda vale
      // até o fim dela, e é o MESMO para o decorado e para o formulário.
      var memoria = "";
      var renovado = false;
    
      function atual() {
        if (!consentiu()) {
          // Sem aceite, nem o de uma visita anterior vale: o aceite pode ter sumido
          // com o cookie ainda lá. Apaga, em vez de deixá-lo pronto para voltar.
          var tinha = memoria || lerCookie();
          memoria = "";
          renovado = false;
          if (lerCookie()) apagarCookie();
          // Limpa também o que já tiver sido posto nos formulários: revogar não
          // dispara clique nenhum, e sem isto o campo oculto sairia com o valor
          // velho se o tema enviar por `form.submit()` ou por um botão de fora.
          if (tinha) limparCampos();
          return "";
        }
        var valor = lerCookie() || memoria || novoAbclid();
        if (!valor) return "";
        memoria = valor;
        if (!renovado || lerCookie() !== valor) {
          gravarCookie(valor, NOVENTA_DIAS);
          renovado = true;
        }
        return valor;
      }
    
      window.__upikAbclid = { atual: atual };
    
      function mensagem(valor) {
        return { type: "upik:parent-journey", v: 1, abclid: valor };
      }
    
      function ehIframeDaPagina(janela) {
        var iframes = document.getElementsByTagName("iframe");
        for (var i = 0; i < iframes.length; i++) {
          if (iframes[i].contentWindow === janela) return true;
        }
        return false;
      }
    
      // O decorado pergunta ("upik:viewer-ready") e a página responde. Só para a
      // origem do decorado, e só para um iframe desta página.
      window.addEventListener("message", function (e) {
        if (e.origin !== ORIGEM_DECORADO) return;
        var d = e.data;
        if (!d || d.type !== "upik:viewer-ready" || d.v !== 1) return;
        if (!e.source || !ehIframeDaPagina(e.source)) return;
        var valor = atual();
        if (!FORMATO.test(valor)) return; // sem consentimento: não responde nada
        e.source.postMessage(mensagem(valor), ORIGEM_DECORADO);
      });
    
      // Para quando a página fica pronta DEPOIS de o decorado parar de perguntar, ou
      // quando a pessoa aceita os cookies no meio da visita.
      var ultimoEntregue = "";
    
      function entregarAosDecorados() {
        var valor = atual();
        if (!FORMATO.test(valor)) return;
        // Não repete a mesma entrega: vários caminhos chamam esta função. Um iframe
        // que apareça depois continua coberto, porque ele pergunta ao carregar.
        if (valor === ultimoEntregue) return;
        ultimoEntregue = valor;
        var iframes = document.getElementsByTagName("iframe");
        for (var i = 0; i < iframes.length; i++) {
          var src = iframes[i].getAttribute("src") || "";
          if (src.indexOf(ORIGEM_DECORADO + "/") !== 0) continue;
          try {
            iframes[i].contentWindow.postMessage(mensagem(valor), ORIGEM_DECORADO);
          } catch (err) {
            // iframe ainda sem janela: ele vai perguntar quando carregar
          }
        }
      }
    
      // O campo oculto nos formulários de lead.
      function preencher(form) {
        var valor = atual();
        var campo = form.querySelector('input[name="' + CAMPO + '"]');
        if (!valor) {
          if (campo) campo.value = "";
          return;
        }
        if (!campo) {
          campo = document.createElement("input");
          campo.type = "hidden";
          campo.name = CAMPO;
          form.appendChild(campo);
        }
        campo.value = valor;
      }
    
      function preencherTodos() {
        var forms = buscar(FORMULARIOS);
        for (var i = 0; i < forms.length; i++) preencher(forms[i]);
      }
    
      // Esvazia os campos sem consultar `atual()`, porque é chamada de dentro dele,
      // no caminho da revogação.
      function limparCampos() {
        var forms = buscar(FORMULARIOS);
        for (var i = 0; i < forms.length; i++) {
          var campo = forms[i].querySelector('input[name="' + CAMPO + '"]');
          if (campo) campo.value = "";
        }
      }
    
      // Muitos temas não disparam um submit nativo: um handler de clique valida e
      // envia por ajax, serializando os campos. Por isso o campo é atualizado no
      // CLIQUE, em fase de captura, antes do handler do botão.
      function aoInteragir(e) {
        var form = maisProximo(e.target, FORMULARIOS);
        if (form) preencher(form);
      }
      document.addEventListener("click", aoInteragir, true);
      document.addEventListener("submit", aoInteragir, true);
    
      // A pessoa clicou em "Aceitar": quando o clique chega aqui, no documento, o
      // handler do próprio botão já gravou o consentimento.
      if (BOTAO_ACEITAR) {
        document.addEventListener("click", function (e) {
          if (maisProximo(e.target, BOTAO_ACEITAR)) {
            preencherTodos();
            entregarAosDecorados();
          }
        });
      }
      // O aceite dado em OUTRA aba. Mudança de cookie não emite `storage`, então
      // para Cookiebot e OneTrust este caminho não existe. O filtro pela chave
      // evita disparar à toa a cada escrita de qualquer outro script do site.
      if (CHAVE_CONSENTIMENTO) {
        window.addEventListener("storage", function (e) {
          if (e.key !== CHAVE_CONSENTIMENTO) return;
          preencherTodos();
          entregarAosDecorados();
        });
      }
    
      // Rede de segurança para o aceite TARDIO, e o único caminho que não depende
      // de configuração: o decorado só pergunta nos primeiros segundos. Cobre o
      // site sem seletor de botão e a ferramenta que grava o consentimento depois
      // do clique. Os três minutos contam do carregamento, não do aceite.
      var vigia = setInterval(function () {
        // Só age COM aceite: sem esta guarda, um `false` passageiro (ferramenta de
        // consentimento ainda carregando) apagaria cookie e campos a cada segundo.
        // A revogação continua valendo pelos eventos: banner, outra aba e o envio.
        if (!consentiu()) return;
        if (!FORMATO.test(atual())) return;
        // Encerra ANTES do trabalho: exceção em callback de `setInterval` não
        // cancela o intervalo, e o trabalho que falhasse se repetiria por 3 minutos.
        clearInterval(vigia);
        preencherTodos();
        entregarAosDecorados();
      }, 1000);
      setTimeout(function () { clearInterval(vigia); }, 180000);
    
      // A tag roda na Inicialização, antes de os formulários existirem.
      if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", preencherTodos);
      } else {
        preencherTodos();
      }
      // A entrega espontânea espera os iframes carregarem. Se o decorado ainda não
      // estiver ouvindo, ele pergunta de novo (1, 3 e 7 s).
      if (document.readyState === "complete") {
        entregarAosDecorados();
      } else {
        if (document.readyState === "interactive") entregarAosDecorados();
        window.addEventListener("load", entregarAosDecorados);
      }
    })();
    </script>

  3. Fazer o campo chegar ao CRM

    A tag acrescenta um campo oculto abclid nos formulários de lead, e ele já vai no envio. Falta o sistema que recebe o formulário repassá-lo ao CRM, o que costuma ser ajuste do fornecedor do site.

    O que pedir a ele: gravar o campo abclid num campo do lead no CRM, normalmente um campo personalizado, em todos os formulários de lead.

    Se o seu grupo também usa o Meu Decorado no formato de incorporadora, avise a gente antes de seguir: os dois formatos pedem um campo de mesmo nome e precisam ser separados, senão um sobrescreve o outro.

    Sem esta etapa, o identificador fica no navegador e nenhum lead se liga ao decorado. A tag sozinha não produz resultado visível no CRM.

  4. Conferir e publicar

    No modo de visualização do Gerenciador de Tags, numa página de imóvel em produção. Sem ele a tag ainda não existe para o navegador, e ele não afeta quem está navegando. Os comandos vão no console.

    • Antes de aceitar os cookies, numa janela anônima ou com os cookies do site limpos, senão este primeiro teste já começa verdadeiro: document.cookie.includes("upik_abclid") deve dar false
    • Depois de aceitar, sem recarregar: o mesmo comando dá true, e window.__upikAbclid.atual() mostra um valor começando com abc_. Não recarregue: é o aceite no meio da visita que exercita o seletor do botão
    • Recarregue: o valor tem que ser exatamente o mesmo. Valor diferente a cada carga significa cookie recusado, quase sempre pelo domínio do cookie preenchido errado
    • No formulário de contato: existe um campo oculto abclid com esse valor, e ele é filho do <form>, não de uma caixa em volta. Se estiver fora, o envio não o leva
    • Num formulário que não é de lead, como currículo: não existe campo abclid. É o teste que pega um seletor largo demais
    • No CRM: envie um lead de teste e veja o campo chegar. Avise o time comercial antes, é um lead real
    • Publique a versão no Gerenciador de Tags. Até publicar, a tag existe só no modo de visualização e nada é medido para quem navega no site
    • Avise a gente que publicou. A última conferência é nossa: confirmamos que o identificador chega junto com o uso do decorado. Daí não dá, porque o decorado roda em outro endereço. Sem ela, todos os itens acima passam e a ligação pode estar zerada

    Sem o botão de aceitar configurado, a tag ainda tenta entregar o identificador por cerca de três minutos após a página carregar. Quem aceita depois disso só é coberto na página seguinte. Para conferir nesse caso, aceite os cookies e recarregue.

O que esperar