Pasar al contenido principal
osky
Cómo detectar alumnos sin cursos en SendPulse y automatizar su inscripción desde tu bot de WhatsApp
Tema SendPulse Academy + WhatsApp Bot

Cómo detectar alumnos sin cursos en SendPulse y automatizar su inscripción desde tu bot de WhatsApp

SendPulse lanzó hace poco el filtro "No course" en la pestaña de Students de su módulo Academy. Suena pequeño, pero para quien maneja cientos o miles de contactos en un bot de WhatsApp que también vende o regala cursos, es exactamente el gancho que necesitabas para cerrar el loop: saber quién está en tu base de datos pero todavía no ha arrancado ningún curso, y actuar sobre eso de forma automática.

Captura de pantalla estilizada del filtro No course en panel de Academy

Este tutorial te lleva de la mano desde la configuración en el panel de SendPulse hasta el snippet de código que dispara la inscripción y manda el mensaje de bienvenida por WhatsApp. Sin rodeos.


Requisitos previos

Antes de tocar código, confirma que tienes esto en orden:

  • Cuenta de SendPulse con el módulo Academy activo (plan de pago o trial con acceso a cursos).
  • Al menos un curso publicado en tu Academy.
  • Bot de WhatsApp activo en SendPulse (o vía la API de WhatsApp Cloud conectada a tu backend).
  • Credenciales de la API REST de SendPulse: client_id y client_secret (Configuración → API).
  • Node.js 20+ o Python 3.11+ en tu servidor/entorno local para los snippets.
  • ngrok o un servidor con IP pública si vas a recibir webhooks en desarrollo.

Paso 1 — Entiende el filtro "No course" en el panel

Entra a Academy → Students. En la barra de filtros verás el selector de estado; elige No course. SendPulse te muestra únicamente los learners registrados en tu academy que no tienen ningún curso asignado.

Esto puede pasar por varias razones reales de producción:

  • El alumno se registró por un formulario de captura pero nunca completó el checkout.
  • Lo importaste manualmente desde un CSV de leads.
  • Completó un curso y lo diste de baja del anterior sin reasignarlo.

El panel te permite seleccionarlos y enrolarlos o eliminarlos de forma masiva. Pero si quieres que esto pase de forma automática cuando un contacto responde a tu bot, necesitas la API.


Paso 2 — Obtén tu token de acceso a la API

SendPulse usa OAuth 2.0 con client_credentials. Cada token dura 3600 segundos; cachéalo.

// auth.js — Node.js 20+
const getToken = async () => {
  const res = await fetch('https://api.sendpulse.com/oauth/access_token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type: 'client_credentials',
      client_id: process.env.SP_CLIENT_ID,
      client_secret: process.env.SP_CLIENT_SECRET,
    }),
  });
  const data = await res.json();
  return data.access_token; // guárdalo en memoria o Redis con TTL de 3500s
};

Advertencia de producción: Nunca hagas una llamada de autenticación por cada request. En bots con alto volumen puedes llegar al rate limit de auth en minutos. Usa un singleton o un caché con TTL.


Paso 3 — Consulta los alumnos sin curso vía API

El endpoint para listar students de tu academy acepta parámetros de filtro:

// students.js
const getStudentsNoCourse = async (token, academyId) => {
  const url = `https://api.sendpulse.com/academy/${academyId}/students?status=no_course&limit=100&offset=0`;
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}` },
  });
  const data = await res.json();
  return data.data ?? []; // array de objetos student
};

Cada objeto student tiene al mínimo: id, email, phone, name, status.

Nota: Confirma el nombre exacto del parámetro status en la documentación vigente de SendPulse Academy API, ya que puede variar entre versiones. Al momento de escribir esto (junio 2026) el valor documentado es no_course.


Paso 4 — Inscribe al alumno en un curso

Una vez que tienes el student_id y el course_id, el endpoint de inscripción es un POST:

// enroll.js
const enrollStudent = async (token, academyId, courseId, studentId) => {
  const res = await fetch(
    `https://api.sendpulse.com/academy/${academyId}/courses/${courseId}/students`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ student_id: studentId }),
    }
  );
  if (!res.ok) {
    const err = await res.json();
    throw new Error(`Enroll failed: ${JSON.stringify(err)}`);
  }
  return await res.json();
};

