Descripción
INFORMACIÓN: La atribución web es una función empresarial. Ponte en contacto con tu gestor de éxito del cliente para habilitar esta función en tu cuenta.
Esta guía explica cómo implementar Singular WebSDK utilizando JavaScript nativo. Este método proporciona el seguimiento más fiable y se recomienda para la mayoría de las implementaciones, ya que no es bloqueado por los bloqueadores de anuncios habituales.
¡IMPORTANTE!
- No implemente los métodos JavaScript nativo y Google Tag Manager a la vez. Elija solo uno para evitar el seguimiento duplicado.
- Singular WebSDK está diseñado para ejecutarse en el lado del cliente, en el navegador del usuario. Requiere acceso a funciones del navegador como localStorage y el Modelo de Objetos de Documento (DOM) para funcionar correctamente. No intente ejecutar el SDK en el lado del servidor (por ejemplo, a través de Next.js SSR o node.js), ya que esto provocará fallos en el seguimiento, ya que los entornos de servidor no proporcionan acceso a las API del navegador.
Requisitos previos
Antes de empezar, asegúrate de que tienes:
-
Clave SDK y secreto SDK:
- Dónde encontrarlos: Inicie sesión en su cuenta de Singular y vaya a Herramientas de desarrollador > Integración del SDK > Claves del SDK.
-
ID del producto:
-
Qué es: un nombre único para su sitio web, idealmente utilizando el formato DNS inverso (por ejemplo,
com.website-name). - Por qué es importante: este ID asocia el sitio web como una aplicación en Singular y debe coincidir con el ID del paquete de la aplicación web que aparece en su página de aplicaciones en Singular.
-
Qué es: un nombre único para su sitio web, idealmente utilizando el formato DNS inverso (por ejemplo,
- Permiso para editar el código HTML de tu sitio web.
- Acceso para añadir JavaScript en la sección
<head>de tus páginas. - Una lista de los eventos que desea rastrear. Consulte nuestra lista completa de eventos estándar de Singular y los eventos recomendados por sector para obtener ideas.
Pasos de implementación
Paso 1: Añadir el script de la biblioteca SDK
Añada el siguiente fragmento de código a la sección <head> de cada página de su sitio web. Colóquelo lo antes posible, idealmente cerca de la parte superior de la etiqueta <head>.
¡CONSEJO! Añadir el script al principio garantiza que la biblioteca JavaScript de Singular esté disponible para cualquier función de Singular en el código fuente de su página.
<script src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"></script>
Especifique la versión específica en la ruta de la biblioteca WebSDK JavaScript, por ejemplo: 1.4.8 se indica aquí:
<script src="https://web-sdk-cdn.singular.net/singular-sdk/1.4.8/singular-sdk.js"></script>
-
Ejecute
npm i singular-sdken el directorio raíz de su proyecto o añada"singular-sdk": "^1.4.8"a la sección de dependencias de su archivo package.json y, a continuación, ejecutenpm install. Añada el siguiente código en los scripts que desee utilizar con el SDK. -
Añade el siguiente código en los scripts en los que quieras utilizar el SDK.
import {singularSdk, SingularConfig} from "singular-sdk";
Uso con Next.js / React
Next.js es un marco de trabajo de React que proporciona renderización del lado del servidor (SSR), generación de sitios estáticos (SSG) y componentes de servidor React. Dado que Singular WebSDK requiere API de navegador (DOM, localStorage, cookies), debe cargarlo solo del lado del cliente.
IMPORTANTE: Nunca cargue el SDK de Singular en código del lado del servidor
(por ejemplo, getServerSideProps, componentes de servidor React
o entornos Node.js). Esto provocará errores porque las API del navegador
no están disponibles en el servidor.
Método 1: Uso del componente de script Next.js (recomendado)
El componente <Script> de Next.js proporciona estrategias de carga optimizadas
y evita la inyección de scripts duplicados en los
cambios de ruta.
// 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} />
</>
)
}
Opciones de estrategia de script:
-
afterInteractive(recomendado): se carga después de que la página se vuelva interactiva, ideal para análisis y seguimiento. -
lazyOnload: Se carga durante el tiempo de inactividad del navegador; utilícelo para widgets no críticos.
Método 2: Importación dinámica para la carga a nivel de componente
Para el control a nivel de componente, utilice las importaciones dinámicas de Next.js con
ssr: false para cargar el script solo cuando sea necesario:
// 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
}
Configuración de variables de entorno
¡CONSEJO! Almacene sus credenciales de Singular en variables de entorno
prefijadas con NEXT_PUBLIC_ para que estén
disponibles en el 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
Estas variables de entorno serán accesibles en su código del lado del cliente y se pueden utilizar durante la inicialización en el paso 2.
Router de aplicaciones frente a router de páginas
| Tipo de enrutador | Diferencias clave | Ubicación del script |
|---|---|---|
| Enrutador de aplicaciones (Next.js 13+) |
Los componentes son componentes de servidor de forma predeterminada. Añade
la directiva 'use client' para las API del navegador.
|
app/layout.tsx
|
| Enrutador de páginas (Next.js 12 y versiones anteriores) | Todos los componentes son componentes de cliente de forma predeterminada. |
pages/_app.tsx
|
Paso 2: Inicializar el SDK
- Inicialice siempre el SDK cada vez que se cargue una página en el navegador.
- La inicialización es necesaria para todas las funciones de atribución y seguimiento de eventos de Singular.
- La inicialización activa un evento
__PAGE_VISIT__evento. - El
__PAGE_VISIT__evento se utiliza para generar una nueva sesión del lado del servidor cuando se cumplen las siguientes condiciones:- El usuario llega con nuevos datos publicitarios en la URL (como parámetros UTM o WP), o
- La sesión anterior ha caducado (tras 30 minutos de inactividad).
- Las sesiones se utilizan para medir la retención de usuarios y respaldar la atribución de la reactivación.
- Cree una función de inicialización y llámela en DOM Ready después de que se cargue la página.
- Asegúrese de que la inicialización se produzca antes de que se notifiquen otros eventos de Singular.
- Para aplicaciones de página única (SPA), inicialice el SDK de Singular en la primera carga de la página y, a continuación, llame a la función Singular Page Visist
window.singularSdk.pageVisit()en cada cambio de ruta que represente una nueva vista de página.
Inicialización básica DOM-Ready
Añada un detector de eventos para llamar a initSingularSDK()
en 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();
});
Inicialización básica con propiedades globales
Debido a que el SDK de Singular no está inicializado, debe implementar
la función personalizada para establecer las propiedades globales en el
localstorage del navegador.
Consulte la función personalizada setGlobalPropertyBeforeInit().
Una vez implementada, puede establecer las propiedades como se describe a continuación antes de
la inicialización del 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();
});
Inicialización básica con enrutamiento de aplicación de página única (SPA)
| Escenario | Qué hacer |
|---|---|
|
Primera carga de la página |
Llamar a |
|
Navegación a una nueva ruta/página |
Llamar a |
|
En la carga inicial en SPA |
No llamar a |
Ejemplo para SPA (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;
Inicialización básica utilizando una llamada de retorno de inicialización
Si necesita ejecutar código después de que el SDK esté listo (por ejemplo, para obtener
el ID de dispositivo
Singular), establezca una llamada de retorno con .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();
});
Inicialización de Next.js / React
Después de cargar el script del SDK en el paso 1, inicialícelo cuando el DOM esté listo. La inicialización debe realizarse en el lado del cliente después de que el script se haya cargado.
Declaraciones de tipo TypeScript
Cree un archivo de declaración de tipos para añadir compatibilidad con TypeScript para el SDK de Singular:
// 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;
}
Inicialización básica con el componente Script de Next.js
Cuando utilice el componente Script del paso 1, añada la llamada de retorno onLoad
para inicializar el 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} />
</>
)
}
Inicialización con el hook useEffect
Si utiliza el método de importación dinámica o necesita más control, inicialice
utilizando 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;
}
Inicialización con opciones de configuración
Encadenar métodos .with para habilitar funciones adicionales como
el seguimiento entre subdominios o los ID de usuario 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);
Enrutamiento de aplicaciones de página única (SPA)
Para aplicaciones Next.js con enrutamiento del lado del cliente, debe realizar un seguimiento de los cambios de ruta por separado de la inicialización inicial.
IMPORTANTE: Inicialice el SDK solo una vez en la primera
carga de la página. A continuación, llame a window.singularSdk.pageVisit()en cada cambio de ruta posterior.
// 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;
}
Uso de una llamada de inicialización
Si necesita ejecutar código después de que el SDK esté listo (por ejemplo, para obtener
el ID de dispositivo Singular), utilice .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);
Prácticas recomendadas para la inicialización de Next.js
- Inicialice el SDK solo una vez al montar la aplicación, no en cada cambio de ruta.
-
Utilice variables de entorno con el prefijo
NEXT_PUBLIC_para las credenciales del SDK. -
Para las SPA, llame a
window.singularSdk.pageVisit()en los cambios de ruta para realizar un seguimiento de la navegación. -
Utilice
useRefpara realizar un seguimiento del estado de inicialización y evitar la inicialización duplicada. -
Añada comprobaciones nulas para
window.singularSdkantes de llamar a métodos para evitar errores durante SSR. - Pruebe en modo de desarrollo para asegurarse de que el SDK se inicializa correctamente sin errores del lado del servidor.
Lista de verificación para desarrolladores de Next.js
- Verifique que el script se haya cargado correctamente en el paso 1.
- Configure las variables de entorno con sus credenciales del SDK.
- Cree declaraciones de tipo TypeScript para mejorar la experiencia del desarrollador. .
-
Elija el método de inicialización: componente de script
onLoado ganchouseEffect. -
Para las SPA, implemente el seguimiento de cambios de ruta con
pageVisit(). -
Pruebe la inicialización comprobando en la consola el
evento
__PAGE_VISIT__.
-
Reemplace
'sdkKey'por su clave SDK real. -
Reemplace
'sdkSecret'por su SDK Secret real. -
Reemplace
'productId'por su ID de producto real. Debe tener el siguiente aspecto:com.website-namey debe coincidir con el valor BundleID de la página Apps de la plataforma Singular.
Opciones de configuración
Mejora la configuración de WebSDK encadenando métodos .with para habilitar funciones adicionales.
Por ejemplo, para admitir el seguimiento entre subdominios mediante la persistencia del ID de dispositivo Singular (SDID) en una cookie o para incluir un ID de usuario personalizado para un visitante que regresa al sitio con un estado de inicio de sesión activo:
var domain = 'website-name.com';
var config = new SingularConfig('sdkKey','sdkSecret','productId')
.withAutoPersistentSingularDeviceId(domain)
.withCustomUserId(userId);
Referencia de métodos SingularConfig
Aquí están todos los métodos «.with» disponibles.
| Método | Descripción | Más información |
.withCustomUserId(customId)
|
Enviar el ID de usuario a Singular | Configuración del ID de usuario |
.withProductName(productName)
|
Un nombre de visualización opcional para el producto | |
.withLogLevel(logLevel)
|
Configurar el nivel de registro: 0 - Ninguno (predeterminado); 1 - Advertencia; 2 - Información; 3 - Depuración. | |
.withSessionTimeoutInMinutes(timeout)
|
Establecer el tiempo de espera de la sesión en minutos (predeterminado: 30 minutos) |
|
.withAutoPersistentSingularDeviceId(domain)
|
Habilitar el seguimiento automático entre subdominios | Persistencia automática mediante cookies |
|
|
Habilitar el seguimiento manual entre subdominios | Establecer manualmente el ID único del dispositivo |
.withInitFinishedCallback(callback)
|
Invocar una devolución de llamada cuando se complete la inicialización del SDK | Invocar una función de devolución de llamada cuando se complete la inicialización |
.withGlobalProperties(globalProperties, false)
|
Establecer propiedades globales durante la inicialización; el segundo parámetro
es overrideExisting (booleano).
Utilizar false para fusionar con las propiedades existentes,
true para sobrescribir las propiedades existentes.
|
Propiedades globales |
.withEventsDedupEnabled()
|
Habilite la deduplicación opcional de eventos para reducir las exportaciones de eventos duplicados (por ejemplo, disparadores repetidos activados en un breve intervalo de tiempo) | Deduplicación de eventos |
.withTimeBetweenEvents(timeBetweenEvents)
|
Establezca el intervalo de tiempo máximo (ms) para la deduplicación (por defecto: 1000 ms / 1 segundo). | Deduplicación de eventos |
Lista de verificación para desarrolladores
- Reúna sus credenciales SDK y el ID del producto.
- Decida si necesita alguna configuración personalizada (ID de usuario, tiempo de espera, etc.).
- Cree una función de inicialización Singular y un objeto SingularConfig utilizando los ejemplos anteriores.
- Compruebe siempre que la inicialización solo se activa una vez al cargar la página.
¡CONSEJO! Para configuraciones avanzadas como el seguimiento de búsquedas orgánicas , es posible que tenga que implementar JavaScript personalizado , por ejemplo, para ajustar los parámetros de consulta, antes de que se inicialice el SDK de Singular . Asegúrese de que su código personalizado se ejecute antes de la función de inicialización de Singular para que los cambios se capturen correctamente. Puede encontrar más detalles sobre cómo implementar el seguimiento de búsquedas orgánicas aquí.
Paso 3: Seguimiento de eventos
Después de inicializar el SDK, puede realizar un seguimiento de los eventos personalizados cuando los usuarios realizan acciones importantes en su sitio web.
¡IMPORTANTE! ¡Singular no bloquea los eventos duplicados! Es responsabilidad de los desarrolladores añadir protecciones contra las actualizaciones de página o la duplicación. Se recomienda incorporar algún método de deduplicación específico para los eventos de ingresos, a fin de evitar datos de ingresos erróneos. Consulte el «Paso 5: Prevención de eventos duplicados» a continuación para ver un ejemplo.
Seguimiento básico de eventos
Realice el seguimiento de un evento sencillo o añada atributos personalizados utilizando JSON válido para proporcionar más contexto sobre el 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);
Seguimiento de eventos de conversión
Realice un seguimiento de un evento de conversión:
var eventName = 'sng_complete_registration';
window.singularSdk.conversionEvent(eventName);
Seguimiento de eventos de ingresos
Realice un seguimiento de los eventos de conversión con información sobre ingresos y añada atributos opcionales para obtener más contexto:
- Pase la moneda de los ingresos como un código de moneda ISO 4217 de tres letras, como «USD», «EUR» o «INR».
- Pase el valor del importe de los ingresos como un 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);
Patrones comunes de implementación de eventos
Eventos de carga de página
El mecanismo de seguimiento de eventos de carga de página utiliza JavaScript para supervisar
cuándo se carga completamente una página web y, a continuación, activa un evento de análisis utilizando
el SDK de Singular. En concreto, aprovecha el
método window.addEventListener('load', ...) para detectar
cuándo se han cargado todos los recursos de la página (por ejemplo, HTML, imágenes, scripts).
Tras este evento, se llama a la función event del SDK de Singular
para registrar un evento personalizado con atributos asociados, lo que permite
realizar un seguimiento de las interacciones de los usuarios con fines analíticos, como supervisar
las visitas a la página o las acciones de los usuarios, como los registros. El mecanismo se utiliza habitualmente
en el análisis web para capturar datos sobre el comportamiento de los usuarios, como
las visitas a la página o acciones específicas, con atributos personalizables para
obtener información detallada.
El mecanismo se puede adaptar para realizar un seguimiento de un evento específico (por ejemplo, un
evento page_view ) con atributos personalizados, como se muestra
a continuación. La estructura del código sigue siendo limpia y modular, con el nombre del evento
y los atributos definidos por separado para mayor claridad y facilidad de mantenimiento.
/**
* 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 clic en botones
El mecanismo de seguimiento de eventos de clic en botones utiliza JavaScript para supervisar
cuándo un usuario hace clic en un botón con el ID checkout-button
y activa un evento checkout_started utilizando el SDK de Singular
para el seguimiento analítico. El código emplea un detector de eventos click
para detectar la interacción con el botón, incluye un mecanismo
para evitar que se activen múltiples eventos (utilizando un
atributo data-event-fired ) y envía el evento con
atributos personalizados (cart_value y currency)
a la plataforma Singular. Esto es ideal para el análisis de comercio electrónico
para realizar un seguimiento de cuándo los usuarios inician un proceso de pago, lo que garantiza la precisión
de los datos al activar el evento solo una vez por sesión de 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 envío de formularios
El mecanismo de seguimiento de eventos de envío de formularios utiliza JavaScript para supervisar
cuándo un usuario envía un formulario con el ID signup-form y
activa un evento signup_completed utilizando el SDK de Singular
para el seguimiento analítico. El código emplea un detector de eventos submit
para detectar el envío de formularios, incluye un mecanismo para
evitar que se activen múltiples eventos (utilizando un atributo data-event-fired
) y envía el evento con un atributo personalizado (signup_method)
a la plataforma Singular. Esto es ideal para realizar un seguimiento de los registros de usuarios
en flujos de trabajo de análisis, como supervisar la adquisición de usuarios o los métodos de registro
, lo que garantiza la precisión de los datos al activar el evento solo una vez por
sesión de página.
A continuación se muestra un ejemplo claro que adapta el código original para utilizar
window.singularSdk.event , separando el nombre del evento
y los atributos para mayor claridad.
/**
* 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);
});
Paso 4: Configuración del ID de usuario del cliente
Puede enviar su ID de usuario interno a Singular utilizando un método SDK de Singular.
NOTA: Si utiliza la solución Cross-Device de Singular, debe recopilar el ID de usuario en todas las plataformas.
- El ID de usuario puede ser cualquier identificador y no debe exponer información de identificación personal (PII). Por ejemplo, no debe utilizar la dirección de correo electrónico, el nombre de usuario o el número de teléfono del usuario. Singular recomienda utilizar un valor hash único para sus datos propios .
- El valor del ID de usuario que se pasa a Singular también debe ser el mismo ID de usuario interno que captura en todas las plataformas (web/móvil/PC/consola/sin conexión).
- Singular incluirá el ID de usuario en las exportaciones a nivel de usuario, ETL y postbacks de BI internos (si están configurados). El ID de usuario es un dato propio y Singular no lo comparte con terceros.
- El valor del ID de usuario, cuando se establece con el método SDK de Singular, persistirá hasta que se desactive mediante el método logout() o hasta que se elimine el almacenamiento local del navegador. Cerrar o actualizar el sitio web no desactiva el ID de usuario.
- En modo privado/incógnito, el SDK no puede conservar el ID de usuario porque el navegador borra automáticamente el almacenamiento local cuando se cierra.
Para establecer el ID de usuario, utilice el método login().
Para
desactivarlo (por ejemplo, si el usuario «cierra sesión» en la cuenta), llame al
método logout().
NOTA: Si varios usuarios utilizan un solo dispositivo, recomendamos implementar un flujo de cierre de sesión para establecer y desactivar el ID de usuario en cada inicio y cierre de sesión.
Si ya conoce el ID de usuario cuando se inicializa el SDK de Singular en el
sitio web,
establezca el ID de usuario en el objeto de configuración. De esta manera, Singular puede tener el ID de usuario
desde
la primera sesión. Sin embargo, el ID de usuario no suele estar disponible hasta
que
El usuario se registra o inicia sesión. En ese caso, llame a login() una vez completado el proceso de registro.
¡CONSEJO! Utilice el mismo ID de usuario de cliente que utiliza en sus SDK móviles. Esto permite la atribución entre dispositivos y proporciona una visión completa del comportamiento del usuario en todas las plataformas.
Prácticas recomendadas para el ID de usuario del cliente:
- Configúrelo tan pronto como el usuario inicie sesión o se registre.
- Utilice el mismo ID en las plataformas web y móviles.
- No utilice información de identificación personal (correo electrónico, número de teléfono).
- Utilice su ID de usuario interno o ID de base de datos.
- Envíela como una cadena, incluso si es numérica:
// After user logs in or signs up
var userId = 'user_12345';
window.singularSdk.login(userId);
// After user logs out
window.singularSdk.logout();
Paso 5: Desduplicación de eventos (opcional)
¡IMPORTANTE! Si su sitio puede activar el mismo disparador más de una vez en un intervalo de tiempo corto, es posible que vea eventos duplicados exportados. Habilite la deduplicación del SDK para suprimir automáticamente los duplicados.
El SDK web de Singular admite la deduplicación opcional de eventos para reducir las exportaciones duplicadas causadas por disparadores repetidos en un intervalo de tiempo corto. Cuando se habilita, el SDK eliminará los eventos duplicados que coincidan con los mismos parámetros de deduplicación dentro del intervalo de tiempo configurado.
Cómo habilitar
-
withEventsDedupEnabled: Habilite la deduplicación de eventos (por defecto está deshabilitada). -
withTimeBetweenEvents: Intervalo de tiempo máximo (en milisegundos) utilizado para considerar dos eventos como duplicados; el valor predeterminado es 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);
Cómo se detectan los duplicados
Cuando la deduplicación está habilitada, el SDK calcula un hash a partir de los campos clave del evento y suprime las repeticiones dentro del intervalo de tiempo. Los parámetros de deduplicación incluyen: EventName, EventProductName, IsRevenueEvent, CustomUserId, GlobalProperties, MatchId y WebUrl (y el SDK también tiene en cuenta la carga útil «extra» adicional del evento, como los ingresos/argumentos, al realizar el hash).
Paso 6: Prueba de la implementación
Después de implementar el SDK, compruebe que funciona correctamente utilizando las herramientas de desarrollo de su navegador.
Verificar que el SDK se ha cargado
- Abra su sitio web en un navegador
- Abra las herramientas de desarrollo (F12 o clic con el botón derecho → Inspeccionar).
- Vaya a la pestaña Consola
- Escriba
typeof singularSdky pulse Intro - Debería ver un objeto «función», no «indefinido»
Verifique las solicitudes de red
- Abre las herramientas de desarrollo (F12).
- Ve a la pestaña Red
- Vuelva a cargar la página
- Filtra por
singularosdk-api - Busca las solicitudes a
sdk-api-v1.singular.net - Haga clic en una solicitud para ver los detalles
- Verifica que el código de estado sea
200 - Comprueba que la carga útil de la solicitud contiene tu ID de producto. Este se encuentra en el parámetro «
i» de la carga útil.
¡ÉXITO! Si ve solicitudes a sdk-api-v1.singular.net con el código de estado 200, su SDK está enviando datos correctamente a Singular.
Verifique los eventos
- Active un evento en su sitio web (haga clic en un botón, envíe un formulario, etc.).
- En la pestaña Red, busque una nueva solicitud a
sdk-api-v1.singular.net - Haga clic en la solicitud y vea la pestaña Carga útil o Solicitud.
-
Comprueba que el nombre de tu evento «
n» aparece en la solicitud.
Para las pruebas de extremo a extremo, utilice la consola de pruebas
Puede probar la integración del SDK web utilizando la consola SDK disponible en el panel de control de Singular.
- En la plataforma Singular, vaya a Herramientas de desarrollador > Consola de pruebas.
- Haga clic en Añadir dispositivo.
- Seleccione la plataforma«Web» e introduzca el ID de dispositivo de Singular.
- Obtenga el ID de dispositivo de Singular desde la carga útil del navegador. Consulte la captura de pantalla anterior para localizar el ID de dispositivo de Singular (
SDID) en un evento. - La consola de pruebas debe permanecer activa mientras realiza la prueba en una ventana separada. La consola no mostrará ningún evento que se active mientras esté cerrada o desconectada.
- Una vez añadido el SDID, puede volver a cargar la página web y activar algunos eventos. El evento «__PAGE_VISIT__» y otros eventos (si se activan) se mostrarán en la consola SDK.
- Otra forma de verificar los resultados de las pruebas es utilizar los registros de exportación (Reports & Insights Export Logs). Este conjunto de datos tiene un retraso de 1 hora.
Paso 7: Implementar el reenvío de web a aplicación
Reenvío de atribución de web a aplicación
Utilice Singular WebSDK para realizar un seguimiento de los recorridos de los usuarios desde su sitio web hasta su aplicación móvil, lo que permite una atribución precisa de las campañas web a las instalaciones y reenganches de aplicaciones móviles. Siga estos pasos para configurar el reenvío de web a aplicación, incluida la compatibilidad con códigos QR para usuarios de escritorio.
- Siga nuestra Guía de reenvío de atribución de sitio web a aplicación móvil para configurar Singular WebSDK para la atribución web móvil.
- Para el seguimiento de la web a la aplicación en ordenadores de sobremesa:
- Complete la configuración de la guía anterior.
- Utilice el enlace de Singular Mobile Web-to-App (por ejemplo,
https://yourlink.sng.link/...) y la función WebSDKbuildWebToAppLink(link)con una biblioteca de códigos QR dinámicos como QRCode.js. - Genere un código QR en su página web que codifique el enlace. Los usuarios de escritorio pueden escanearlo con su dispositivo móvil para abrir la aplicación, pasando los datos de la campaña para su atribución.
- Explore nuestra demostración de Singular WebSDK para ver un ejemplo práctico de generación de códigos QR y gestión de enlaces web-to-app.
¡CONSEJO! Las vistas web del navegador móvil integrado en la aplicación (como las que utilizan Facebook, Instagram y TikTok) pueden provocar que el ID de dispositivo de Singular cambie si el usuario se desplaza al navegador nativo del dispositivo, lo que interrumpe la atribución.
Para evitarlo, utilice siempre el formato de enlace de seguimiento de Singular adecuado para cada red publicitaria:
Demostración de Singular WebSDK
A continuación se muestra una implementación sencilla pero completa que utiliza la API WebSDK de Singular documentada. Esta muestra proporciona una separación clara entre eventos personalizados, eventos de conversión, eventos de ingresos y compatibilidad con enlaces de web a aplicación mediante el reenvío de web a aplicación compatible con WebSDK.
Puede ejecutar este código en un archivo HTML local o modificarlo en su sitio para una integración avanzada y la resolución de problemas.
Copie y pegue el código siguiente como un archivo HTML y ábralo en su 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/es-419/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>
Temas avanzados
Banners de Singular
Propiedades globales
El SDK de Singular le permite definir propiedades personalizadas que se enviarán a los servidores de Singular junto con cada sesión y evento enviado desde la aplicación. Estas propiedades pueden representar cualquier información que desee sobre el usuario, el modo/estado de la aplicación o cualquier otra cosa.
-
Puede definir hasta 5 propiedades globales como un objeto JSON válido. Las propiedades globales se conservan en el navegador
localstorageo hasta que se borran o se cambia el contexto del navegador. -
Cada nombre y valor de propiedad puede tener hasta 200 caracteres de longitud. Si se pasa un nombre o valor de propiedad más largo, se truncará a 200 caracteres.
-
Las propiedades globales se reflejan actualmente en los registros de eventos a nivel de usuario de Singular (consulte Exportación de registros de atribución) y en los postbacks.
-
Las propiedades globales están disponibles para mí enviadas en postbacks desde Singular a un tercero con fines de coincidencia es necesario.
Para admitir la configuración de propiedades globales durante la inicialización de WebSDK,
debe implementar la opción .withGlobalProperties()
en el objeto de configuración.
Para gestionar las propiedades globales después de la inicialización, debe utilizar
las funciones del 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);
/**
* Establece una propiedad global singular después de la inicialización del SDK.
* Permite hasta 5 pares clave/valor. Opcionalmente, sobrescribe el valor existente para una clave.
*
* @param {string} propertyKey: la clave de la propiedad que se va a establecer.
* @param {string} propertyValue: el valor de la propiedad que se va a establecer.
* @param {boolean} overrideExisting: si se va a sobrescribir la propiedad si ya existe.
*/
// Uso
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();
Seguimiento de búsquedas orgánicas
¡IMPORTANTE! Este ejemplo se proporciona como una solución alternativa para habilitar el seguimiento de búsquedas orgánicas. El código debe utilizarse únicamente como ejemplo y ser actualizado y mantenido por el desarrollador web en función de las necesidades de su departamento de marketing. El seguimiento de búsquedas orgánicas puede tener diferentes significados según el anunciante. Revise la muestra y ajústela a sus necesidades.
¿Por qué utilizarlo?
-
Garantiza que las visitas de búsqueda orgánica se rastreen correctamente, incluso si no hay parámetros de campaña.
-
Añade el parámetro singular «Fuente»
wpsrccon el valor de la referencia (referrer) y el parámetro «Nombre de la campaña»wpcncomo «OrganicSearch» a la URL para una atribución clara . -
Almacena la URL actual y el referente en
localStoragepara su uso posterior. -
Puro JavaScript, sin dependencias y fácil de integrar.
Cómo funciona
-
Comprueba la URL de la página en busca de parámetros de campaña conocidos de (Google, Facebook, TikTok, UTM, etc.).
-
Si no hay parámetros de campaña y el referente es un motor de búsqueda, añade:
-
wpsrc(con el referente como su valor) -
wpcn(con OrganicSearch como su valor)
-
-
Actualiza la URL en el navegador sin recargar la página.
-
Almacena la URL actual y el referente en
localStoragecomosng_urlysng_ref.
Uso
-
Añade
setupOrganicSearchTracking.jsa tu sitio como biblioteca. -
Llame a la función
setupOrganicSearchTracking()ANTES de inicializar el 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');
}
}
Seguimiento entre subdominios
De forma predeterminada, Singular WebSDK genera un ID de dispositivo Singular y lo conserva utilizando el almacenamiento del navegador. Dado que este almacenamiento no se puede compartir entre subdominios, el SDK termina generando un nuevo ID para cada subdominio.
Si desea conservar el ID de dispositivo Singular en todos los subdominios, puede utilizar una de las siguientes opciones:
Método B (avanzado): configurar el ID de dispositivo singular manualmente
Si no desea que Singular SDK conserve automáticamente el ID del dispositivo, puede conservar el ID manualmente en todos los dominios, por ejemplo, utilizando una cookie de dominio de nivel superior o una cookie del lado del servidor. El valor debe ser un ID previamente generado por Singular, en formato uuid4 válido.
NOTA: Puede leer el ID de dispositivo Singular utilizando singularSdk.getSingularDeviceId() después de llamar al método init o utilizando InitFinishedCallback.
| Método withPersistentSingularDeviceId | |
|---|---|
|
Descripción |
Inicializar el SDK con opciones de configuración, incluido el ID de dispositivo Singular que desea conservar. |
| Firma | withPersistentSingularDeviceId(singularDeviceId) |
| Ejemplo de uso |
|
Próximos pasos
- Cree enlaces a sitios web en Singular para sus campañas web
- Siga nuestras guías para Google Ads Web, Facebook Web y TikTok Ads Web si realiza campañas web para inventario móvil.
- Supervise sus datos en los informes de Singular