SDK web: guía de implementación de JavaScript nativo

Documento

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: 
  • 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. 
  • 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.

Latest VersionSpecific VersionUsing NPMNext.js / React
<script src="https://web-sdk-cdn.singular.net/singular-sdk/latest/singular-sdk.js"></script>

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.
  1. Cree una función de inicialización y llámela en DOM Ready después de que se cargue la página.
  2. Asegúrese de que la inicialización se produzca antes de que se notifiquen otros eventos de Singular.
  3. 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.
Basic InitializationNext.js / React

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 window.singularSdk.init(config)

Navegación a una nueva ruta/página

Llamar a window.singularSdk.pageVisit()

En la carga inicial en SPA

No llamar a window.singularSdk.pageVisit() (La inicialización proporcionará el evento de la primera visita a la página).

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();
});
  • 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-name y 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:

Config with Options
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

.withPersistentSingularDeviceId(singularDeviceId)

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.

Basic EventConversion EventRevenue Event

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);

Patrones comunes de implementación de eventos

Page Load EventsButton Click EventsForm Submission Events

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);
});

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:
Set the User IDUnset the User ID
// After user logs in or signs up
var userId = 'user_12345';
window.singularSdk.login(userId);

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

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

  1. Abra su sitio web en un navegador
  2. Abra las herramientas de desarrollo (F12 o clic con el botón derecho → Inspeccionar).
  3. Vaya a la pestaña Consola
  4. Escriba typeof singularSdk y pulse Intro
  5. Debería ver un objeto «función», no «indefinido»