Paso 5 — Conecta esto a tu bot de WhatsApp

Aquí está la parte jugosa. Cuando un contacto en tu bot llega a un nodo determinado (por ejemplo, responde "SÍ" a "¿Quieres acceder al curso gratis?"), tu webhook recibe el evento. En ese punto:

  1. Buscas al contacto por teléfono en Academy.
  2. Verificas si tiene status === 'no_course'.
  3. Si sí, lo inscribes.
  4. Mandas el mensaje de bienvenida con el link al curso.
// webhook-handler.js
import express from 'express';
import { getToken } from './auth.js';
import { getStudentsNoCourse } from './students.js';
import { enrollStudent } from './enroll.js';

const app = express();
app.use(express.json());

const ACADEMY_ID = process.env.SP_ACADEMY_ID;
const COURSE_ID = process.env.SP_COURSE_ID;

app.post('/webhook/whatsapp', async (req, res) => {
  const { contact_phone, message_text } = req.body; // adapta a tu estructura real
  res.sendStatus(200); // responde rápido, procesa async

  if (message_text?.toLowerCase() !== 'sí') return;

  try {
    const token = await getToken();
    const students = await getStudentsNoCourse(token, ACADEMY_ID);
    const student = students.find(s => s.phone === contact_phone);

    if (!student) {
      console.log(`Contacto ${contact_phone} no está en Academy todavía.`);
      return;
    }

    await enrollStudent(token, ACADEMY_ID, COURSE_ID, student.id);

    // Aquí mandas el mensaje de WhatsApp con el link al curso
    // usando la API de SendPulse Chatbots o WhatsApp Cloud directamente
    await sendWhatsAppMessage(contact_phone, token,
      `¡Listo! Ya tienes acceso al curso. Entra aquí: https://academy.tudominio.com/cursos/${COURSE_ID}`
    );
  } catch (e) {
    console.error('Error en flujo de inscripción:', e);
  }
});

app.listen(3000);

Advertencia crítica: Responde el webhook con 200 antes de procesar la lógica. SendPulse (y la mayoría de plataformas de mensajería) reintenta el webhook si no recibe respuesta en ~5 segundos. Si tu lógica tarda más, terminarás inscribiendo al mismo alumno dos o tres veces.


Paso 6 — Automatiza el barrido nocturno (cron job)

Además del flujo reactivo, conviene tener un job que cada noche revise a todos los alumnos con no_course y les mande un recordatorio por WhatsApp:

// cron.js — usa node-cron o un cronjob del sistema
import cron from 'node-cron';
import { getToken } from './auth.js';
import { getStudentsNoCourse } from './students.js';

cron.schedule('0 9 * * *', async () => { // cada día a las 9am
  const token = await getToken();
  const students = await getStudentsNoCourse(token, process.env.SP_ACADEMY_ID);
  console.log(`Alumnos sin curso: ${students.length}`);
  for (const s of students) {
    // lógica de reenganche: manda mensaje o crea tarea en CRM
    console.log(`Pendiente de reenganche: ${s.email} / ${s.phone}`);
  }
});

Consideraciones finales de producción

  • Idempotencia: Antes de inscribir, verifica que el alumno no esté ya en el curso para evitar duplicados si el webhook se dispara dos veces.
  • Rate limits: La API de SendPulse tiene límites por minuto. Si tienes más de 500 alumnos sin curso, pagina la consulta con offset y agrega un sleep entre inscripciones masivas.
  • Logs: Guarda cada inscripción en tu base de datos con timestamp y status. Si algo falla a las 3am, quieres saber exactamente qué pasó.
  • Variables de entorno: Nunca hardcodees client_id ni client_secret. Usa .env + dotenv o los secrets de tu plataforma de deploy.

Con esto tienes el flujo completo: desde el filtro visual en el panel hasta el bot que detecta, inscribe y notifica sin que tengas que mover un dedo.