Saltar al contenido principal
Utiliza estas plantillas HTML de copiar y pegar para construir mensajes dentro de la aplicación personalizados de OneSignal más rápido. Nuestro Editor HTML de Mensajes Dentro de la Aplicación te permite controlar completamente el diseño y comportamiento de tu mensaje dentro de la aplicación usando HTML, CSS y JavaScript. El editor no incluye plantillas integradas, pero esta página proporciona ejemplos listos para usar que puedes pegar en el editor y personalizar.
Estas plantillas se ejecutan dentro de un webview de mensaje dentro de la aplicación. Para cerrar mensajes, abrir URLs, etiquetar usuarios y capturar clics, usa la API JS de Mensaje Dentro de la Aplicación.

Requisitos previos

Antes de empezar, te recomendamos revisar:
No pongas secretos (claves API, tokens) en el código de plantillas. Trata todas las entradas de mensajes dentro de la aplicación como no confiables y valídalas en tu aplicación o backend.

Cómo usar las plantillas

  1. En OneSignal, ve a Mensajes > Dentro de la Aplicación > Nuevo Mensaje Dentro de la Aplicación.
  2. Selecciona el editor HTML.
  3. Encuentra una plantilla abajo.
  4. Copia el HTML completo del bloque de código y pégalo en el editor.
  5. Actualiza los marcadores de posición (URLs, endpoints, fechas y copia).
  6. Prueba en un dispositivo real, luego publica.

Plantillas disponibles

email-form

Formulario de Recolección de Correo

Pregunta por el correo del usuario y envíalo a tu aplicación mediante nombre de clic.
sms-form

Formulario de Recolección de Números de Teléfono

Pregunta y obtén consentimiento para enviar SMS. Incluye número de teléfono en formato E.164 y envíalo a tu aplicación mediante nombre de clic.
checklist-survey

Encuesta de Lista de Verificación

Encuesta de selección múltiple que puedes enviar a tu backend o convertir en etiquetas.
count_down

Cuenta Regresiva

Temporizador de cuenta regresiva para promociones sensibles al tiempo.
promo-wheel

Rueda Promocional

Experiencia promocional de girar para ganar (personaliza el manejo de promociones).
quiz_modal

Modal de Quiz

Experiencia de quiz que puede etiquetar usuarios con su puntuación.
ranking-survey

Encuesta de Calificación

Encuesta de calificación de 1–5 (envía a tu endpoint o etiqueta usuario).
ui-ui

Reproductor de Audio/Video

UI simple de vista previa de audio para un archivo MP3 directo.
vertical-swiping

Deslizamiento Vertical

Tour de incorporación o características de múltiples diapositivas con deslizamiento vertical.

Formulario de correo

Recopila Suscripciones de correo a través de un mensaje dentro de la aplicación. Cómo funciona este formulario:
  1. El usuario ingresa una dirección de correo y marca una casilla de consentimiento.
  2. Al enviar, se llama a la API Crear Usuario de OneSignal para crear la Suscripción de correo en tu aplicación.
  3. Además, la plantilla llama OneSignalIamApi.addClickName(e, email) que pasa la dirección de correo al Listener de Clic de Mensaje Dentro de la Aplicación de nuestro SDK.
  4. Dentro de tu aplicación, puedes agregar el Listener de Clic de Mensaje Dentro de la Aplicación para leer el nombre del clic y pasar el correo al método addEmail de nuestro SDK.
Puedes notar que tanto el paso 2 como el paso 4 involucran crear la Suscripción de correo.
  • El paso 2 no requiere agregar código directamente en la aplicación pero tampoco agrega la Suscripción de correo al usuario si llamaste al método login.
  • El paso 4 requiere código adicional (el Listener de Clic de Mensaje Dentro de la Aplicación) pero también agrega la Suscripción de correo al usuario si llamaste al método login.
