Visión general
INFORMACIÓN: Web Attribution es una función para empresas. Póngase en contacto con su Customer Success Manager para activar esta función en su 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 comunes.
IMPORTANTE
- No implemente ambos métodos, JavaScript nativo y Google Tag Manager. Elija sólo uno para evitar el seguimiento duplicado.
- Singular WebSDK está diseñado para ejecutarse en el navegador del usuario. Requiere acceso a funciones del navegador como localStorage y el Modelo de objetos del 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 rastreo, puesto que los entornos de servidor no proporcionan acceso a las API del navegador.
Requisitos previos
Antes de empezar, asegúrese de que dispone de
-
SDK Key & SDK Secret:
- Dónde encontrarlos: Inicie sesión en su cuenta de Singular y vaya a Herramientas para desarrolladores > Integración SDK > Claves SDK.
-
ID de 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 App en Singular y debe coincidir con el bundleID de la Web App que aparece en su Página de Apps 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 su sitio Web.
- Acceso para añadir JavaScript en la sección
<head>de sus páginas. - Una lista de los eventos que desea controlar. Consulte nuestros Eventos estándar de Singular: Lista completa y Eventos recomendados por vertical 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 todas las páginas de su sitio web. Colóquelo lo antes posible, idealmente cerca de la parte superior de la etiqueta <head>.
CONSEJO Si añade el script lo antes posible, se asegurará de que Singular JavaScript Library 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 concreta en la ruta de la biblioteca WebSDK JavaScript, por ejemplo: aquí se indica 1.4.3:
<script src="https://web-sdk-cdn.singular.net/singular-sdk/1.4.3/singular-sdk.js"></script>
-
Ejecute
npm i singular-sdken el directorio raíz de su proyecto, o añada"singular-sdk": "^1.4.3"a la sección de dependencias de su archivo package.json y, a continuación, ejecutenpm install. -
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.j s es un framework de React que proporciona renderizado 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 únicamente del lado del cliente.
IMPORTANTE: Nunca cargue el Singular SDK en código del lado del servidor (por ejemplo, getServerSideProps, React Server Components, 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(recomendada): Se carga después de que la página se vuelve interactiva - ideal para análisis y seguimiento. -
lazyOnloadSe carga durante el tiempo de inactividad del navegador, ideal 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 sólo 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 Almacena tus credenciales de Singular en variables de entorno con el prefijo 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 tu código del lado del cliente y pueden ser utilizadas durante la inicialización en el Paso 2.
App Router vs Páginas Router
| Tipo de router | Diferencias clave | Ubicación del script |
|---|---|---|
| App Router (Next.js 13+) |
Los componentes son componentes de servidor por defecto. Añade la directiva 'use client' para las API de navegador. |
app/layout.tsx
|
| Enrutador de páginas (Next.js 12 y anteriores) | Todos los componentes son componentes cliente por defecto. |
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 evento
__PAGE_VISIT__se utiliza para generar una nueva sesión en el 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 expirado (después de 30 minutos de inactividad).
- Las sesiones se utilizan para medir la retención de usuarios y apoyar la atribución de reenganche.
- Crear una función de inicialización y llamar a esta función en DOM Ready después de cargar la página.
- Asegúrese de que la inicialización se produce antes de que se notifique cualquier otro evento Singular.
- Para aplicaciones de una sola página (SPA), inicialice Singular SDK en la primera carga de página y luego 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 receptor 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 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 indica 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 una sola página (SPA)
| Escenario | Qué hacer |
|---|---|
|
Carga de la primera página |
Llamar a |
|
Navegar a una nueva ruta/página |
Llamar a |
|
En la carga inicial en SPA |
No llamar a |
Ejemplo 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;
Inicialización Básica Usando un Callback de Inicialización
Si necesitas ejecutar código después de que el SDK esté listo (por ejemplo, para obtener el Singular Device ID), establece un callback 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();
});
Next.js / Inicialización de React
Después de cargar el script del SDK en el paso 1, inicialízalo cuando el DOM esté listo. La inicialización debe ocurrir del lado del cliente después de que el script se haya cargado.
Declaraciones de Tipo TypeScript
Crea un archivo de declaración de tipos para añadir compatibilidad con TypeScript para 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;
}
Inicialización básica con el componente de script Next.js
Cuando utilice el componente Script del paso 1, añada la llamada de retorno onLoadpara 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 gancho useEffect
Si está utilizando 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
Encadene los métodos de .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 una sola página (SPA)
Para aplicaciones Next.js con enrutamiento del lado del cliente, es necesario realizar un seguimiento de los cambios de ruta de forma independiente a la inicialización inicial.
IMPORTANTE: Inicialice el SDK sólo una vez en la primera carga de página. Luego llame a window.singularSdk.pageVisit()en cada cambio de ruta subsiguiente.
// 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 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), 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);
Mejores prácticas para la inicialización de Next.js
- Inicialice el SDK sólo 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 SPAs, llame a
window.singularSdk.pageVisit()en los cambios de ruta para rastrear la navegación. -
Utilice
useRefpara realizar un seguimiento del estado de inicialización y evitar la inicialización duplicada. -
Añada comprobaciones null para
window.singularSdkantes de llamar a métodos para evitar errores durante la SSR. - Realice pruebas en modo de desarrollo para garantizar que el SDK se inicializa correctamente sin errores en el servidor.
Lista de comprobación para desarrolladores de Next.js
- Compruebe que el script se ha cargado correctamente en el paso 1.
- Configure las variables de entorno con sus credenciales del SDK.
- Cree declaraciones de tipos TypeScript para mejorar la experiencia del desarrollador.
-
Elija el método de inicialización: Componente de script
onLoado ganchouseEffect. -
Para SPAs, implemente el seguimiento de cambios de ruta con
pageVisit(). -
Pruebe la inicialización comprobando en la consola el evento
__PAGE_VISIT__.
-
Sustituya
'sdkKey'por su clave SDK actual. -
Sustituya
'sdkSecret'por su SDK Secret real. -
Sustituya
'productId'con su ID de producto real. Debe parecerse a:com.website-namey debe coincidir con el valor BundleID en la página Apps en la plataforma Singular.
Opciones de configuración
Mejore la configuración del WebSDK encadenando métodos de .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 del sitio que regresa 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 del método SingularConfig
Estos son todos los métodos ".with" disponibles.
| Método | Descripción | Más información |
.withCustomUserId(customId)
|
Enviar el ID de usuario a Singular | Establecer el ID de usuario |
.withProductName(productName)
|
Un nombre opcional para mostrar el producto | |
.withLogLevel(logLevel)
|
Configurar el nivel de registro: 0 - Ninguno (por defecto); 1 - Advertencia; 2 - Información; 3 - Depuración. | |
.withSessionTimeoutInMinutes(timeout)
|
Configurar el tiempo de espera de la sesión en minutos (por defecto: 30 minutos) |
|
.withAutoPersistentSingularDeviceId(domain)
|
Activar el seguimiento automático entre subdominios | Persistencia automática mediante cookies |
|
|
Activar el seguimiento manual entre subdominios | Establecer manualmente el ID de dispositivo singular |
.withInitFinishedCallback(callback)
|
Invocar una función callback cuando se completa la inicialización del SDK | Invocar una función de devolución de llamada cuando se ha completado la inicialización |
Lista de comprobación para desarrolladores
- Reúna sus credenciales del SDK y su ID de producto.
- Decida si necesita alguna configuración personalizada (ID de usuario, tiempo de espera, etc.).
- Construya una función Singular Intitialization y un objeto SingularConfig utilizando los ejemplos anteriores.
- Compruebe siempre que la inicialización sólo se activa una vez al cargar la página.
CONSEJO Para configuraciones avanzadas como el seguimiento de búsqueda orgánica, puede que necesite implementar JavaScript personalizado (por ejemplo, para ajustar los parámetros de consulta) antes de que se inicialice Singular SDK. Asegúrese de que su código personalizado se ejecuta antes de la función de inicialización de Singularpara que los cambios se capturen correctamente. Puede encontrar más información sobre cómo implementar el seguimiento de búsqueda orgánica aquí.
Paso 3: Seguimiento de eventos
Después de inicializar el SDK, puede realizar un seguimiento de los eventos personalizados cuando los usuarios realicen acciones importantes en su sitio web.
¡IMPORTANTE! Singular no bloquea los eventos duplicados. Es responsabilidad de los desarrolladores añadir protecciones contra los refrescos de página o la duplicación. Se recomienda incorporar algún método de deduplicación específico para los eventos de ingresos para evitar datos de ingresos erróneos. Consulte "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 simple 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
Seguimiento de un evento de conversión:
var eventName = 'sng_complete_registration';
window.singularSdk.conversionEvent(eventName);
Seguimiento de eventos de ingresos
Seguimiento de eventos de conversión con información de ingresos y adición de atributos opcionales para obtener más contexto:
- Pase la divisa de los ingresos como un código de divisa 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 mediante Singular SDK. En concreto, aprovecha el método window.addEventListener('load', ...) para detectar cuándo se han cargado todos los recursos de la página (p. ej., HTML, imágenes, secuencias de comandos). Tras este evento, se llama a la función event de Singular SDK para registrar un evento personalizado con atributos asociados, lo que permite el seguimiento de las interacciones del usuario con fines analíticos, como el control de las visitas a la página o las acciones del usuario, como los registros. Este mecanismo se utiliza habitualmente en la analítica web para capturar datos sobre el comportamiento de los usuarios, como visitas a páginas o acciones específicas, con atributos personalizables para obtener información detallada.
El mecanismo puede adaptarse para rastrear un evento específico (por ejemplo, un evento page_view ) con atributos personalizados, como se muestra a continuación. La estructura del código se mantiene 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 de botón
El mecanismo de seguimiento de eventos de clic de botón utiliza JavaScript para controlar cuándo un usuario hace clic en un botón con el ID checkout-buttony activa un evento checkout_started utilizando el SDK de Singular para el seguimiento analítico. El código emplea un receptor de eventos clickpara detectar la interacción con el botón, incluye un mecanismo para evitar la activación múltiple de eventos (mediante 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 que los analistas de comercio electrónico hagan un seguimiento de cuándo los usuarios inician un proceso de pago, garantizando datos precisos al disparar el evento sólo 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 controlar 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 receptor de eventos submitpara detectar el envío del formulario, incluye un mecanismo para evitar que se activen varios eventos (mediante un atributo data-event-fired) y envía el evento con un atributo personalizado (signup_method) a la plataforma Singular. Esto es ideal para el seguimiento de las inscripciones de usuarios en flujos de trabajo de análisis, como la supervisión de la adquisición de usuarios o métodos de inscripción, garantizando datos precisos al disparar el evento sólo una vez por sesión de página.
A continuación se muestra un ejemplo limpio adaptando 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: Establecer el ID de usuario del cliente
Puede enviar su ID de usuario interno a Singular utilizando un método del 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 PII (Personally Identifiable Information). Por ejemplo, no debe utilizar la dirección de correo electrónico, el nombre de usuario o el número de teléfono de un usuario. Singular recomienda utilizar un valor hash único sólo para sus datos de origen.
- El valor de ID de usuario que se pasa a Singular también debe ser el mismo ID de usuario interno que se captura en todas las plataformas (Web/Móvil/PC/Consola/Offline).
- Singular incluirá el ID de usuario en las exportaciones a nivel de usuario, ETL y devoluciones internas de BI (si está configurado). El ID de usuario es un dato de origen y Singular no lo comparte con terceros.
- El valor del ID de usuario, cuando se establece con el método Singular SDK, persistirá hasta que se desactive utilizando el método logout() o hasta que se elimine el almacenamiento local del navegador. Cerrar o actualizar el sitio web no elimina el ID de usuario.
- En modo privado/incógnito, el SDK no puede mantener 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 desestablecerlo (por ejemplo, si el usuario "cierra sesión" en la cuenta), llame al método logout().
NOTA: Si varios usuarios utilizan un único dispositivo, recomendamos implementar un flujo de cierre de sesión para establecer y desestablecer el ID de usuario para cada inicio y cierre de sesión.
Si ya conoce el ID de usuario cuando se inicializa Singular SDK en el sitio web, establezca el ID de usuario en el objeto config. De esta forma, 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 realiza un inicio de sesión. En ese caso, llame a login() una vez finalizado el flujo 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.
Mejores prácticas para el ID de usuario del cliente:
- Establézcalo en cuanto el usuario inicie sesión o se registre.
- Utilice el mismo ID en todas 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íelo como una cadena, aunque sea 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: Prevención de eventos duplicados
¡IMPORTANTE! Este es uno de los errores de implementación más comunes. Sin las salvaguardas adecuadas, los eventos pueden dispararse múltiples veces, inflando sus métricas.
Evitar la duplicación de eventos de ingresos en una aplicación web es importante para obtener análisis precisos y evitar la sobreinformación de conversiones. El objetivo es garantizar que cada evento único sólo se active una vez por sesión o visita a la página, incluso si el usuario vuelve a cargar la página o activa varios envíos sin darse cuenta.
- Se genera una clave única para cada evento (basada en los detalles del evento y en atributos personalizados opcionales).
- Esta clave se almacena en el sessionStorage del navegador (o en el localStorage para la deduplicación persistente).
- Antes de enviar, el código comprueba si ya se ha disparado un evento de ingresos con la misma carga útil.
- Si no es así, envía el evento y guarda la clave.
- Si es así, bloquea la repetición y notifica al usuario o al desarrollador.
Uso del método de almacenamiento de sesión
// Sends a revenue event to Singular WebSDK, preventing duplicates in the same session
// @param {string} eventName - Name of the revenue event (defaults to "web_purchase")
// @param {number} amount - Revenue amount (defaults to 0)
// @param {string} currency - Currency code (defaults to "USD", normalized to uppercase)
// @param {object} [attributes] - Optional key-value pairs for additional event data
function sendRevenueEvent(eventName, amount, currency, attributes) {
// Normalize inputs: set defaults and ensure currency is uppercase
eventName = eventName ? eventName : "web_purchase";
currency = currency ? currency.toUpperCase() : "USD";
amount = amount ? amount : 0;
// Create a payload object to standardize event data
var payload = {
eventName: eventName,
amount: amount,
currency: currency,
attributes: attributes ? attributes : null // Null if no attributes provided
};
// Generate a unique key for sessionStorage by hashing the payload
// This ensures deduplication of identical events in the same session
var storageKey = 'singular_revenue_' + btoa(JSON.stringify(payload));
// Check if the event was already sent in this session to prevent duplicates
if (!sessionStorage.getItem(storageKey)) {
// Mark event as sent in sessionStorage
sessionStorage.setItem(storageKey, 'true');
// Send revenue event to Singular SDK, including attributes if provided and valid
if (attributes && typeof attributes === 'object' && Object.keys(attributes).length > 0) {
window.singularSdk.revenue(eventName, currency, amount, attributes);
} else {
// Fallback to basic revenue event without attributes
window.singularSdk.revenue(eventName, currency, amount);
}
// Log event details for debugging
console.log("Revenue event sent:", payload);
} else {
// Log and alert if a duplicate event is detected
console.log("Duplicate revenue event prevented:", payload);
alert("Duplicate revenue event prevented!");
}
}
Paso 6: Probar su implementación
Después de implementar el SDK, verifique que funciona correctamente utilizando las herramientas de desarrollo de su navegador.
Verificar que el SDK está cargado
- Abra su sitio web en un navegador
- Abra las herramientas para desarrolladores (F12 o haga clic con el botón derecho → Inspeccionar)
- Vaya a la pestaña Consola
- Escriba
typeof singularSdky pulse Intro - Debería ver un objeto "function", no "undefined".
Verifique las solicitudes de red
- Abra Herramientas de desarrollo (F12)
- Vaya a la pestaña Red
- Vuelva a cargar la página
- Filtre por
singularosdk-api - Busque solicitudes a
sdk-api-v1.singular.net - Haga clic en una solicitud para ver los detalles
- Compruebe que el código de estado es
200 - Compruebe que la carga útil de la solicitud contiene su ID de producto. Éste estará 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.
Verificar 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 Payload o Solicitud
-
Compruebe que el parámetro del nombre del evento "
n" aparece en la solicitud.
Para las pruebas de extremo a extremo, utilice la consola de pruebas
Puede probar la integración Web SDK 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 del dispositivo Singular.
- Obtenga el ID de dispositivo Singular de la carga útil del navegador. Compruebe la captura de pantalla anterior para localizar el
SDIDen un evento. - La consola de pruebas debe permanecer en estado activo mientras realiza las pruebas en una ventana independiente. La consola no mostrará los eventos que se activen mientras esté cerrada o desconectada.
- Una vez añadido el SDID, puede recargar la página web y activar algunos eventos. El evento "__PAGE_VISIT__" y otros eventos (si se han disparado) se mostrarán en la consola SDK.
- Utilizar los registros de exportación(Reports & Insights Export Logs) es otra forma de verificar los resultados de las pruebas. Este conjunto de datos tiene un retraso de 1 hora.
Paso 7: Implementar el reenvío de Web a App
Reenvío de atribuciones de Web a App
Utilice Singular WebSDK para realizar un seguimiento de los recorridos de los usuarios desde su sitio web a su aplicación móvil, lo que permite una atribución precisa de la campaña web a las instalaciones y reutilizaciones de la aplicación móvil. Siga estos pasos para configurar el reenvío de la web a la 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 Web a aplicación de escritorio:
- Complete la configuración de la guía anterior.
- Utilice Singular Mobile Web-to-App Link (por ejemplo,
https://yourlink.sng.link/...) y la función WebSDKbuildWebToAppLink(link)con una biblioteca dinámica de códigos QR como QRCode.js. - Genera un código QR en tu 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.
- Explora nuestra Demo Singular WebSDK para ver un ejemplo práctico de generación de códigos QR y gestión de enlaces web-a-aplicación.
CONSEJO Las vistas web del navegador de la aplicación móvil (como las utilizadas por Facebook, Instagram y TikTok) pueden hacer que el ID de dispositivo de Singular cambie si un usuario cambia al navegador nativo del dispositivo, lo que interrumpe la atribución.
Para evitarlo, utilice siempre el formato de enlace de seguimiento 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 Singular WebSDK documentada. Esta muestra proporciona una separación clara para eventos personalizados, eventos de conversión, eventos de ingresos y soporte de enlace web-a-aplicación utilizando el reenvío web-a-aplicación soportado por WebSDK.
Puede ejecutar este código en un archivo HTML local o modificarlo en su sitio para la integración avanzada y la solución de problemas.
Copie y pegue el código de abajo 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';
var config = new SingularConfig(sdkKey, sdkSecret, productId)
.withInitFinishedCallback(function(initParams) {
var singularDeviceId = initParams.singularDeviceId;
console.log('Singular Device ID:', singularDeviceId);
// Optionally store for use in events
// sessionStorage.setItem('singularDeviceId', singularDeviceId);
});
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 Singulares
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 desees sobre el usuario, el modo/estado de la app o cualquier otra cosa.
-
Puedes definir hasta 5 propiedades globales como un objeto JSON válido. Las propiedades globales se mantienen en el navegador
localstoragehasta que se borra o se cambia el contexto del navegador. -
Cada nombre y valor de propiedad puede tener hasta 200 caracteres. Si pasas 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 (véase Exportación de registros de atribuciones) y en los postbacks.
-
Si es necesario, las propiedades globales pueden enviarse a un tercero desde Singular con fines de comparación.
Para soportar la configuración de Propiedades Globales antes de que el WebSDK sea inicializado debe implementar la función personalizada setGlobalPropertyBeforeInit, y llamarla antes de la Inicialización del SDK. Si no necesita configurar las propiedades globales antes de la inicialización, puede omitir este código personalizado.
Para manejar las Propiedades Globales después de la inicialización, debe utilizar las funciones del SDK: setGlobalProperties(), getGlobalProperties(), clearGlobalProperties().
/**
* Set a Singular global property before SDK initialization.
* Allows up to 5 key/value pairs. Optionally overwrites existing value for a key.
*
* @param {string} sdkKey - The Singular SDK key.
* @param {string} productId - The Singular product ID.
* @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.
*/
function setGlobalPropertyBeforeInit(sdkKey, productId, propertyKey, propertyValue, overrideExisting) {
const storageKey = `${sdkKey}_${productId}_global_properties`;
let properties = {};
// Try to load any existing properties
const existing = localStorage.getItem(storageKey);
if (existing) {
try {
properties = JSON.parse(existing) || {};
} catch (e) {
// If parsing fails, reset properties
properties = {};
}
}
// Only allow up to 5 keys
const propertyExists = Object.prototype.hasOwnProperty.call(properties, propertyKey);
const numKeys = Object.keys(properties).length;
if (!propertyExists && numKeys >= 5) {
console.warn("You can only set up to 5 Singular global properties.");
return;
}
// Apply logic for overwrite or skip
if (propertyExists && !overrideExisting) {
console.log(`Global property '${propertyKey}' exists and overrideExisting is false; property not changed.`);
return;
}
properties[propertyKey] = propertyValue;
localStorage.setItem(storageKey, JSON.stringify(properties));
console.log("Singular Global Properties set:", storageKey, properties);
}
/** * Establecer una propiedad global Singular antes 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 a establecer * @param {string} propertyValue - El valor de la propiedad a establecer * @param {boolean} overrideExisting - Si 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 activar el seguimiento de búsqueda orgánica. El código debe ser utilizado sólo como un ejemplo y actualizado y mantenido por el desarrollador web en función de las necesidades de su departamento de marketing. El seguimiento de Búsqueda Orgánica puede tener diferentes significados por anunciante. Por favor, revise el ejemplo y ajústelo a sus necesidades.
¿Por qué utilizarlo?
-
Asegura que las visitas de búsqueda orgánica sean rastreadas correctamente, incluso si no hay parámetros de campaña presentes.
-
Añade el parámetro Singular "Source"
wpsrccon el valor del (referrer) y el parámetro "Campaign Name"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, UTMs, etc.).
-
Si no hay parámetros de campaña presentes y el referente es un motor de búsqueda, añade:
-
wpsrc(con el referente como valor) -
wpcn(con OrganicSearch como valor)
-
-
Actualiza la URL en el navegador sin recargar la página.
-
Almacena la URL actual y el referente en
localStoragecomosng_urlysng_ref.
Uso de
-
Añade
setupOrganicSearchTracking.jsa tu sitio como una librería. -
Llama 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
Por defecto, el Singular WebSDK genera un Singular Device ID y lo persiste utilizando el almacenamiento del navegador. Dado que este almacenamiento no puede compartirse entre subdominios, el SDK acaba 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): Establecimiento manual del ID de dispositivo singular
Si no desea que Singular SDK mantenga el ID de dispositivo automáticamente, puede mantener el ID manualmente en todos los dominios, por ejemplo, utilizando una cookie de dominio de nivel superior o una cookie de servidor. El valor debe ser un ID generado previamente por Singular, en formato uuid4 válido.
NOTA: Puede leer el ID del dispositivo Singular utilizando singularSdk.getSingularDeviceId() después de llamar al método init o utilizando InitFinishedCallback.
| Método withPersistentSingularDeviceId | |
|---|---|
|
Descripción |
Inicializa el SDK con las opciones de configuración, incluido el Id. de dispositivo singular que se desea mantener. |
| Firma | withPersistentSingularDeviceId(singularDeviceId) |
| Ejemplo de uso |
|
Pasos siguientes
- Cree enlaces web en Singular para sus campañas web
- Siga nuestras guías para Google Ads Web, Facebook Web y TikTok Ads Web, si ejecuta campañas web para inventario móvil.
- Monitoriza tus datos en los informes de Singular