Verifique las solicitudes de red

  1. Abre las herramientas de desarrollo (F12).
  2. Ve a la pestaña Red
  3. Vuelva a cargar la página
  4. Filtra por singular o sdk-api
  5. Busca las solicitudes a sdk-api-v1.singular.net
  6. Haga clic en una solicitud para ver los detalles
  7. Verifica que el código de estado sea 200
  8. 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

  1. Active un evento en su sitio web (haga clic en un botón, envíe un formulario, etc.).
  2. En la pestaña Red, busque una nueva solicitud a sdk-api-v1.singular.net
  3. Haga clic en la solicitud y vea la pestaña Carga útil o Solicitud.
  4. Comprueba que el nombre de tu evento «n » aparece en la solicitud.

    event_test.png

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.

  1. En la plataforma Singular, vaya a Herramientas de desarrollador > Consola de pruebas.
  2. Haga clic en Añadir dispositivo.
  3. Seleccione la plataforma«Web» e introduzca el ID de dispositivo de Singular.
  4. 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.
  5. 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.
  6. 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.
  7. 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.

  1. 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.
  2. 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 WebSDK buildWebToAppLink(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.

Demostración de Singular WebSDK (código fuente)
#

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

Habilitar banners Singular
#

INFORMACIÓN: Los banners singulares son una función empresarial. Para obtener más información sobre esta función, póngase en contacto con su gestor de éxito del cliente.

Los banners singulares se pueden mostrar en su sitio web móvil para dirigir a los usuarios web de forma fluida a su aplicación y mostrar el contenido más relevante de la aplicación. Una vez que habilite los banners singulares en su sitio web, su organización podrá diseñar, implementar y mantener fácilmente los banners a través de la interfaz de usuario de banners singulares.

Artículos relacionados

Guía de implementación paso a paso

  1. Añada el script Singular Native JavaScript WebSDK a su sitio web.

    Siga la guía de integración anterior para añadir el SDK web JavaScript nativo de Singular a su sitio web antes de continuar con la implementación de los banners.

  2. Otorgue a Singular permiso para acceder a los datos de las sugerencias del cliente

    Con las limitaciones en los datos del agente de usuario en los navegadores web basados en Chromium , implementadas en febrero-marzo de 2023, los anunciantes ahora necesitan obtener datos de sugerencias del cliente, así como dar permiso al Singular WebSDK para recibir estos datos. Para obtener más información, consulte las preguntas frecuentes sobre banners.

    El sitio web debe:

    • Empezar a solicitar Client Hints (accept-ch header).
    • Conceder permiso a Singular (como tercero) para que el navegador envíe a Singular las sugerencias del cliente en las solicitudes de obtención de banners (permissions-policy header).

    Añada los siguientes encabezados de respuesta a la respuesta de carga de la página:

    accept-ch:
    sec-ch-ua-model,
    sec-ch-ua-platform-version,
    sec-ch-ua-full-version-list
    
    permissions-policy:
    ch-ua-model=("https://sdk-api-v1.singular.net"),
    ch-ua-platform-version=("https://sdk-api-v1.singular.net"),
    ch-ua-full-version-list=("https://sdk-api-v1.singular.net")
  3. Añadir la opción de configuración de banners a la inicialización de WebSDK

    Los banners de Singular muestran avisos inteligentes de descarga de aplicaciones en su sitio web móvil, al tiempo que conservan la atribución de la fuente de marketing original. Cuando se habilita con compatibilidad web-a-aplicación, el SDK rastrea qué red publicitaria o campaña llevó al usuario a su sitio y, a continuación, atribuye la posterior instalación de la aplicación a esa fuente original.

    /**
     * Initialize Singular SDK with Banner support and web-to-app attribution.
     * This enables smart banners that direct mobile web users to download your app
     * while preserving the original marketing source (UTM parameters, ad network, etc.)
     * for proper attribution when the app is installed.
     * @param {string} sdkKey - Your Singular SDK Key
     * @param {string} sdkSecret - Your Singular SDK Secret
     * @param {string} productId - Your Product ID (e.g., com.your-website)
     */
    function initSingularSDK() {
      // Enable web-to-app tracking to preserve attribution data
      var bannersOptions = new BannersOptions().withWebToAppSupport();
      
      // Configure SDK with banner support
      var config = new SingularConfig(sdkKey, sdkSecret, productId)
        .withBannersSupport(bannersOptions);
      
      // Initialize the SDK
      window.singularSdk.init(config);
      
      // Display the smart banner
      window.singularSdk.showBanner();
    }
    
    // Call on DOM ready
    document.addEventListener('DOMContentLoaded', function() {
      initSingularSDK();
    });
  4. Volver a mostrar el banner en la ruta de la página (solo aplicaciones de una sola página)

    Si su aplicación es una aplicación de página única, debe ocultar y volver a mostrar el banner en cada ruta de página. Esto garantiza que Singular muestre el banner adecuado para su experiencia web.

    Para ocultar y volver a mostrar el banner, utilice el siguiente código:

    window.singularSdk.hideBanner();
    window.singularSdk.showBanner();
  5. [Opción avanzada] Personalizar la configuración de los enlaces

    Singular ofrece una forma de personalizar los enlaces del banner mediante código.

    Para personalizar los enlaces:

    • Cree un objeto LinkParams y utilice una o varias de las funciones siguientes. Hágalo antes de llamar a window.singularSdk.showBanner() .
    • A continuación, pase el objeto LinkParams cuando llame a window.singularSdk.showBanner().

    Ejemplo:

    // Define a LinkParams object
    let bannerParams = new LinkParams();
    
    // Configure link options (see details on each option below)
    bannerParams.withAndroidRedirect("androidRedirectValue");
    bannerParams.withAndroidDL("androidDLParamValue");
    bannerParams.withAndroidDDL("androidDDLparamValue");
    bannerParams.withIosRedirect("iosRedirectValue");
    bannerParams.withIosDL("iosDLValue");
    bannerParams.withIosDDL("iosDDLValue");
    
    // Show the banner with the defined options
    window.singularSdk.showBanner(bannerParams);

    Lista de opciones:

    Método Descripción
    withAndroidRedirect Pasa un enlace de redireccionamiento a la página de descarga de tu aplicación Android , normalmente una página de Play Store.
    withAndroidDL Pasa un enlace profundo a una página dentro de tu aplicación Android.
    withAndroidDDL Pase un enlace profundo diferido, es decir, un enlace a una página de su aplicación Android que el usuario aún no ha instalado .
    withIosRedirect Pasa un enlace de redireccionamiento a la página de descarga de tu aplicación iOS , normalmente una página de App Store.
    withIosDL Pasa un enlace profundo a una página dentro de tu aplicación iOS.
    withIosDDL Pase un enlace profundo diferido, es decir, un enlace a una página de su aplicación iOS que el usuario aún no ha instalado .
  6. [Opción avanzada] Utilizar código para forzar la ocultación/visualización de banners

    Como se menciona en el paso 3, si tienes una aplicación de una sola página, debes utilizar los métodos hideBanner() y showBanner() en cada ruta de página para garantizar que se muestre el banner adecuado (véase más arriba).

    hideBanner() y showBanner() también están disponibles para su uso en todo el código si desea ocultar un banner que de otro modo se mostraría, o volver a mostrar un banner que ocultó.

    Método Descripción
    singularSdk.hideBanner() Ocultar un banner visible de su página.
    singularSdk.showBanner() Mostrar el banner preconfigurado.
    singularSdk.showBanner(params) Mostrar el banner preconfigurado, pero anular los enlaces con las opciones definidas en el objeto linkParams ( véase el paso 4).

Propiedades globales

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 localstorage o 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() .

.withGlobalProperties()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);           

Seguimiento de búsquedas orgánicas

Ejemplo de 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» wpsrc con el valor de la referencia (referrer) y el parámetro «Nombre de la campaña» wpcn como «OrganicSearch» a la URL para una atribución clara .

  • Almacena la URL actual y el referente en localStorage para su uso posterior.

  • Puro JavaScript, sin dependencias y fácil de integrar.

Cómo funciona

  1. Comprueba la URL de la página en busca de parámetros de campaña conocidos de (Google, Facebook, TikTok, UTM, etc.).

  2. 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)
  3. Actualiza la URL en el navegador sin recargar la página.

  4. Almacena la URL actual y el referente en localStorage como sng_url y sng_ref.

Uso

  1. Añade setupOrganicSearchTracking.js a tu sitio como biblioteca.
  2. Llame a la función setupOrganicSearchTracking() ANTES de inicializar el WebSDK.
setupOrganicSearchTracking.js
// 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): establecer el ID de dispositivo singular manualmente
#

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
function initSingularSDK() {
  const config = new SingularConfig(sdkKey, sdkSecret, productId)
    .withPersistentSingularDeviceId(singularDeviceId);
  window.singularSdk.init(config);
}

Próximos pasos

Artículos relacionados