**Reemplaza YOUR_APP_ID con tu ID de Aplicación de OneSignal que se encuentra en Configuración > Claves e IDs.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- Prevenir zoom de iOS en el foco del input -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <style>
        /* ===== RESET ===== */
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        /* ===== BASE ===== */
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: transparent;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            padding: 20px;
        }
        
        /* ===== CARD ===== */
        .container {
            position: relative;
            background: #ffffff;
            border-radius: 16px;
            padding: 32px 24px;
            max-width: 340px;
            width: 100%;
            box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
            text-align: center;
            overflow: hidden;
        }
        
        /* ===== BOTÓN CERRAR ===== */
        .close-btn {
            position: absolute;
            top: 12px;
            right: 12px;
            background: none;
            border: none;
            font-size: 24px;
            color: #999;
            cursor: pointer;
            padding: 4px 8px;
            line-height: 1;
        }
        
        .close-btn:hover {
            color: #333;
        }
        
        /* ===== TIPOGRAFÍA ===== */
        h1 {
            font-size: 22px;
            font-weight: 600;
            color: #333;
            margin-bottom: 8px;
        }
        
        p {
            font-size: 14px;
            color: #666;
            margin-bottom: 24px;
            line-height: 1.5;
        }
        
        /* ===== INPUT DE CORREO ===== */
        .email-input {
            width: 100%;
            padding: 14px 12px;
            font-size: 16px; /* Prevenir zoom de iOS */
            border: 2px solid #e0e0e0;
            border-radius: 10px;
            margin-bottom: 16px;
            outline: none;
            transition: border-color 0.2s;
            touch-action: manipulation; /* Mejorar experiencia móvil */
        }
        
        .email-input:focus {
            border-color: #007AFF;
        }
        
        .email-input::placeholder {
            color: #aaa;
        }
        
        /* ===== CHECKBOX DE CONSENTIMIENTO ===== */
        .consent-wrapper {
            display: flex;
            align-items: flex-start;
            gap: 10px;
            text-align: left;
            margin-bottom: 16px;
        }
        
        .consent-wrapper input[type="checkbox"] {
            width: 18px;
            height: 18px;
            margin-top: 2px;
            cursor: pointer;
            flex-shrink: 0;
        }
        
        .consent-wrapper label {
            font-size: 13px;
            color: #666;
            line-height: 1.4;
            cursor: pointer;
        }
        
        /* ===== BOTÓN ENVIAR ===== */
        .submit-btn {
            width: 100%;
            padding: 14px 24px;
            font-size: 16px;
            font-weight: 600;
            color: #fff;
            background: #007AFF;
            border: none;
            border-radius: 10px;
            cursor: pointer;
            transition: background 0.2s, opacity 0.2s;
        }
        
        .submit-btn:hover {
            background: #0056b3;
        }
        
        .submit-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            opacity: 0.7;
        }
        
        /* ===== MENSAJES DE ESTADO ===== */
        .error-msg,
        .success-msg {
            font-size: 12px;
            margin-top: -12px;
            margin-bottom: 12px;
            display: none;
        }
        
        .error-msg {
            color: #dc3545;
        }
        
        .success-msg {
            color: #28a745;
        }
        
        /* ===== RESPONSIVE ===== */
        @media (max-width: 480px) {
            body {
                padding: 15px;
            }
            
            .container {
                padding: 24px 20px;
            }
            
            h1 {
                font-size: 20px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- Botón cerrar -->
        <button class="close-btn" onclick="OneSignalIamApi.close(event)">&times;</button>
        
        <!-- Título y descripción -->
        <h1>Obtén Actualizaciones Exclusivas</h1>
        <p>Ingresa tu correo para recibir las últimas noticias y ofertas especiales.</p>
        
        <!-- Formulario -->
        <form id="emailForm">
            <!-- Mensaje de error -->
            <div class="error-msg" id="errorMsg">Por favor ingresa un correo válido</div>
            
            <!-- Mensaje de éxito -->
            <div class="success-msg" id="successMsg">¡Gracias! Te contactaremos pronto.</div>
            
            <!-- Input de correo -->
            <input 
                type="email" 
                class="email-input" 
                id="emailInput" 
                placeholder="Ingresa tu correo" 
                required
            />
            
            <!-- Checkbox de consentimiento -->
            <div class="consent-wrapper">
                <input type="checkbox" id="consent" required />
                <label for="consent">
                    Acepto recibir actualizaciones por correo y contenido promocional. Puedes cancelar la suscripción en cualquier momento.
                </label>
            </div>
            
            <!-- Botón enviar -->
            <button type="submit" class="submit-btn" id="submitBtn">
                Suscribirse a Actualizaciones
            </button>
        </form>
    </div>

    <script>
        // Esperar carga del DOM
        document.addEventListener('DOMContentLoaded', function() {
            const form = document.getElementById('emailForm');
            const emailInput = document.getElementById('emailInput');
            const consentCheckbox = document.getElementById('consent');
            const submitBtn = document.getElementById('submitBtn');
            const errorMsg = document.getElementById('errorMsg');
            const successMsg = document.getElementById('successMsg');

            // Función de validación de correo
            function isValidEmail(email) {
                const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
                return emailRegex.test(email);
            }

            // Mostrar mensaje de error
            function showError(message) {
                errorMsg.textContent = message;
                errorMsg.style.display = 'block';
                successMsg.style.display = 'none';
            }

            // Mostrar mensaje de éxito
            function showSuccess(message) {
                successMsg.textContent = message;
                successMsg.style.display = 'block';
                errorMsg.style.display = 'none';
            }

            // Ocultar mensajes
            function hideMessages() {
                errorMsg.style.display = 'none';
                successMsg.style.display = 'none';
            }

            // Validación de entrada y actualización de UI
            function validateForm() {
                const email = emailInput.value.trim();
                const isEmailValid = isValidEmail(email);
                const isConsentGiven = consentCheckbox.checked;
                
                submitBtn.disabled = !(isEmailValid && isConsentGiven);
                
                if (email && !isEmailValid) {
                    showError('Por favor ingresa un correo válido');
                } else if (!email || isEmailValid) {
                    hideMessages();
                }
            }

            // Event listeners
            emailInput.addEventListener('input', validateForm);
            emailInput.addEventListener('blur', validateForm);
            consentCheckbox.addEventListener('change', validateForm);

            // Manejo de envío de formulario
            form.addEventListener('submit', async function(e) {
                e.preventDefault();
                
                const email = emailInput.value.trim();
                
                if (!isValidEmail(email)) {
                    showError('Por favor ingresa un correo válido');
                    return;
                }
                
                if (!consentCheckbox.checked) {
                    showError('Por favor acepta recibir actualizaciones por correo');
                    return;
                }

                // Deshabilitar formulario
                submitBtn.disabled = true;
                submitBtn.textContent = 'Procesando...';

                try {
                    // Paso 2: Crear suscripción de correo mediante REST API
                    const response = await fetch('https://api.onesignal.com/apps/YOUR_APP_ID/users', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({
                            properties: {
                                tags: {
                                    email_collected_via_iam: 'true'
                                }
                            },
                            subscriptions: [
                                {
                                    type: 'Email',
                                    token: email,
                                    enabled: true
                                }
                            ]
                        })
                    });

                    if (response.ok) {
                        showSuccess('¡Gracias! Te contactaremos pronto.');
                    } else {
                        throw new Error('Fallo en suscripción');
                    }

                    // Paso 3: Pasar correo al SDK mediante nombre de clic
                    OneSignalIamApi.addClickName(e, email);

                    // Retrasar cierre del mensaje
                    setTimeout(() => {
                        OneSignalIamApi.close(e);
                    }, 2000);

                } catch (error) {
                    console.error('Error de suscripción de correo:', error);
                    showError('Ocurrió un error, por favor intenta de nuevo');
                    
                    // Re-habilitar formulario
                    submitBtn.disabled = false;
                    submitBtn.textContent = 'Suscribirse a Actualizaciones';
                }
            });

            // Validación inicial
            validateForm();
        });
    </script>
</body>
</html>
Requisitos del Paso 4:
  1. Mantén la llamada addClickName en tu manejador de envío HTML.
  2. Usa el Listener de Clic de Mensaje Dentro de la Aplicación de nuestro SDK para leer la entrada.
  3. Cuando el nombre del clic se vea como un correo, llama al método addEmail en el Listener de Clic de Mensaje Dentro de la Aplicación.
Ejemplo usando el Listener de Clic de Mensaje Dentro de la Aplicación y método addEmail:
// Ejemplo de manejador de clic de mensaje dentro de la aplicación para capturar correos y teléfonos en mensajes HTML dentro de la aplicación
class InAppMessageClickHandler: NSObject, OSInAppMessageClickListener {
    func onClick(event: OSInAppMessageClickEvent) {
        // Obtener el nombre del clic (action ID) del evento
        let clickName = event.result.actionId
        print("Mensaje dentro de la aplicación clicado con actionId: \(clickName ?? "nil")")
        
        guard let value = clickName else { return }
        
        // Verificar si el nombre del clic se ve como una dirección de correo
        if value.contains("@") && value.contains(".") {
            OneSignal.User.addEmail(value)
            print("Correo agregado a OneSignal: \(value)")
        }
        // Verificar si el nombre del clic se ve como un número de teléfono E.164 (+1XXXXXXXXXX)
        else if value.hasPrefix("+") && value.count >= 11 {
            OneSignal.User.addSms(value)
            print("SMS agregado a OneSignal: \(value)")
        }
    }
}

Formulario SMS

Recopila Suscripciones SMS a través de un mensaje dentro de la aplicación. Cómo funciona este formulario:
  1. El usuario selecciona su código de país, ingresa un número de 10 dígitos y marca una casilla de consentimiento.
  2. Al enviar, se llama a la API Crear Usuario de OneSignal para crear la Suscripción SMS en tu aplicación.
  3. Además, la plantilla llama OneSignalIamApi.addClickName(e, e164Phone) que pasa el número de teléfono al Listener de Clic de Mensaje Dentro de la Aplicación de nuestro SDK.
  4. Dentro de tu aplicación, puedes agregar el Listener de Clic de Mensaje Dentro de la Aplicación para leer el nombre del clic y pasar el número de teléfono al método addSms de nuestro SDK.
Puedes notar que tanto el paso 2 como el paso 4 involucran crear la Suscripción SMS.
  • El paso 2 no requiere agregar código directamente en la aplicación pero tampoco agrega la Suscripción SMS al usuario si llamaste al método login.
  • El paso 4 requiere código adicional (el Listener de Clic de Mensaje Dentro de la Aplicación) pero también agrega la Suscripción SMS al usuario si llamaste al método login.
**Reemplaza YOUR_APP_ID con tu ID de Aplicación de OneSignal que se encuentra en Configuración > Claves e IDs.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <style>
        /* ===== RESET ===== */
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        /* ===== BASE ===== */
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: transparent;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            padding: 20px;
        }
        
        /* ===== CARD ===== */
        .container {
            position: relative;
            background: #ffffff;
            border-radius: 16px;
            padding: 32px 24px;
            max-width: 340px;
            width: 100%;
            box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
            text-align: center;
            overflow: hidden;
        }
        
        /* ===== BOTÓN CERRAR ===== */
        .close-btn {
            position: absolute;
            top: 12px;
            right: 12px;
            background: none;
            border: none;
            font-size: 24px;
            color: #999;
            cursor: pointer;
            padding: 4px 8px;
            line-height: 1;
        }
        
        .close-btn:hover {
            color: #333;
        }
        
        /* ===== TIPOGRAFÍA ===== */
        h1 {
            font-size: 22px;
            font-weight: 600;
            color: #333;
            margin-bottom: 8px;
        }
        
        p {
            font-size: 14px;
            color: #666;
            margin-bottom: 24px;
            line-height: 1.5;
        }
        
        /* ===== INPUT DE TELÉFONO ===== */
        .phone-input-wrapper {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 16px;
            width: 100%;
        }
        
        .country-select {
            display: flex;
            align-items: center;
            padding: 14px 8px;
            background: #f5f5f5;
            border: 2px solid #e0e0e0;
            border-radius: 10px;
            font-size: 14px;
            color: #333;
            flex-shrink: 0;
            cursor: pointer;
            outline: none;
            appearance: none;
            -webkit-appearance: none;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: right 8px center;
            padding-right: 24px;
        }
        
        .country-select:focus {
            border-color: #007AFF;
        }
        
        .phone-input {
            flex: 1;
            min-width: 0;
            padding: 14px 12px;
            font-size: 16px;
            border: 2px solid #e0e0e0;
            border-radius: 10px;
            outline: none;
            transition: border-color 0.2s;
            width: 100%;
            touch-action: manipulation;
        }
        
        .phone-input:focus {
            border-color: #007AFF;
        }
        
        .phone-input::placeholder {
            color: #aaa;
        }
        
        /* ===== CHECKBOX DE CONSENTIMIENTO ===== */
        .consent-wrapper {
            display: flex;
            align-items: flex-start;
            gap: 10px;
            text-align: left;
            margin-bottom: 16px;
        }
        
        .consent-wrapper input[type="checkbox"] {
            width: 18px;
            height: 18px;
            margin-top: 2px;
            cursor: pointer;
            flex-shrink: 0;
        }
        
        .consent-wrapper label {
            font-size: 13px;
            color: #666;
            line-height: 1.4;
            cursor: pointer;
        }
        
        /* ===== BOTÓN ENVIAR ===== */
        .submit-btn {
            width: 100%;
            padding: 14px 24px;
            font-size: 16px;
            font-weight: 600;
            color: #fff;
            background: #007AFF;
            border: none;
            border-radius: 10px;
            cursor: pointer;
            transition: background 0.2s, opacity 0.2s;
        }
        
        .submit-btn:hover {
            background: #0056b3;
        }
        
        .submit-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            opacity: 0.7;
        }
        
        /* ===== MENSAJES DE ESTADO ===== */
        .error-msg,
        .success-msg {
            font-size: 12px;
            margin-top: -12px;
            margin-bottom: 12px;
            display: none;
        }
        
        .error-msg {
            color: #dc3545;
        }
        
        .success-msg {
            color: #28a745;
        }
        
        /* ===== RESPONSIVE ===== */
        @media (max-width: 480px) {
            body {
                padding: 15px;
            }
            
            .container {
                padding: 24px 20px;
            }
            
            h1 {
                font-size: 20px;
            }
            
            .phone-input-wrapper {
                flex-direction: column;
            }
            
            .country-select {
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- Botón cerrar -->
        <button class="close-btn" onclick="OneSignalIamApi.close(event)">&times;</button>
        
        <!-- Título y descripción -->
        <h1>Obtén Actualizaciones SMS</h1>
        <p>Ingresa tu número de teléfono para recibir actualizaciones importantes e información promocional.</p>
        
        <!-- Formulario -->
        <form id="smsForm">
            <!-- Mensaje de error -->
            <div class="error-msg" id="errorMsg">Por favor ingresa un número de teléfono válido</div>
            
            <!-- Mensaje de éxito -->
            <div class="success-msg" id="successMsg">¡Gracias! Te contactaremos pronto.</div>
            
            <!-- Input de teléfono -->
            <div class="phone-input-wrapper">
                <select class="country-select" id="countrySelect">
                    <option value="+1">🇺🇸 +1</option>
                    <option value="+44">🇬🇧 +44</option>
                    <option value="+33">🇫🇷 +33</option>
                    <option value="+49">🇩🇪 +49</option>
                    <option value="+34">🇪🇸 +34</option>
                    <option value="+39">🇮🇹 +39</option>
                    <option value="+81">🇯🇵 +81</option>
                    <option value="+82">🇰🇷 +82</option>
                    <option value="+86">🇨🇳 +86</option>
                    <option value="+91">🇮🇳 +91</option>
                    <option value="+61">🇦🇺 +61</option>
                    <option value="+55">🇧🇷 +55</option>
                    <option value="+52">🇲🇽 +52</option>
                    <option value="+7">🇷🇺 +7</option>
                    <option value="+90">🇹🇷 +90</option>
                </select>
                <input 
                    type="tel" 
                    class="phone-input" 
                    id="phoneInput" 
                    placeholder="Ingresa número de teléfono" 
                    required
                />
            </div>
            
            <!-- Checkbox de consentimiento -->
            <div class="consent-wrapper">
                <input type="checkbox" id="consent" required />
                <label for="consent">
                    Acepto recibir actualizaciones SMS y contenido promocional. Se aplican tarifas estándar de mensajes. Puedes cancelar la suscripción en cualquier momento.
                </label>
            </div>
            
            <!-- Botón enviar -->
            <button type="submit" class="submit-btn" id="submitBtn">
                Suscribirse a Actualizaciones SMS
            </button>
        </form>
    </div>

    <script>
        // Esperar carga del DOM
        document.addEventListener('DOMContentLoaded', function() {
            const form = document.getElementById('smsForm');
            const countrySelect = document.getElementById('countrySelect');
            const phoneInput = document.getElementById('phoneInput');
            const consentCheckbox = document.getElementById('consent');
            const submitBtn = document.getElementById('submitBtn');
            const errorMsg = document.getElementById('errorMsg');
            const successMsg = document.getElementById('successMsg');

            // Función de validación de teléfono
            function isValidPhone(phone) {
                // Validación básica: 7-15 dígitos, posiblemente incluyendo guiones, espacios o paréntesis
                const phoneRegex = /^[\d\s\-\(\)]{7,15}$/;
                return phoneRegex.test(phone.replace(/\s/g, ''));
            }

            // Formatear a formato E.164
            function formatToE164(countryCode, phone) {
                // Quitar todos los caracteres no numéricos
                const cleanPhone = phone.replace(/\D/g, '');
                return countryCode + cleanPhone;
            }

            // Mostrar mensaje de error
            function showError(message) {
                errorMsg.textContent = message;
                errorMsg.style.display = 'block';
                successMsg.style.display = 'none';
            }

            // Mostrar mensaje de éxito
            function showSuccess(message) {
                successMsg.textContent = message;
                successMsg.style.display = 'block';
                errorMsg.style.display = 'none';
            }

            // Ocultar mensajes
            function hideMessages() {
                errorMsg.style.display = 'none';
                successMsg.style.display = 'none';
            }

            // Validación de formulario y actualización de UI
            function validateForm() {
                const phone = phoneInput.value.trim();
                const isPhoneValid = isValidPhone(phone);
                const isConsentGiven = consentCheckbox.checked;
                
                submitBtn.disabled = !(isPhoneValid && isConsentGiven);
                
                if (phone && !isPhoneValid) {
                    showError('Por favor ingresa un número de teléfono válido');
                } else if (!phone || isPhoneValid) {
                    hideMessages();
                }
            }

            // Event listeners
            phoneInput.addEventListener('input', validateForm);
            phoneInput.addEventListener('blur', validateForm);
            consentCheckbox.addEventListener('change', validateForm);
            countrySelect.addEventListener('change', validateForm);

            // Manejo de envío de formulario
            form.addEventListener('submit', async function(e) {
                e.preventDefault();
                
                const phone = phoneInput.value.trim();
                const countryCode = countrySelect.value;
                
                if (!isValidPhone(phone)) {
                    showError('Por favor ingresa un número de teléfono válido');
                    return;
                }
                
                if (!consentCheckbox.checked) {
                    showError('Por favor acepta recibir actualizaciones SMS');
                    return;
                }

                // Deshabilitar formulario
                submitBtn.disabled = true;
                submitBtn.textContent = 'Procesando...';

                const e164Phone = formatToE164(countryCode, phone);

                try {
                    // Paso 2: Crear suscripción SMS mediante REST API
                    const response = await fetch('https://api.onesignal.com/apps/YOUR_APP_ID/users', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({
                            properties: {
                                tags: {
                                    sms_collected_via_iam: 'true'
                                }
                            },
                            subscriptions: [
                                {
                                    type: 'SMS',
                                    token: e164Phone,
                                    enabled: true
                                }
                            ]
                        })
                    });

                    if (response.ok) {
                        showSuccess('¡Gracias! Te contactaremos pronto.');
                    } else {
                        throw new Error('Fallo en suscripción');
                    }

                    // Paso 3: Pasar número de teléfono al SDK mediante nombre de clic
                    OneSignalIamApi.addClickName(e, e164Phone);

                    // Retrasar cierre del mensaje
                    setTimeout(() => {
                        OneSignalIamApi.close(e);
                    }, 2000);

                } catch (error) {
                    console.error('Error de suscripción SMS:', error);
                    showError('Ocurrió un error, por favor intenta de nuevo');
                    
                    // Re-habilitar formulario
                    submitBtn.disabled = false;
                    submitBtn.textContent = 'Suscribirse a Actualizaciones SMS';
                }
            });

            // Validación inicial
            validateForm();
        });
    </script>
</body>
</html>

Encuesta de Lista de Verificación

Encuesta de selección múltiple que envía resultados a tu backend.
  • Establece tu endpoint en handleSurveyAnswer().
  • Actualiza los valores name de los checkboxes y etiquetas para coincidir con tu pregunta.
Si dejas var url = "", la solicitud fallará. Establece un endpoint real o reemplaza fetch() con etiquetado (ejemplo abajo).
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Mensaje Dentro de la Aplicación OneSignal</title>
    <!-- Google Fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@500;700&family=Raleway:wght@500;700&display=swap"
      rel="stylesheet"
    />
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        margin: 0;
        padding-top: var(--safe-area-inset-top);
        padding-right: var(--safe-area-inset-right);
        padding-bottom: calc(var(--safe-area-inset-bottom) + 20px);
        padding-left: var(--safe-area-inset-left);
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
        display: flex;
        align-items: center;
      }

      .center-modal {
        position: relative;
        background: #fae8cd;
        margin: 18px;
        border-radius: 8px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        height: 85%;
        max-height: 640px;
        width: 100%;
        box-shadow: rgb(0 0 0 / 30%) 0px 0px 12.5px,
          rgb(0 0 0 / 15%) 0px 0px 2.5px;
      }

      .center-modal .close-button {
        position: absolute;
        top: 10px;
        right: 10px;
        background: rgba(255, 255, 255, 0.5);
        border: none;
        z-index: 1;
        display: flex;
        justify-content: center;
        flex-direction: column;
        align-items: center;
        /* Consejo: Haz tu botón de cerrar relativamente grande para que sea fácil de hacer clic */
        min-width: 36px;
        min-height: 36px;
        border-radius: 50%;
      }

      .center-modal .headings {
        padding: 24px 24px 0 24px;
      }

      .center-modal h1 {
        margin: 32px 0 0 0;
        color: #222;
        text-decoration: none;
        font-family: Raleway;
        font-size: 24px;
        font-weight: 700;
        line-height: 28px;
        letter-spacing: 0px;
        text-align: left;
      }

      .center-modal h2 {
        font-family: Raleway;
        font-size: 16px;
        font-weight: 500;
        line-height: 24px;
        letter-spacing: 0px;
        text-align: left;
        color: #555;
        margin: 8px 0 0 0;
      }

      .center-modal .content {
        padding: 24px;
        display: flex;
        flex-direction: column;
      }

      .checkbox-group {
        display: flex;
        flex-direction: column;
        gap: 12px;
        margin: 16px 0;
      }

      .checkbox-item {
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 8px;
        background: rgba(255, 255, 255, 0.3);
        border-radius: 6px;
      }

      .checkbox-item input[type="checkbox"] {
        width: 20px;
        height: 20px;
      }

      .checkbox-item label {
        font-family: "Nunito Sans", sans-serif;
        font-size: 14px;
        font-weight: 500;
        color: #333;
        cursor: pointer;
      }

      .submit-button {
        background: #ff6b35;
        color: white;
        border: none;
        padding: 12px 24px;
        border-radius: 6px;
        font-family: "Nunito Sans", sans-serif;
        font-size: 16px;
        font-weight: 700;
        cursor: pointer;
        margin-top: 16px;
        transition: background-color 0.2s;
      }

      .submit-button:hover {
        background: #e55a2b;
      }

      .submit-button:disabled {
        background: #ccc;
        cursor: not-allowed;
      }
    </style>
  </head>
  <body>
    <div class="center-modal">
      <!-- Botón cerrar -->
      <button class="close-button" onclick="OneSignalIamApi.close(event)">

      </button>
      
      <div class="headings">
        <h1>¿Qué te gusta más?</h1>
        <h2>Cuéntanos tus preferencias para mejores recomendaciones</h2>
      </div>
      
      <div class="content">
        <div class="checkbox-group">
          <div class="checkbox-item">
            <input type="checkbox" id="option1" name="preferences" value="sports">
            <label for="option1">Deportes y Fitness</label>
          </div>
          <div class="checkbox-item">
            <input type="checkbox" id="option2" name="preferences" value="technology">
            <label for="option2">Tecnología y Gadgets</label>
          </div>
          <div class="checkbox-item">
            <input type="checkbox" id="option3" name="preferences" value="food">
            <label for="option3">Comida y Cocina</label>
          </div>
          <div class="checkbox-item">
            <input type="checkbox" id="option4" name="preferences" value="travel">
            <label for="option4">Viajes y Aventura</label>
          </div>
          <div class="checkbox-item">
            <input type="checkbox" id="option5" name="preferences" value="entertainment">
            <label for="option5">Entretenimiento y Juegos</label>
          </div>
        </div>
        
        <button class="submit-button" onclick="handleSurveyAnswer(event)">
          Enviar Respuestas
        </button>
      </div>
    </div>

    <script>
      function handleSurveyAnswer(e) {
        const checkboxes = document.querySelectorAll('input[name="preferences"]:checked');
        const selectedValues = Array.from(checkboxes).map(cb => cb.value);
        
        if (selectedValues.length === 0) {
          alert('Por favor selecciona al menos una opción');
          return;
        }

        // Opción 1: Enviar a tu endpoint backend
        const url = ""; // Reemplaza con tu URL de endpoint
        
        if (url) {
          fetch(url, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              survey: 'preferences',
              answers: selectedValues,
              timestamp: new Date().toISOString()
            })
          }).then(response => {
            console.log('Respuestas de encuesta enviadas');
          }).catch(error => {
            console.error('Error enviando respuestas de encuesta:', error);
          });
        }
        
        // Opción 2: Usar etiquetas de OneSignal (sin backend requerido)
        // Descomenta las siguientes líneas para usar etiquetas:
        /*
        const tags = {};
        selectedValues.forEach(value => {
          tags[`preference_${value}`] = 'true';
        });
        OneSignalIamApi.sendTags(tags);
        */
        
        // Pasar selecciones mediante nombre de clic
        OneSignalIamApi.addClickName(e, selectedValues.join(','));
        
        // Cerrar mensaje
        OneSignalIamApi.close(e);
      }
    </script>
  </body>
</html>