Visão geral
INFORMAÇÃO: A atribuição web é uma funcionalidade empresarial. Contacte o seu Gestor de Sucesso do Cliente para ativar esta funcionalidade na sua conta.
Este guia orienta a implementação do Singular WebSDK usando JavaScript nativo. Esse método oferece o rastreamento mais confiável e é recomendado para a maioria das implementações, pois não é bloqueado por bloqueadores de anúncios comuns.
IMPORTANTE!
- Não implemente os métodos JavaScript nativo e Google Tag Manager. Escolha apenas um para evitar rastreamento duplicado.
- O Singular WebSDK foi projetado para ser executado no lado do cliente, no navegador do usuário. Ele requer acesso a recursos do navegador, como localStorage e Document Object Model (DOM), para funcionar corretamente. Não tente executar o SDK no lado do servidor (por exemplo, via Next.js SSR ou node.js) — isso causará falhas no rastreamento, pois os ambientes de servidor não fornecem acesso às APIs do navegador.
Pré-requisitos
Antes de começar, certifique-se de ter:
-
Chave SDK e Segredo SDK:
- Onde encontrá-los: Faça login na sua conta Singular e navegue até Ferramentas do desenvolvedor > Integração do SDK > Chaves do SDK.
-
ID do produto:
-
O que é: um nome exclusivo para o seu site, idealmente usando o formato DNS reverso (por exemplo,
com.website-name). - Por que é importante: esse ID associa o site como um aplicativo no Singular e deve corresponder ao bundleID do aplicativo web listado na sua página de aplicativos no Singular.
-
O que é: um nome exclusivo para o seu site, idealmente usando o formato DNS reverso (por exemplo,
- Permissão para editar o código HTML do seu site.
- Acesso para adicionar JavaScript na seção
<head>das suas páginas. - Uma lista de eventos que você deseja rastrear. Veja nossos Eventos Padrão do Singular: Lista Completa e Eventos Recomendados por Vertical para obter ideias.
Etapas de implementação
Etapa 1: Adicione o script da biblioteca SDK
Adicione o seguinte trecho de código à seção <head> de todas as páginas do seu site. Coloque-o o mais cedo possível, de preferência próximo ao topo da tag <head>.
DICA! Adicionar o script logo no início garante que a biblioteca JavaScript do Singular esteja disponível para qualquer função do Singular no código-fonte da sua página.
<script src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"></script>
Especifique a versão específica no caminho da biblioteca WebSDK JavaScript, por exemplo: 1.4.8 é indicado aqui:
<script src="https://web-sdk-cdn.singular.net/singular-sdk/1.4.8/singular-sdk.js"></script>
-
Execute
npm i singular-sdkno diretório raiz do seu projeto , ou adicione"singular-sdk": "^1.4.8"à seção de dependências no seu arquivo package.json e, em seguida, executenpm install. -
Adicione o seguinte código nos scripts que você deseja usar o SDK.
import {singularSdk, SingularConfig} from "singular-sdk";
Usando com Next.js / React
Next.js é uma estrutura React que fornece renderização do lado do servidor (SSR), geração de sites estáticos (SSG) e Componentes do Servidor React. Como o Singular WebSDK requer APIs do navegador (DOM, localStorage, cookies), você deve carregá-lo apenas no lado do cliente.
IMPORTANTE: Nunca carregue o Singular SDK em código do lado do servidor
(por exemplo, getServerSideProps, React Server Components ou
ambientes Node.js). Isso causará erros porque as APIs do navegador
não estão disponíveis no servidor.
Método 1: Usando o componente de script Next.js (recomendado)
O componente <Script> do Next.js fornece estratégias de carregamento otimizadas
e evita a injeção de scripts duplicados em
mudanças de rota.
// pages/_app.tsx (Pages Router) or app/layout.tsx (App Router)
import Script from 'next/script'
export default function App({ Component, pageProps }) {
return (
<>
<Script
src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"
strategy="afterInteractive"
/>
<Component {...pageProps} />
</>
)
}
Opções de estratégia de script:
-
afterInteractive(recomendado): Carrega depois que a página se torna interativa - ideal para análises e rastreamento. -
lazyOnload: Carrega durante o tempo ocioso do navegador - use para widgets não críticos.
Método 2: Importação dinâmica para carregamento em nível de componente
Para controle no nível do componente, use importações dinâmicas do Next.js com
ssr: false para carregar o script somente quando necessário:
// pages/index.tsx
import dynamic from 'next/dynamic'
const SingularSDKLoader = dynamic(
() => import('../components/SingularSDKLoader'),
{ ssr: false }
)
export default function Page() {
return (
<div>
<SingularSDKLoader />
<h1>Your Page Content</h1>
</div>
)
}
// components/SingularSDKLoader.tsx
'use client' // Required for Next.js App Router
import { useEffect } from 'react'
export default function SingularSDKLoader() {
useEffect(() => {
// Load Singular SDK script dynamically
const script = document.createElement('script')
script.src = 'https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js'
script.async = true
document.body.appendChild(script)
// Cleanup on unmount
return () => {
if (document.body.contains(script)) {
document.body.removeChild(script)
}
}
}, [])
return null
}
Configuração de variáveis de ambiente
DICA! Armazene suas credenciais Singular em variáveis de ambiente
prefixadas com NEXT_PUBLIC_ para torná-las
disponíveis no navegador:
# .env.local
NEXT_PUBLIC_SINGULAR_SDK_KEY=your_sdk_key_here
NEXT_PUBLIC_SINGULAR_SDK_SECRET=your_sdk_secret_here
NEXT_PUBLIC_SINGULAR_PRODUCT_ID=com.your-website
Essas variáveis de ambiente estarão acessíveis no seu código do lado do cliente e podem ser usadas durante a inicialização na Etapa 2.
Roteador de aplicativos vs roteador de páginas
| Tipo de roteador | Principais diferenças | Localização do script |
|---|---|---|
| Roteador de aplicativos (Next.js 13+) |
Os componentes são componentes do servidor por padrão. Adicione
a diretiva 'use client' para APIs do navegador.
|
app/layout.tsx
|
| Roteador de páginas (Next.js 12 e versões anteriores) | Todos os componentes são componentes do cliente por padrão. |
pages/_app.tsx
|
Etapa 2: Inicialize o SDK
- Sempre inicialize o SDK cada vez que uma página for carregada no navegador.
- A inicialização é necessária para todos os recursos de atribuição e rastreamento de eventos do Singular.
- A inicialização aciona um
__PAGE_VISIT__evento. - O
__PAGE_VISIT__evento é usado para gerar uma nova sessão no lado do servidor quando as seguintes condições são atendidas:- O usuário chega com novos dados de publicidade na URL (como parâmetros UTM ou WP) ou
- A sessão anterior expirou (após 30 minutos de inatividade).
- As sessões são usadas para medir a retenção de usuários e apoiar a atribuição de reengajamento.
- Crie uma função de inicialização e chame essa função no DOM Ready após o carregamento da página.
- Certifique-se de que a inicialização ocorra antes que quaisquer outros eventos Singular sejam relatados.
- Para aplicativos de página única (SPA), inicialize o SDK Singular no primeiro carregamento da página e, em seguida, chame a função Singular Page Visist
window.singularSdk.pageVisit()em cada mudança de rota que represente uma nova visualização de página.
Inicialização básica DOM-Ready
Adicione um ouvinte de eventos para chamar o initSingularSDK()
em DOMContentLoaded
/**
* Initializes the Singular SDK with the provided configuration.
* @param {string} sdkKey - The SDK key for Singular.
* @param {string} sdkSecret - The SDK secret for Singular.
* @param {string} productId - The product ID for Singular.
* @example
* initSingularSDK(); // Initializes SDK with default config
*/
function initSingularSDK() {
var config = new SingularConfig('sdkKey', 'sdkSecret', 'productId');
window.singularSdk.init(config);
}
/**
* Triggers Singular SDK initialization when the DOM is fully parsed.
*/
document.addEventListener('DOMContentLoaded', function() {
initSingularSDK();
});
Inicialização básica com propriedades globais
Como o SDK Singular não está inicializado, você deve implementar
a função personalizada para definir as propriedades globais no
localstorage do navegador.
Consulte a função personalizada setGlobalPropertyBeforeInit().
Depois de implementada, você pode definir as propriedades conforme descrito abaixo antes da
inicialização do SDK.
/**
* Initializes the Singular SDK with the provided configuration.
* @param {string} sdkKey - The SDK key for Singular.
* @param {string} sdkSecret - The SDK secret for Singular.
* @param {string} productId - The product ID for Singular.
* @example
* initSingularSDK(); // Initializes SDK with default config
*/
function initSingularSDK() {
var sdkKey = 'sdkKey';
var sdkSecret = 'sdkSecret';
var productId = 'productId';
// Set global properties before SDK initialization
setGlobalPropertyBeforeInit(sdkKey, productId, 'global_prop_1', 'test', false);
setGlobalPropertyBeforeInit(sdkKey, productId, 'global_prop_2', 'US', true);
// Initialize SDK
var config = new SingularConfig('sdkKey', 'sdkSecret', 'productId');
window.singularSdk.init(config);
}
/**
* Triggers Singular SDK initialization when the DOM is fully parsed.
*/
document.addEventListener('DOMContentLoaded', function() {
initSingularSDK();
});
Inicialização básica com roteamento de aplicativo de página única (SPA)
| Cenário | O que fazer |
|---|---|
|
Primeiro carregamento da página |
Chame |
|
Navegando para uma nova rota/página |
Chame |
|
No carregamento inicial no SPA |
Não chame |
Exemplo para SPAs (React)
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
/**
* Initializes the Singular SDK with the provided configuration.
* @param {string} sdkKey - The SDK key for Singular.
* @param {string} sdkSecret - The SDK secret for Singular.
* @param {string} productId - The product ID for Singular.
* @example
* // Initialize the Singular SDK
* initSingularSDK();
*/
function initSingularSDK() {
var config = new SingularConfig('sdkKey', 'sdkSecret', 'productId');
window.singularSdk.init(config);
}
/**
* Tracks a page visit event with the Singular SDK on route changes.
* @example
* // Track a page visit
* trackPageVisit();
*/
function trackPageVisit() {
window.singularSdk.pageVisit();
}
/**
* A React component that initializes the Singular SDK on mount and tracks page visits on route changes.
* @returns {JSX.Element} The component rendering the SPA content.
* @example
* // Use in a React SPA with react-router-dom
* function App() {
* const location = useLocation();
* useEffect(() => {
* initSingularSDK();
* }, []);
* useEffect(() => {
* trackPageVisit();
* }, [location.pathname]);
* return <div>Your SPA Content</div>;
* }
*/
function App() {
const location = useLocation();
useEffect(() => {
initSingularSDK();
}, []); // Run once on mount for SDK initialization
useEffect(() => {
trackPageVisit();
}, [location.pathname]); // Run on route changes for page visits
return (
<div>Your SPA Content</div>
);
}
export default App;
Inicialização básica usando um callback de inicialização
Se você precisar executar o código após o SDK estar pronto (por exemplo, para obter
o Singular
Device ID), defina um callback com .withInitFinishedCallback():
/**
* Initializes the Singular SDK with a callback to handle initialization completion.
* @param {string} sdkKey - The SDK key for Singular.
* @param {string} sdkSecret - The SDK secret for Singular.
* @param {string} productId - The product ID for Singular.
* @example
* initSingularSDK(); // Initializes SDK and logs device ID
*/
function initSingularSDK() {
var config = new SingularConfig('sdkKey', 'sdkSecret', 'productId')
.withInitFinishedCallback(function(initParams) {
var singularDeviceId = initParams.singularDeviceId;
// Example: Store device ID for analytics
console.log('Singular Device ID:', singularDeviceId);
// Optionally store in localStorage or use for event tracking
// localStorage.setItem('singularDeviceId', singularDeviceId);
});
window.singularSdk.init(config);
}
/**
* Triggers Singular SDK initialization when the DOM is fully parsed.
*/
document.addEventListener('DOMContentLoaded', function() {
initSingularSDK();
});
Inicialização Next.js / React
Após carregar o script do SDK na Etapa 1, inicialize-o quando o DOM estiver pronto. A inicialização deve ocorrer no lado do cliente após o script ter sido carregado.
Declarações de tipo TypeScript
Crie um arquivo de declaração de tipo para adicionar suporte a TypeScript para o Singular SDK:
// types/singular.d.ts
interface SingularConfig {
new (sdkKey: string, sdkSecret: string, productId: string): SingularConfig;
withCustomUserId(userId: string): SingularConfig;
withAutoPersistentSingularDeviceId(domain: string): SingularConfig;
withLogLevel(level: number): SingularConfig;
withSessionTimeoutInMinutes(timeout: number): SingularConfig;
withProductName(productName: string): SingularConfig;
withPersistentSingularDeviceId(singularDeviceId: string): SingularConfig;
withInitFinishedCallback(callback: (params: { singularDeviceId: string }) => void): SingularConfig;
}
interface SingularSDK {
init(config: SingularConfig): void;
event(eventName: string, attributes?: Record<string, any>): void;
conversionEvent(eventName: string): void;
revenue(eventName: string, currency: string, amount: number, attributes?: Record<string, any>): void;
login(userId: string): void;
logout(): void;
pageVisit(): void;
buildWebToAppLink(baseLink: string): string;
getSingularDeviceId(): string;
setGlobalProperties(key: string, value: string, override: boolean): void;
getGlobalProperties(): Record<string, string>;
clearGlobalProperties(): void;
}
interface Window {
singularSdk: SingularSDK;
SingularConfig: typeof SingularConfig;
}
Inicialização básica com o componente de script Next.js
Ao usar o componente Script da Etapa 1, adicione a chamada de retorno onLoad
para inicializar o SDK:
// pages/_app.tsx or app/layout.tsx
import Script from 'next/script'
export default function App({ Component, pageProps }) {
return (
<>
<Script
src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"
strategy="afterInteractive"
onLoad={() => {
// Initialize Singular SDK after script loads
const config = new (window as any).SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID
);
window.singularSdk.init(config);
}}
/>
<Component {...pageProps} />
</>
)
}
Inicialização com o hook useEffect
Se você estiver usando o método de importação dinâmica ou precisar de mais controle, inicialize
usando useEffect:
// components/SingularInitializer.tsx
'use client'
import { useEffect } from 'react'
export default function SingularInitializer() {
useEffect(() => {
// Wait for script to load, then initialize
const checkAndInit = () => {
if (window.singularSdk && window.SingularConfig) {
const config = new window.SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY!,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET!,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID!
);
window.singularSdk.init(config);
} else {
// Retry if SDK not loaded yet
setTimeout(checkAndInit, 100);
}
};
checkAndInit();
}, []); // Empty dependency - run once on mount
return null;
}
Inicialização com opções de configuração
Encadeie métodos .with para habilitar recursos adicionais, como
rastreamento entre subdomínios ou IDs de usuário personalizados:
const config = new window.SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY!,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET!,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID!
)
.withAutoPersistentSingularDeviceId('your-domain.com')
.withCustomUserId(userId)
.withLogLevel(2); // 0=None, 1=Warn, 2=Info, 3=Debug
window.singularSdk.init(config);
Roteamento de aplicativo de página única (SPA)
Para aplicativos Next.js com roteamento do lado do cliente, você precisa rastrear as alterações de rota separadamente da inicialização inicial.
IMPORTANTE: Inicialize o SDK apenas uma vez na primeira
carregada da página. Em seguida, chame window.singularSdk.pageVisit()
em cada alteração de rota subsequente.
// components/SingularPageTracker.tsx
'use client'
import { useEffect, useRef } from 'react'
import { usePathname } from 'next/navigation' // App Router
// or import { useRouter } from 'next/router' // Pages Router
export default function SingularPageTracker() {
const pathname = usePathname() // App Router
// const router = useRouter(); const pathname = router.pathname // Pages Router
const isInitialized = useRef(false);
// Initialize SDK once on mount
useEffect(() => {
if (!isInitialized.current && window.singularSdk && window.SingularConfig) {
const config = new window.SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY!,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET!,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID!
);
window.singularSdk.init(config);
isInitialized.current = true;
}
}, []); // Empty dependency - runs once
// Track page visits on route changes (NOT on initial mount)
useEffect(() => {
if (isInitialized.current && window.singularSdk) {
window.singularSdk.pageVisit();
}
}, [pathname]); // Runs when pathname changes
return null;
}
Usando uma chamada de retorno de inicialização
Se você precisar executar código após o SDK estar pronto (por exemplo, para obter
o ID do dispositivo Singular), use .withInitFinishedCallback():
const config = new window.SingularConfig(
process.env.NEXT_PUBLIC_SINGULAR_SDK_KEY!,
process.env.NEXT_PUBLIC_SINGULAR_SDK_SECRET!,
process.env.NEXT_PUBLIC_SINGULAR_PRODUCT_ID!
)
.withInitFinishedCallback((initParams) => {
const singularDeviceId = initParams.singularDeviceId;
console.log('Singular Device ID:', singularDeviceId);
// Store for analytics or pass to other services
});
window.singularSdk.init(config);
Melhores práticas para inicialização do Next.js
- Inicialize o SDK apenas uma vez na montagem do aplicativo, não em cada mudança de rota.
-
Use variáveis de ambiente com o prefixo
NEXT_PUBLIC_para credenciais do SDK. -
Para SPAs, chame
window.singularSdk.pageVisit()nas mudanças de rota para rastrear a navegação. -
Use
useRefpara rastrear o estado de inicialização e evitar inicializações duplicadas. -
Adicione verificações nulas para
window.singularSdkantes de chamar métodos para evitar erros durante o SSR. - Teste no modo de desenvolvimento para garantir que o SDK seja inicializado corretamente sem erros do lado do servidor.
Lista de verificação do desenvolvedor para Next.js
- Verifique se o script foi carregado com sucesso na Etapa 1.
- Configure variáveis de ambiente com suas credenciais do SDK.
- Crie declarações de tipo TypeScript para uma melhor experiência do desenvolvedor .
-
Escolha o método de inicialização: componente de script
onLoadou hookuseEffect. -
Para SPAs, implemente o rastreamento de alterações de rota com
pageVisit(). -
Teste a inicialização verificando o console para o
evento
__PAGE_VISIT__.
-
Substitua
'sdkKey'pela sua chave SDK real. -
Substitua
'sdkSecret'pelo seu segredo SDK real. -
Substitua
'productId'pelo seu ID do produto real. Deve ficar assim:com.website-namee deve corresponder ao valor BundleID na página Apps na plataforma Singular.
Opções de configuração
Aprimore a configuração do WebSDK encadeando métodos .with para habilitar recursos extras.
Por exemplo, para oferecer suporte ao rastreamento entre subdomínios, mantendo o Singular Device ID (SDID) em um cookie ou para incluir um ID de usuário personalizado para um visitante que retorna ao site com um estado de login ativo:
var domain = 'website-name.com';
var config = new SingularConfig('sdkKey','sdkSecret','productId')
.withAutoPersistentSingularDeviceId(domain)
.withCustomUserId(userId);
Referência do método SingularConfig
Aqui estão todos os métodos ".with" disponíveis.
| Método | Descrição | Saiba mais |
.withCustomUserId(customId)
|
Enviar o ID do usuário para o Singular | Definir o ID do usuário |
.withProductName(productName)
|
Um nome de exibição opcional para o produto | |
.withLogLevel(logLevel)
|
Configure o nível de registro: 0 - Nenhum (padrão); 1 - Aviso; 2 - Informação; 3 - Depuração. | |
.withSessionTimeoutInMinutes(timeout)
|
Defina o tempo limite da sessão em minutos (padrão: 30 minutos) |
|
.withAutoPersistentSingularDeviceId(domain)
|
Habilitar rastreamento automático entre subdomínios | Persistência automática usando cookies |
|
|
Ativar rastreamento manual entre subdomínios | Definir manualmente o ID único do dispositivo |
.withInitFinishedCallback(callback)
|
Chamar um callback quando a inicialização do SDK estiver concluída | Chamar uma função de retorno de chamada quando a inicialização estiver concluída |
.withGlobalProperties(globalProperties, false)
|
Definir propriedades globais durante a inicialização; o segundo parâmetro
é overrideExisting (booleano).
Use false para mesclar com as propriedades existentes,
true para sobrescrever as propriedades existentes.
|
Propriedades globais |
.withEventsDedupEnabled()
|
Habilite a deduplicação opcional de eventos para reduzir a exportação de eventos duplicados (por exemplo, gatilhos repetidos disparados em um curto intervalo) | Deduplicação de eventos |
.withTimeBetweenEvents(timeBetweenEvents)
|
Defina a janela de tempo máxima (ms) para deduplicação (padrão: 1000 ms / 1 segundo) | Deduplicação de eventos |
Lista de verificação do desenvolvedor
- Reúna suas credenciais SDK e ID do produto.
- Decida se você precisa de configurações personalizadas (ID do usuário, tempo limite, etc.).
- Crie uma função de inicialização Singular e um objeto SingularConfig usando os exemplos acima.
- Sempre teste para garantir que a inicialização seja acionada apenas uma vez ao carregar a página.
DICA! Para configurações avançadas, como rastreamento de pesquisa orgânica , talvez seja necessário implementar JavaScript personalizado — por exemplo, para ajustar parâmetros de consulta — antes que o SDK Singular seja inicializado. Certifique-se de que seu código personalizado seja executado antes da função de inicialização Singular para que as alterações sejam capturadas corretamente. Mais detalhes sobre como implementar o rastreamento de pesquisa orgânica podem ser encontrados aqui.
Etapa 3: Rastreamento de eventos
Após inicializar o SDK, você pode rastrear eventos personalizados quando os usuários realizam ações importantes em seu site.
IMPORTANTE! O Singular não bloqueia eventos duplicados! É responsabilidade dos desenvolvedores adicionar proteções contra atualizações ou duplicações de página. Recomenda-se incorporar algum método de deduplicação específico para eventos de receita, a fim de evitar dados de receita errôneos. Veja "Etapa 5: Prevenção de eventos duplicados" abaixo para um exemplo.
Rastreamento básico de eventos
Rastreie um evento simples ou adicione atributos personalizados usando JSON válido para fornecer mais contexto sobre o evento:
// Basic event tracking
var eventName = 'page_view';
window.singularSdk.event(eventName);
// Optional: With attributes for more context
var eventName = 'sng_content_view';
var attributes = {
key1: 'value1', // First custom attribute
key2: 'value2' // Second custom attribute
};
window.singularSdk.event(eventName, attributes);
Rastreamento de eventos de conversão
Rastreie um evento de conversão:
var eventName = 'sng_complete_registration';
window.singularSdk.conversionEvent(eventName);
Rastreamento de eventos de receita
Rastreie eventos de conversão com informações de receita e adicione atributos opcionais para obter mais contexto:
- Passe a moeda da receita como um código de moeda ISO 4217 de três letras, como “USD”, “EUR” ou “INR”.
- Passe o valor da receita como um valor decimal.
// Revenue event tracking
var revenueEventName = 'sng_ecommerce_purchase';
var revenueCurrency = 'USD';
var revenueAmount = 49.99;
window.singularSdk.revenue(revenueEventName, revenueCurrency, revenueAmount);
// Optional: With attributes for more context
var revenueEventName = 'sng_ecommerce_purchase';
var revenueCurrency = 'USD';
var revenueAmount = 49.99;
var attributes = {
key1: 'value1', // First custom attribute
key2: 'value2' // Second custom attribute
};
window.singularSdk.revenue(revenueEventName, revenueCurrency, revenueAmount, attributes);
Padrões comuns de implementação de eventos
Eventos de carregamento de página
O mecanismo de rastreamento de eventos de carregamento de página usa JavaScript para monitorar
quando uma página da web é totalmente carregada e, em seguida, aciona um evento de análise usando
o SDK Singular. Especificamente, ele utiliza o
método window.addEventListener('load', ...) para detectar
quando todos os recursos da página (por exemplo, HTML, imagens, scripts) foram carregados.
Após esse evento, a função event do SDK Singular é
chamada para registrar um evento personalizado com atributos associados, permitindo
o rastreamento das interações do usuário para fins de análise, como monitorar
visualizações de página ou ações do usuário, como registros. O mecanismo é comumente
usado em análises da web para capturar dados sobre o comportamento do usuário, como
visitas à página ou ações específicas, com atributos personalizáveis para
insights detalhados.
O mecanismo pode ser adaptado para rastrear um evento específico (por exemplo, um
evento page_view ) com atributos personalizados, conforme mostrado
abaixo. A estrutura do código permanece limpa e modular, com o nome do evento
e os atributos definidos separadamente para maior clareza e facilidade de manutenção.
/**
* Tracks a registration event with the Singular SDK when the page fully loads.
* @param {string} eventName - The name of the event to track (e.g., 'page_view').
* @param {Object} attributes - A JSON object with custom event attributes.
* @param {string} attributes.key1 - First custom attribute (e.g., 'value1').
* @param {string} attributes.key2 - Second custom attribute (e.g., 'value2').
*/
window.addEventListener('load', function() {
var eventName = 'page_view';
var attributes = {
key1: 'value1', // First custom attribute
key2: 'value2' // Second custom attribute
};
window.singularSdk.event(eventName, attributes);
});
Eventos de clique no botão
O mecanismo de rastreamento de eventos de clique em botões usa JavaScript para monitorar
quando um usuário clica em um botão com o ID checkout-button
e aciona um evento checkout_started usando o Singular
SDK para rastreamento analítico. O código emprega um ouvinte de eventos click
para detectar a interação com o botão, inclui um mecanismo
para evitar múltiplos acionamentos de eventos (usando um
atributo data-event-fired ) e envia o evento com
atributos personalizados (cart_value e currency)
para a plataforma Singular. Isso é ideal para análises de comércio eletrônico
para rastrear quando os usuários iniciam um processo de checkout, garantindo dados precisos
ao disparar o evento apenas uma vez por sessão da página.
/**
* Tracks a checkout started event with the Singular SDK when a button is clicked.
* @param {string} eventName - The name of the event to track (e.g., 'checkout_started').
* @param {Object} attributes - A JSON object with custom event attributes.
* @param {number} attributes.cart_value - The total value of the cart (e.g., 99.99).
* @param {string} attributes.currency - The currency of the cart value (e.g., 'USD').
*/
document.getElementById('checkout-button').addEventListener('click', function(e) {
// Prevent multiple fires
if (this.hasAttribute('data-event-fired')) {
return;
}
this.setAttribute('data-event-fired', 'true');
var eventName = 'checkout_started';
var attributes = {
cart_value: 99.99, // Total value of the cart
currency: 'USD' // Currency of the cart value
};
window.singularSdk.event(eventName, attributes);
});
Eventos de envio de formulário
O mecanismo de rastreamento de eventos de envio de formulário usa JavaScript para monitorar
quando um usuário envia um formulário com o ID signup-form e
aciona um evento signup_completed usando o Singular
SDK para rastreamento analítico. O código emprega um submit
event listener para detectar o envio do formulário, inclui um mecanismo para
evitar múltiplos acionamentos de eventos (usando um data-event-fired
attribute) e envia o evento com um atributo personalizado (signup_method)
para a plataforma Singular. Isso é ideal para rastrear inscrições de usuários
em fluxos de trabalho de análise, como monitorar métodos de aquisição ou inscrição de usuários
, garantindo dados precisos ao disparar o evento apenas uma vez por
sessão da página.
Abaixo está um exemplo simples que adapta o código original para usar
window.singularSdk.event , separando o nome do evento
e os atributos para maior clareza.
/**
* Tracks a signup completed event with the Singular SDK when a form is submitted.
* @param {string} eventName - The name of the event to track (e.g., 'signup_completed').
* @param {Object} attributes - A JSON object with custom event attributes.
* @param {string} attributes.signup_method - The method used for signup (e.g., 'email').
* @example
* // Track a signup event with additional attributes
* var eventName = 'signup_completed';
* var attributes = {
* signup_method: 'email',
* email: document.getElementById('email')?.value || 'unknown'
* };
* window.singularSdk.event(eventName, attributes);
*/
document.getElementById('signup-form').addEventListener('submit', function(e) {
// Prevent multiple fires
if (this.hasAttribute('data-event-fired')) {
return;
}
this.setAttribute('data-event-fired', 'true');
var eventName = 'signup_completed';
var attributes = {
signup_method: 'email' // Method used for signup
};
window.singularSdk.event(eventName, attributes);
});
Etapa 4: Definir o ID do usuário do cliente
Você pode enviar sua ID de usuário interna para o Singular usando um método SDK do Singular.
NOTA: Se você usar a solução Cross-Device da Singular, deverá coletar a ID do usuário em todas as plataformas.
- A ID do usuário pode ser qualquer identificador e não deve expor PII (informações de identificação pessoal). Por exemplo, você não deve usar o endereço de e-mail, nome de usuário ou número de telefone de um usuário. A Singular recomenda o uso de um valor hash exclusivo apenas para seus dados primários .
- O valor do ID do usuário passado para o Singular também deve ser o mesmo ID interno do usuário que você captura em todas as plataformas (Web/Mobile/PC/Console/Offline).
- A Singular incluirá a ID do usuário em exportações no nível do usuário, ETL e postbacks internos de BI (se configurados). A ID do usuário é um dado primário, e a Singular não a compartilha com terceiros.
- O valor do ID do usuário, quando definido com o método Singular SDK, permanecerá até ser desativado usando o método logout() ou até que o armazenamento local do navegador seja excluído. Fechar ou atualizar o site não desativa o ID do usuário.
- No modo privado/incógnito, o SDK não pode manter a ID do usuário porque o navegador exclui automaticamente o armazenamento local quando é fechado.
Para definir a ID do usuário, use o método login().
Para
desativá-la (por exemplo, se o usuário "sair" da conta), chame o método
logout() .
NOTA: Se vários usuários usarem um único dispositivo, recomendamos implementar um fluxo de logout para definir e desativar o ID do usuário para cada login e logout.
Se você já souber o ID do usuário quando o SDK Singular for inicializado no
site,
defina o ID do usuário no objeto de configuração. Dessa forma, o Singular pode ter o ID do usuário
desde a primeira sessão. No entanto, o ID do usuário normalmente não está disponível até
o
O usuário se registra ou faz login. Nesse caso, chame login() após a conclusão do fluxo de registro.
DICA! Use o mesmo ID de usuário do cliente que você usa em seus SDKs móveis. Isso permite a atribuição entre dispositivos e fornece uma visão completa do comportamento do usuário em todas as plataformas.
Práticas recomendadas para a ID de usuário do cliente:
- Defina-a assim que o usuário fizer login ou se cadastrar
- Use a mesma ID nas plataformas web e móveis
- Não use informações de identificação pessoal (e-mail, número de telefone)
- Use sua ID de usuário interna ou ID de banco de dados
- Envie como uma string, mesmo que seja numérica:
// After user logs in or signs up
var userId = 'user_12345';
window.singularSdk.login(userId);
// After user logs out
window.singularSdk.logout();
Etapa 5: Desduplicação de eventos (opcional)
IMPORTANTE! Se o seu site puder disparar o mesmo gatilho mais de uma vez em um curto intervalo de tempo, você poderá ver eventos duplicados exportados. Habilite a deduplicação do SDK para suprimir automaticamente as duplicatas.
O SDK da Web da Singular oferece suporte à deduplicação opcional de eventos para reduzir exportações duplicadas causadas por gatilhos repetidos disparados em um curto intervalo de tempo. Quando ativado, o SDK descartará eventos duplicados que correspondam aos mesmos parâmetros de deduplicação dentro do intervalo de tempo configurado.
Como ativar
-
withEventsDedupEnabled: Ative a deduplicação de eventos (o padrão é desativado). -
withTimeBetweenEvents: Janela de tempo máxima (em milissegundos) usada para considerar dois eventos duplicados; o padrão é 1000 ms (1 segundo).
JavaScript
// Enable optional event deduplication (recommended when triggers may fire repeatedly)
var config = new SingularConfig("SDK_KEY", "SDK_SECRET", "PRODUCT_ID")
.withEventsDedupEnabled() // turns deduplication on
.withTimeBetweenEvents(1000); // dedup window in ms (default: 1000 = 1s)
window.singularSdk.init(config);
Como as duplicatas são detectadas
Quando a deduplicação está ativada, o SDK calcula um hash a partir dos campos-chave do evento e suprime repetições dentro da janela de tempo. Os parâmetros de deduplicação incluem: EventName, EventProductName, IsRevenueEvent, CustomUserId, GlobalProperties, MatchId e WebUrl (e o SDK também leva em consideração cargas "extras" adicionais do evento, como receita/args, ao fazer o hash).
Etapa 6: Testando sua implementação
Após implementar o SDK, verifique se ele está funcionando corretamente usando as ferramentas de desenvolvedor do seu navegador.
Verifique se o SDK foi carregado
- Abra seu site em um navegador
- Abra as Ferramentas de desenvolvedor (F12 ou clique com o botão direito → Inspecionar)
- Vá para a guia Console
- Digite
typeof singularSdke pressione Enter - Você deverá ver um objeto "função", não "indefinido"
Verifique as solicitações de rede
- Abra as Ferramentas do desenvolvedor (F12)
- Vá para a guia Rede
- Recarregue a página
- Filtre por
singularousdk-api - Procure por solicitações para
sdk-api-v1.singular.net - Clique em uma solicitação para ver os detalhes
- Verifique se o código de status é
200 - Verifique se a carga útil da solicitação contém o ID do seu produto. Ele estará no parâmetro "
i" da carga útil.
SUCESSO! Se você vir solicitações para sdk-api-v1.singular.net com o código de status 200, seu SDK está enviando dados para o Singular com sucesso.
Verifique os eventos
- Acione um evento no seu site (clique em um botão, envie um formulário etc.).
- Na guia Rede, procure uma nova solicitação para
sdk-api-v1.singular.net - Clique na solicitação e visualize a guia Carga útil ou Solicitação
-
Verifique se o nome do seu evento "
n" aparece na solicitação
Para testes de ponta a ponta, use o Console de Testes
Você pode testar a integração do Web SDK usando o Console SDK disponível no painel do Singular.
- Na plataforma Singular, vá para Ferramentas do desenvolvedor > Console de testes.
- Clique em Adicionar dispositivo.
- Selecione a plataforma "Web" e insira o ID do dispositivo Singular.
- Obtenha o ID do dispositivo Singular na carga útil do navegador. Verifique a captura de tela acima para localizar o ID do dispositivo Singular (
SDID) em um evento. - O console de teste deve permanecer ativo enquanto você testa em uma janela separada. O console não exibirá nenhum evento acionado enquanto estiver fechado ou offline.
- Depois que o SDID for adicionado, você poderá recarregar a página da web e acionar alguns eventos. O "__PAGE_VISIT__" e outros eventos (se acionados) serão exibidos no Console SDK.
- Utilizar os logs de exportação (Reports & Insights Export Logs) é outra maneira de verificar os resultados dos testes. Esse conjunto de dados tem um atraso de 1 hora.
Etapa 7: implementar o encaminhamento da Web para o aplicativo
Encaminhamento de atribuição Web-to-App
Use o Singular WebSDK para rastrear as jornadas dos usuários do seu site para o seu aplicativo móvel, permitindo a atribuição precisa da campanha da web às instalações e reengajamentos do aplicativo móvel. Siga estas etapas para configurar o encaminhamento da web para o aplicativo, incluindo suporte a código QR para usuários de desktop.
- Siga nosso Guia de encaminhamento de atribuição do site para o aplicativo móvel para configurar o Singular WebSDK para atribuição da web móvel.
- Para rastreamento de desktop para aplicativo:
- Conclua a configuração no guia acima.
- Use o link do Singular Mobile Web-to-App (por exemplo,
https://yourlink.sng.link/...) e a função WebSDKbuildWebToAppLink(link)com uma biblioteca de códigos QR dinâmicos como QRCode.js. - Gere um código QR na sua página da Web que codifique o link. Os usuários de desktop podem digitalizá-lo com seus dispositivos móveis para abrir o aplicativo, passando os dados da campanha para atribuição.
- Explore nossa demonstração do Singular WebSDK para ver um exemplo prático de geração de código QR e gerenciamento de links web-to-app.
DICA! As visualizações da web no navegador do aplicativo móvel (como as usadas pelo Facebook, Instagram e TikTok) podem fazer com que o ID do dispositivo Singular mude se o usuário mudar para o navegador nativo do dispositivo, interrompendo a atribuição.
Para evitar isso, sempre use o formato de link de rastreamento Singular adequado para cada rede de anúncios:
Demonstração do Singular WebSDK
Abaixo está uma implementação simples, mas abrangente, usando a API Singular WebSDK documentada. Esta amostra fornece uma separação clara para eventos personalizados, eventos de conversão, eventos de receita e suporte a links da web para aplicativos usando o encaminhamento da web para aplicativos suportado pelo WebSDK.
Você pode executar este código em um arquivo HTML local ou modificá-lo em seu site para integração avançada e solução de problemas.
Copie e cole o código abaixo como um arquivo HTML e abra-o no seu navegador.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Singular WebSDK Demo</title>
<script src="https://cdn.jsdelivr.net/npm/qrcodejs/qrcode.min.js"></script>
<script src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #1a1d29;
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
padding: 40px 20px;
min-height: 100vh;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 48px;
border-radius: 16px;
box-shadow: 0 4px 24px rgba(30, 41, 59, 0.08);
border: 1px solid rgba(148, 163, 184, 0.1);
}
.header-brand {
display: flex;
align-items: center;
margin-bottom: 32px;
padding-bottom: 24px;
border-bottom: 2px solid #f1f5f9;
}
.brand-dot {
width: 12px;
height: 12px;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
border-radius: 50%;
margin-right: 12px;
}
h1 {
font-size: 32px;
font-weight: 700;
color: #0f172a;
letter-spacing: -0.025em;
}
h2 {
font-size: 24px;
font-weight: 600;
margin: 48px 0 24px 0;
color: #1e293b;
padding-bottom: 12px;
border-bottom: 2px solid #e2e8f0;
position: relative;
}
h2:before {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 60px;
height: 2px;
background: linear-gradient(90deg, #6366f1, #8b5cf6);
}
p {
margin-bottom: 18px;
color: #475569;
font-size: 15px;
}
strong {
color: #1e293b;
font-weight: 600;
}
.form-group {
margin-bottom: 24px;
}
.radio-group {
display: flex;
gap: 24px;
margin-bottom: 24px;
padding: 16px;
background: #f8fafc;
border-radius: 12px;
border: 1px solid #e2e8f0;
}
.radio-group label {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
color: #475569;
transition: color 0.2s ease;
}
.radio-group label:hover {
color: #1e293b;
}
input[type="radio"] {
width: 20px;
height: 20px;
cursor: pointer;
accent-color: #6366f1;
}
input[type="text"] {
width: 100%;
padding: 14px 18px;
font-size: 15px;
font-weight: 400;
border: 2px solid #e2e8f0;
border-radius: 12px;
transition: all 0.2s ease;
background: #fafbfc;
color: #1e293b;
}
input[type="text"]:focus {
outline: none;
border-color: #6366f1;
background: white;
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
}
input[type="text"]::placeholder {
color: #94a3b8;
font-weight: 400;
}
button {
padding: 14px 32px;
font-size: 15px;
font-weight: 700;
color: #fff;
background: linear-gradient(90deg, #2363f6 0%, #1672fe 100%);
border: none;
border-radius: 4px; /* Pill shape */
cursor: pointer;
box-shadow: 0 2px 8px rgba(35, 99, 246, 0.12);
transition: background 0.2s, transform 0.2s;
letter-spacing: 0.03em;
}
button:hover {
background: linear-gradient(90deg, #1672fe 0%, #2363f6 100%);
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(22, 114, 254, 0.18);
}
button:active {
transform: translateY(0);
}
a {
color: #6366f1;
text-decoration: none;
font-weight: 600;
transition: all 0.2s ease;
position: relative;
}
a:hover {
color: #4f46e5;
}
a:after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: -2px;
left: 0;
background: #6366f1;
transition: width 0.2s ease;
}
a:hover:after {
width: 100%;
}
ol {
margin-left: 10px;
margin-bottom: 18px;
}
ol li {
margin-left: 10px;
}
.info-box {
background: linear-gradient(135deg, #eff6ff 0%, #f0f9ff 100%);
border-left: 4px solid #6366f1;
padding: 20px;
margin: 24px 0;
border-radius: 12px;
border: 1px solid rgba(99, 102, 241, 0.1);
}
.info-box p {
color: #1e40af;
margin-bottom: 0;
}
.section-label {
font-size: 12px;
font-weight: 700;
color: #6b7280;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1px;
}
.button-group {
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
.analytics-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
color: white;
padding: 6px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
margin-bottom: 16px;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin: 24px 0;
}
.feature-item {
padding: 16px;
background: #f8fafc;
border-radius: 10px;
border: 1px solid #e2e8f0;
text-align: center;
}
.feature-item strong {
display: block;
color: #6366f1;
font-size: 14px;
margin-bottom: 4px;
}
.metric-dot {
width: 8px;
height: 8px;
background: #10b981;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
</style>
</head>
<body>
<div class="container">
<div class="header-brand">
<div class="brand-dot"></div>
<h1>Singular WebSDK Demo</h1>
</div>
<p>
Below is a simple but comprehensive implementation using the documented Singular WebSDK API.
This sample provides clear separation for <strong>custom events</strong>, <strong>conversion events</strong>, <strong>revenue events</strong>, and <strong>web-to-app link support using WebSDK supported Web-to-App forwarding</strong>. </p>
<p>You can run this code in a local HTML file or modify it in your site for advanced integration and troubleshooting.
</p>
<h2>SDK Initialization Testing</h2>
<ol>
<li>Open your browsers developer tools.</li>
<li>Inspect the Network tab and filter for "sdk-api-v1.singular.net"</li>
<li>Refresh this page, and examin the payload of the request.</li>
</ol>
<hr>
<h2>User Authentication</h2>
<p>User authentication is key to supporting cross-device attribution. Pass the common User ID value that would also be sent to Singular from mobile SDKs for cross-device continuity.</p>
<div class="form-group">
<div class="section-label">Simulate User Authentication</div>
<input type="text" id="userId" placeholder="Enter a User ID"/>
</div>
<div class="form-group">
<div class="button-group">
<button onclick="setUserId()">Set the User ID (Login)</button>
<button onclick="unsetUserId()">Unset the User ID (Logout)</button>
</div>
</div>
<hr>
<h2>SDK Event Testing</h2>
<div class="form-group">
<div class="section-label">Event Type Selection</div>
<div class="radio-group">
<label>
<input type="radio" name="eventType" value="custom" checked onchange="handleRevenueFields(this)">
<span>Custom Event</span>
</label>
<label>
<input type="radio" name="eventType" value="conversion" onchange="handleRevenueFields(this)">
<span>Conversion Event</span>
</label>
<label>
<input type="radio" name="eventType" value="revenue" onchange="handleRevenueFields(this)">
<span>Revenue Event</span>
</label>
</div>
</div>
<div class="form-group">
<div class="section-label">Event Configuration</div>
<input type="text" id="eventName" placeholder="Enter event name"/>
</div>
<div class="form-group" id="currencyGroup" style="display:none">
<input type="text" id="currency" placeholder="Currency code (e.g., USD, EUR)"/>
</div>
<div class="form-group" id="amountGroup" style="display:none">
<input type="text" id="amount" placeholder="Revenue amount (e.g., 29.99)"/>
</div>
<div class="form-group" id="attributesGroup" style="display:none">
<input type="text" id="attributes" placeholder='Optional attributes (JSON, e.g. {"color":"blue"})'/>
</div>
<div class="form-group">
<button id="send" onclick="sendCustomEvent()">Send Event to Singular</button>
</div>
<hr>
<h2>Web-to-App Forwarding</h2>
<div class="form-group">
<ol>
<li><a href="https://support.singular.net/hc/pt-br/articles/360042283811" target="_blank">Learn more about Web-to-App Forwarding</a></li>
<li>In the page source code you will find the mobile web-to-app base link "<strong>https://seteam.sng.link/D0mvs/y3br?_smtype=3</strong>" used for demonstration purposes.</li>
<li>Replace all occurances of the link with your own Singular Mobile Web-to-App link to test functionality.</li>
<li>Add Singular Web Parameters to the URL and refresh the page. (example: "<strong>?wpsrc=SingularTest&wpcn=MyCampaign</strong>").</li>
</ol>
<hr>
<div class="section-label">Mobile Web-to-App Test</div>
<div class="form-group">
<div class="button-group">
<button onclick="displayWebLink()">Display Constructed Web-to-App Link</button>
<button onclick="testWebLink()">Test Web-to-App Link</button>
</div>
</div>
</div>
<hr>
<div class="section-label">Desktop Web-to-App Configuration</div>
<div class="form-group">
<p>Use a dynamic QR Code generation library like <a href="https://davidshimjs.github.io/qrcodejs/" target="_blank">QRCode.js</a> to build and display a QR code using the Singular Mobile Web-to-App link with constructed campaign parameters.</p>
<div id="qrcode"></div>
</div>
</div>
<script>
/**
* Initializes the Singular SDK with the provided configuration.
* @param {string} sdkKey - The SDK key for Singular (e.g., 'se_team_9b3431b0').
* @param {string} sdkSecret - The SDK secret for Singular (e.g., 'bcdee06e8490949422c071437da5c5ed').
* @param {string} productId - The product ID for Singular (e.g., 'com.website').
* @example
* // Initialize the Singular SDK
* initSingularSDK('se_team_9b3431b0', 'bcdee06e8490949422c071437da5c5ed', 'com.website');
*/
function initSingularSDK() {
var sdkKey = 'se_team_9b3431b0';
var sdkSecret = 'bcdee06e8490949422c071437da5c5ed';
var productId = 'com.website';
// Global Properties set during init (plain object; max 5 keys persisted).
const globalProperties = {
plan: "pro",
region: "us"
};
// Applies to the whole object: false = merge with existing, true = clear existing then set.
const overrideExistingGlobalProperties = false; // required
const config = new SingularConfig(sdkKey, sdkSecret, productId)
.withLogLevel(3)
.withSessionTimeoutInMinutes(0.5)
.withGlobalProperties(globalProperties, overrideExistingGlobalProperties)
.withInitFinishedCallback(function (initParams) {
console.log("Singular Device ID:", initParams.singularDeviceId);
console.log("Global props (SDK):", window.singularSdk.getGlobalProperties());
// If you need to override just one key after init set it in the withInitFinishedCallback:
window.singularSdk.setGlobalProperties("plan", "enterprise");
});
// Initialize the Singular SDK
window.singularSdk.init(config);
generateDesktopWebToAppQRCode(); // Generate QR code after initialization
}
/**
* Triggers Singular SDK initialization when the DOM is fully parsed.
* @example
* document.addEventListener('DOMContentLoaded', function() {
* initSingularSDK();
* });
*/
document.addEventListener('DOMContentLoaded', function() {
initSingularSDK();
});
// Fires a custom event using the Singular SDK
function sendCustomEvent() {
var eventName = document.getElementById('eventName').value;
window.singularSdk.event(eventName);
}
// Fires a conversion event using the Singular SDK
function sendConversionEvent() {
var eventName = document.getElementById('eventName').value;
window.singularSdk.conversionEvent(eventName);
}
// Fires a revenue event only once per session with deduplication
// Parameters:
// - eventName: string (name of event), default "web_purchase" if blank
// - amount: revenue amount, default 0 if blank
// - currency: string, defaults to "USD" if blank
// - attributes: optional object with extra payload
function sendRevenueEvent(eventName, amount, currency, attributes) {
// Fill in defaults and normalize values
eventName = eventName ? eventName : "web_purchase";
currency = currency ? currency.toUpperCase() : "USD";
amount = amount ? amount : 0;
// Create a unique key based on event data for deduplication
// btoa(JSON.stringify(...)) ensures order consistency in local/sessionStorage
var payload = {
eventName: eventName,
amount: amount,
currency: currency,
attributes: attributes ? attributes : null
};
var storageKey = 'singular_revenue_' + btoa(JSON.stringify(payload));
// Only fire event if no identical event has already fired in this tab/session
if (!sessionStorage.getItem(storageKey)) {
sessionStorage.setItem(storageKey, 'true');
if (attributes && typeof attributes === 'object' && Object.keys(attributes).length > 0) {
// Fire event with attributes payload
window.singularSdk.revenue(eventName, currency, amount, attributes);
} else {
// Fire simple revenue event
window.singularSdk.revenue(eventName, currency, amount);
}
console.log("Revenue event sent:", payload);
} else {
// Duplicate detected, don't send to SDK
console.log("Duplicate revenue event prevented:", payload);
alert("Duplicate revenue event prevented!");
}
}
// Collects form values, validates attributes JSON, and calls sendRevenueEvent()
// Ensures only valid values are sent and blocks on malformed JSON
function fireFormRevenueEvent() {
var eventName = document.getElementById('eventName').value;
var currency = document.getElementById('currency').value;
var amount = document.getElementById('amount').value;
var attributesRaw = document.getElementById('attributes').value;
var attributes = null;
if (attributesRaw) {
try {
// Try to parse the optional attributes field from JSON string
attributes = JSON.parse(attributesRaw);
} catch (e) {
alert("Optional attributes must be valid JSON.");
return; // Stop if invalid JSON
}
}
// Calls main revenue logic for deduplication and event sending
sendRevenueEvent(eventName, amount, currency, attributes);
}
// Controls which form fields are visible depending on selected event type
// Revenue events require currency, amount, and attributes fields
function handleRevenueFields(sender) {
var isRevenue = sender.value === "revenue";
document.getElementById("currencyGroup").style.display = isRevenue ? "block" : "none";
document.getElementById("amountGroup").style.display = isRevenue ? "block" : "none";
document.getElementById("attributesGroup").style.display = isRevenue ? "block" : "none";
// Dynamically assign the event button's onclick handler
var send = document.getElementById("send");
send.onclick = (sender.value === "custom") ? sendCustomEvent
: (sender.value === "conversion") ? sendConversionEvent
: fireFormRevenueEvent; // Only fires revenue logic for "revenue"
}
// Opens demo Singular web-to-app link in a new tab/window
function testWebLink() {
var singularWebToAppBaseLink = 'https://seteam.sng.link/D0mvs/y3br?_smtype=3';
window.open(singularWebToAppBaseLink);
}
// Displays constructed Singular web-to-app link with campaign parameters
function displayWebLink() {
var singularWebToAppBaseLink = 'https://seteam.sng.link/D0mvs/y3br?_smtype=3';
var builtLink = window.singularSdk.buildWebToAppLink(singularWebToAppBaseLink);
console.log("Singular Web-to-App Link: ", builtLink);
alert("Singular Web-to-App Link: " + builtLink);
}
// Generates QR code for desktop deep linking using Singular Mobile Web-to-App link
function generateDesktopWebToAppQRCode() {
var singularWebToAppBaseLink = 'https://seteam.sng.link/D0mvs/y3br?_smtype=3';
const value = window.singularSdk.buildWebToAppLink(singularWebToAppBaseLink);
new QRCode(document.getElementById("qrcode"), {
text: value,
width: 128,
height: 128,
colorDark: "#000",
colorLight: "#fff",
correctLevel: QRCode.CorrectLevel.H
});
}
// Simulate user authentication and send login event
function setUserId() {
var userId = document.getElementById('userId').value;
window.singularSdk.login(userId);
console.log("Singular User ID is Set to: " + userId);
window.singularSdk.event("sng_login");
}
// Simulate user logout and unset Singular user ID
function unsetUserId() {
window.singularSdk.logout();
console.log("Singular User ID is Unset");
}
</script>
</body>
</html>
Tópicos avançados
Banners Singular
Propriedades globais
O SDK Singular permite definir propriedades personalizadas a serem enviadas aos servidores Singular junto com cada sessão e evento enviado pelo aplicativo. Essas propriedades podem representar qualquer informação que você desejar sobre o usuário, o modo/status do aplicativo ou qualquer outra coisa.
-
Você pode definir até 5 propriedades globais como um objeto JSON válido. As propriedades globais são mantidas nos navegadores
localstorageaté serem apagadas ou o contexto do navegador ser alterado. -
Cada nome e valor de propriedade pode ter até 200 caracteres. Se você passar um nome ou valor de propriedade mais longo, ele será truncado para 200 caracteres.
-
As propriedades globais são atualmente refletidas nos logs de eventos no nível do usuário do Singular (consulte Exportando logs de atribuição) e nos postbacks.
-
As propriedades globais estão disponíveis para mim enviadas em postbacks do Singular para terceiros para fins de correspondência, se necessário.
Para oferecer suporte à configuração de propriedades globais durante a inicialização do WebSDK,
você deve implementar a opção .withGlobalProperties()
no objeto de configuração.
Para lidar com propriedades globais após a inicialização, você deve usar
as funções SDK: setGlobalProperties(),
getGlobalProperties() ,
clearGlobalProperties() .
/**
* Set a Singular global property during SDK initialization.
* Allows up to 5 key/value pairs. Optionally overwrites existing value for a key.
* @param {{[key: string]: string}} globalProperties - The global property key/value pair object.
* @param {string} propertyKey - The property key to set.
* @param {string} propertyValue - The property value to set.
* @param {boolean} overrideExisting - Whether to overwrite the property if it already exists.
*/
// Global Properties set during init (plain object; max 5 keys persisted).
var globalProperties = {
propertyKey: propertyValue
};
// Set the override, applies to the whole object: false = merge with existing, true = clear existing then set.
var overrideExisting = false; // required
var config = new SingularConfig(sdkKey, sdkSecret, productId)
.withLogLevel(3)
.withSessionTimeoutInMinutes(0.5)
.withGlobalProperties(globalProperties, overrideExisting)
.withInitFinishedCallback(function (initParams) {
console.log("Singular Device ID:", initParams.singularDeviceId);
console.log("Global props (SDK):", window.singularSdk.getGlobalProperties());
// If you need to override just one key after init set it in the withInitFinishedCallback:
window.singularSdk.setGlobalProperties("plan", "enterprise");
});
// Initialize the Singular SDK
window.singularSdk.init(config);
/**
* Define uma propriedade global Singular após a inicialização do SDK.
* Permite até 5 pares de chave/valor. Opcionalmente, sobrescreve o valor existente para uma chave.
*
* @param {string} propertyKey - A chave da propriedade a ser definida.
* @param {string} propertyValue - O valor da propriedade a ser definido.
* @param {boolean} overrideExisting - Se a propriedade já existe, se deve ser substituída.
*/
// Utilização
window.singularSdk.setGlobalProperties(propertyKey, propertyValue, overrideExisting);
// Get the JSON Object of global property values.
// Usage
window.singularSdk.getGlobalProperties();
// Clears all global property values.
// Usage
window.singularSdk.clearGlobalProperties();
Rastreamento de pesquisa orgânica
IMPORTANTE! Este exemplo é fornecido como uma solução alternativa para ativar o rastreamento de pesquisa orgânica. O código deve ser usado apenas como exemplo e atualizado e mantido pelo desenvolvedor web com base nas necessidades do seu departamento de marketing. O rastreamento de pesquisa orgânica pode ter significados diferentes para cada anunciante. Por favor, revisar a amostra e ajustar de acordo com suas necessidades.
Por que usar isso?
-
Garante que as visitas de pesquisa orgânica sejam rastreadas corretamente, mesmo que não haja parâmetros de campanha.
-
Anexa o parâmetro singular "Fonte"
wpsrccom o valor do (referenciador) e o parâmetro "Nome da campanha"wpcncomo "OrganicSearch" à URL para uma atribuição clara . -
Armazena a URL atual e o referenciador em
localStoragepara uso posterior. -
JavaScript puro, sem dependências e fácil de integrar.
Como funciona
-
Verifica a URL da página em busca de parâmetros de campanha conhecidos de (Google, Facebook, TikTok, UTMs, etc.).
-
Se não houver parâmetros de campanha e o referenciador for um mecanismo de pesquisa, acrescenta:
-
wpsrc(com o referenciador como seu valor) -
wpcn(com OrganicSearch como seu valor)
-
-
Atualiza a URL no navegador sem recarregar a página.
-
Armazena o URL atual e o referenciador em
localStoragecomosng_urlesng_ref.
Utilização
-
Adicione
setupOrganicSearchTracking.jsao seu site como uma biblioteca. -
Chame a função
setupOrganicSearchTracking()ANTES de inicializar o WebSDK.
// singular-web-organic-search-tracking: setupOrganicSearchTracking.js
// Tracks organic search referrals by appending wpsrc and wpcn to the URL if no campaign parameters exist and the referrer is a search engine.
// Configuration for debugging (set to true to enable logs)
const debug = true;
// List of campaign parameters to check for exclusion
const campaignParams = [
'gclid', 'fbclid', 'ttclid', 'msclkid', 'twclid', 'li_fat_id',
'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'wpsrc'
];
// Whitelist of legitimate search engine domains (prevents false positives)
const legitimateSearchEngines = new Set([
// Google domains
'google.com', 'google.co.uk', 'google.ca', 'google.com.au', 'google.de',
'google.fr', 'google.it', 'google.es', 'google.co.jp', 'google.co.kr',
'google.com.br', 'google.com.mx', 'google.co.in', 'google.ru', 'google.com.sg',
// Bing domains
'bing.com', 'bing.co.uk', 'bing.ca', 'bing.com.au', 'bing.de',
// Yahoo domains
'yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'yahoo.com.au', 'yahoo.de',
'yahoo.fr', 'yahoo.it', 'yahoo.es', 'yahoo.co.jp',
// Other search engines
'baidu.com', 'duckduckgo.com', 'yandex.com', 'yandex.ru',
'ask.com', 'aol.com', 'ecosia.org', 'startpage.com',
'qwant.com', 'seznam.cz', 'naver.com', 'daum.net'
]);
// Extract main domain from hostname (removes subdomains)
function getMainDomain(hostname) {
if (!hostname) return '';
const lowerHost = hostname.toLowerCase();
// Handle special cases for known search engines with country codes
const searchEnginePatterns = {
'google': (host) => {
// Match google.TLD patterns more precisely
if (host.includes('google.co.') || host.includes('google.com')) {
const parts = host.split('.');
const googleIndex = parts.findIndex(part => part === 'google');
if (googleIndex !== -1 && googleIndex < parts.length - 1) {
return parts.slice(googleIndex).join('.');
}
}
return null;
},
'bing': (host) => {
if (host.includes('bing.co') || host.includes('bing.com')) {
const parts = host.split('.');
const bingIndex = parts.findIndex(part => part === 'bing');
if (bingIndex !== -1 && bingIndex < parts.length - 1) {
return parts.slice(bingIndex).join('.');
}
}
return null;
},
'yahoo': (host) => {
if (host.includes('yahoo.co') || host.includes('yahoo.com')) {
const parts = host.split('.');
const yahooIndex = parts.findIndex(part => part === 'yahoo');
if (yahooIndex !== -1 && yahooIndex < parts.length - 1) {
return parts.slice(yahooIndex).join('.');
}
}
return null;
}
};
// Try specific patterns for major search engines
for (const [engine, patternFn] of Object.entries(searchEnginePatterns)) {
if (lowerHost.includes(engine)) {
const result = patternFn(lowerHost);
if (result) return result;
}
}
// Handle other known engines with simple mapping
const otherEngines = {
'baidu.com': 'baidu.com',
'duckduckgo.com': 'duckduckgo.com',
'yandex.ru': 'yandex.ru',
'yandex.com': 'yandex.com',
'ask.com': 'ask.com',
'aol.com': 'aol.com',
'ecosia.org': 'ecosia.org',
'startpage.com': 'startpage.com',
'qwant.com': 'qwant.com',
'seznam.cz': 'seznam.cz',
'naver.com': 'naver.com',
'daum.net': 'daum.net'
};
for (const [domain, result] of Object.entries(otherEngines)) {
if (lowerHost.includes(domain)) {
return result;
}
}
// Fallback: Extract main domain by taking last 2 parts (for unknown domains)
const parts = hostname.split('.');
if (parts.length >= 2) {
return parts[parts.length - 2] + '.' + parts[parts.length - 1];
}
return hostname;
}
// Get query parameter by name, using URL.searchParams with regex fallback for IE11
function getParameterByName(name, url) {
if (!url) url = window.location.href;
try {
return new URL(url).searchParams.get(name) || null;
} catch (e) {
if (debug) console.warn('URL API not supported, falling back to regex:', e);
name = name.replace(/[\[\]]/g, '\\$&');
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
const results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
}
// Check if any campaign parameters exist in the URL
function hasAnyParameter(url, params) {
return params.some(param => getParameterByName(param, url) !== null);
}
// Improved search engine detection - only checks hostname, uses whitelist
function isSearchEngineReferrer(referrer) {
if (!referrer) return false;
let hostname = '';
try {
hostname = new URL(referrer).hostname.toLowerCase();
} catch (e) {
// Fallback regex for hostname extraction
const match = referrer.match(/^(?:https?:\/\/)?([^\/\?#]+)/i);
hostname = match ? match[1].toLowerCase() : '';
}
if (!hostname) return false;
// First check: exact match against whitelist
if (legitimateSearchEngines.has(hostname)) {
if (debug) console.log('Exact match found for:', hostname);
return true;
}
// Second check: subdomain of legitimate search engine
const hostParts = hostname.split('.');
if (hostParts.length >= 3) {
// Try domain.tld combination (e.g., google.com from www.google.com)
const mainDomain = hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
if (legitimateSearchEngines.has(mainDomain)) {
if (debug) console.log('Subdomain match found for:', hostname, '-> main domain:', mainDomain);
return true;
}
// Try last 3 parts for country codes (e.g., google.co.uk from www.google.co.uk)
if (hostParts.length >= 3) {
const ccDomain = hostParts[hostParts.length - 3] + '.' + hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
if (legitimateSearchEngines.has(ccDomain)) {
if (debug) console.log('Country code domain match found for:', hostname, '-> cc domain:', ccDomain);
return true;
}
}
}
if (debug) {
console.log('Hostname not recognized as legitimate search engine:', hostname);
}
return false;
}
// Main function to update URL with organic search tracking parameters
function setupOrganicSearchTracking() {
const url = window.location.href;
const referrer = document.referrer || '';
// Store URL and referrer in localStorage
try {
localStorage.setItem('sng_url', url);
localStorage.setItem('sng_ref', referrer);
} catch (e) {
if (debug) console.warn('localStorage not available:', e);
}
if (debug) {
console.log('Current URL:', url);
console.log('Referrer:', referrer);
}
// Skip if campaign parameters exist or referrer is not a search engine
const hasCampaignParams = hasAnyParameter(url, campaignParams);
if (hasCampaignParams || !isSearchEngineReferrer(referrer)) {
if (debug) console.log('Skipping URL update: Campaign params exist or referrer is not a legitimate search engine');
return;
}
// Extract and validate referrer hostname
let referrerHostname = '';
try {
referrerHostname = new URL(referrer).hostname;
} catch (e) {
if (debug) console.warn('Invalid referrer URL, falling back to regex:', e);
referrerHostname = referrer.match(/^(?:https?:\/\/)?([^\/]+)/i)?.[1] || '';
}
// Extract main domain from hostname
const mainDomain = getMainDomain(referrerHostname);
if (debug) {
console.log('Full hostname:', referrerHostname);
console.log('Main domain:', mainDomain);
}
// Only proceed if main domain is valid and contains safe characters
if (!mainDomain || !/^[a-zA-Z0-9.-]+$/.test(mainDomain)) {
if (debug) console.log('Skipping URL update: Invalid or unsafe main domain');
return;
}
// Update URL with wpsrc and wpcn parameters
const urlObj = new URL(url);
// Set wpsrc to the main domain (e.g., google.com instead of tagassistant.google.com)
urlObj.searchParams.set('wpsrc', mainDomain);
// Set wpcn to 'Organic Search' to identify the campaign type
urlObj.searchParams.set('wpcn', 'Organic Search');
// Update the URL without reloading (check if history API is available)
if (window.history && window.history.replaceState) {
try {
window.history.replaceState({}, '', urlObj.toString());
if (debug) console.log('Updated URL with organic search tracking:', urlObj.toString());
} catch (e) {
if (debug) console.warn('Failed to update URL:', e);
}
} else {
if (debug) console.warn('History API not supported, cannot update URL');
}
}
Rastreamento entre subdomínios
Por padrão, o Singular WebSDK gera um Singular Device ID e o mantém usando o armazenamento do navegador. Como esse armazenamento não pode ser compartilhado entre subdomínios, o SDK acaba gerando um novo ID para cada subdomínio.
Se você deseja manter o ID do dispositivo Singular entre subdomínios, pode usar uma das seguintes opções:
Método B (avançado): definir manualmente o ID do dispositivo Singular
Se você não quiser que o SDK Singular persista o ID do dispositivo automaticamente, você pode persistir o ID manualmente entre domínios - por exemplo, usando um cookie de domínio de nível superior ou um cookie do lado do servidor. O valor deve ser um ID previamente gerado pelo Singular, no formato uuid4 válido.
NOTA: Você pode ler o ID do dispositivo Singular usando singularSdk.getSingularDeviceId() após chamar o método init ou usar InitFinishedCallback.
| Método withPersistentSingularDeviceId | |
|---|---|
|
Descrição |
Inicialize o SDK com opções de configuração, incluindo o Singular Device Id que você deseja manter. |
| Assinatura | withPersistentSingularDeviceId(singularDeviceId) |
| Exemplo de uso |
|
Próximos passos
- Crie links de sites no Singular para suas campanhas na web
- Siga nossos guias para Google Ads Web, Facebook Web e TikTok Ads Web, se você executar campanhas na web para inventário móvel.
- Monitore seus dados nos relatórios do Singular