Raphael Serafim· Publicado el 10 de septiembre de 2026· 9 min de lectura

Lista de contactos para WhatsApp: opt-in, limpieza y opt-out automático

La lista decide el resultado del envío antes de que salga el primer mensaje. Cómo registrar un opt-in que sirva de prueba, limpiar números inválidos antes de enviar, procesar el opt-out automáticamente y medir la salud de la base.

Ver como Markdown

La lista decide el resultado del envío antes de que salga el primer mensaje. Puedes acertar la plantilla, el ritmo y el horario, y aun así quemar el número — si la lista está mal.

Este artículo trata de la lista. Sobre la mecánica del volumen, está cola, rate limit y reintentos.

Un opt-in que sirva de prueba

Casi todo sistema guarda el consentimiento así:

acepta_whatsapp BOOLEAN DEFAULT FALSE

Eso no es un registro de consentimiento. Es una afirmación sin prueba — y el día que alguien lo cuestione, no tienes qué mostrar.

El registro útil guarda circunstancia:

CREATE TABLE optin_whatsapp (
  id            BIGSERIAL PRIMARY KEY,
  contacto_id   BIGINT NOT NULL,
  numero        VARCHAR(20) NOT NULL,
  aceptado_en   TIMESTAMPTZ NOT NULL,
  origen        VARCHAR(50) NOT NULL,   -- checkout | formulario | atencion
  texto_mostrado TEXT NOT NULL,         -- lo que la persona leyó al aceptar
  ip            INET,
  user_agent    TEXT,
  revocado_en   TIMESTAMPTZ             -- NULL = activo
);

El campo que más falta en los sistemas es texto_mostrado. El consentimiento es para una finalidad: quien aceptó "recibir novedades de mi pedido" no aceptó una promoción semanal. Guardar el texto es lo que te permite responder para qué dijo que sí.

revocado_en en lugar de DELETE: borrar el registro borra también la prueba de que hubo consentimiento antes. El historial es la defensa.

Lo que no es opt-in

  • Lista comprada.
  • Números raspados de un sitio, un grupo o un marketplace.
  • "El que no quiera que avise."
  • Base de hace cinco años sin contacto desde entonces.
  • Consentimiento para correo reutilizado para WhatsApp.

Todos funcionan un tiempo. Todos terminan igual: bloqueo, denuncia, calidad del número por el piso, plantilla pausada.

Limpiar antes de enviar

Un número inválido no es solo desperdicio: en la API oficial, el intento de entrega fallido cuenta en contra de la reputación. Conviene filtrar antes.

Normaliza primero. Una base latinoamericana trae de todo: (55) 5512-3456, +52 55 5512 3456, 04455....

function normalizar(bruto, paisPorDefecto = '52') {
  let n = String(bruto).replace(/\D/g, '');

  // Quita el cero o el prefijo de larga distancia inicial
  n = n.replace(/^0+/, '');

  // Sin código de país, asume el del país de la operación
  const codigos = ['52', '54', '56', '57'];   // MX, AR, CL, CO
  if (!codigos.some((c) => n.startsWith(c))) n = paisPorDefecto + n;

  if (n.length < 10 || n.length > 15) return null;
  return n;
}

Después comprueba quién tiene WhatsApp de verdad:

async function limpiar(numeros) {
  const validos = [];
  for (const bruto of numeros) {
    const n = normalizar(bruto);
    if (!n) { await marcar(bruto, 'formato_invalido'); continue; }

    const existe = await wa.contact.checkNumber(n);
    if (!existe) { await marcar(n, 'sin_whatsapp'); continue; }

    validos.push(n);
    await esperar(200);   // la verificación también tiene límite
  }
  return validos;
}

Hazlo antes de la campaña, no durante. Y guarda el resultado: reverificar la base entera en cada envío es desperdicio.

Opt-out automático

Ignorar un pedido de baja es el camino más rápido a la denuncia — y una denuncia pesa mucho más que un bloqueo.

const BAJA = /^\s*(salir|parar|baja|cancelar|eliminar|stop|no)\s*!?\.?\s*$/i;

async function procesar(msg) {
  if (BAJA.test(msg.text.body)) {
    await registrarOptOut(msg.from);
    await enviar(msg.from, 'Listo, no volverás a recibir nuestros mensajes. ' +
                           'Si necesitas algo, escríbenos por acá.');
    return;   // no sigue a la atención normal
  }
  await atencionNormal(msg);
}

La expresión regular exige que sea el mensaje completo, a propósito. Buscar la palabra suelta marcaría como baja a quien escribió "quiero cancelar mi pedido", que es un cliente pidiendo atención, no una baja.

El error que casi todos cometen: revisar el opt-out solo al armar la campaña. Una campaña de 50 mil contactos tarda horas en drenar, y quien pide salir en el minuto 10 sigue recibiendo por horas. La comprobación tiene que estar al momento de enviar cada mensaje:

// en el worker, justo antes del envío
if (await estaEnOptOut(job.data.to)) return;   // salió después de encolarse

Y deja la salida visible. Un "responde SALIR para no recibir más" al pie de la plantilla convierte denuncias en bajas — la diferencia entre perder un contacto y lastimar el número.

Medir la salud de la base

Tres indicadores, por campaña:

IndicadorQué observar
Tasa de entregaPor debajo del 95%, hay números muertos: toca limpiar
BloqueosSi suben, el contenido no corresponde a lo que se consintió
DenunciasCualquier aumento es grave; es lo que más tumba números

Si los bloqueos y las denuncias suben, enviar más empeora. La respuesta es bajar la frecuencia, revisar el contenido y reconfirmar el opt-in de los contactos antiguos.

Reconfirmar una base antigua

Una base parada más de un año es prácticamente una base nueva, y dispararle promociones es pedir denuncias. El camino seguro es un único mensaje de utilidad, con salida explícita:

¡Hola, Ana! Hace tiempo que no hablamos. ¿Quieres seguir recibiendo novedades de Tienda Ejemplo por acá? Responde para continuar o SALIR para no recibir más.

Quien no responde no es un "sí". Es silencio — y el silencio, en la duda, se trata como una baja.

Conclusión

Una buena lista es una lista lo bastante chica como para que sepas de dónde salió cada contacto. La tentación de enviarle a todo el mundo es fuerte, y el costo aparece una campaña después.

Opt-in con prueba, limpieza antes del envío, opt-out comprobado al momento de enviar y tres indicadores vigilados. Ninguna de las cuatro cosas es difícil — todas son tediosas, y por eso casi nadie las hace.

¿Listo para automatizar tu WhatsApp?

Crea tu cuenta gratis y empieza a enviar mensajes por la API en minutos.

Empezar gratis

Preguntas frecuentes

¿Qué es el opt-in en WhatsApp y cómo se registra?+

Es el consentimiento de la persona para recibir tus mensajes en ese canal. Registrarlo significa guardar cuándo, dónde y cómo se dio: fecha y hora, origen (formulario, checkout, atención), el texto que se mostró en ese momento y el identificador de la sesión. Una columna booleana 'aceptó' no es un registro de consentimiento: es una afirmación sin prueba.

¿Cómo saber si un número tiene WhatsApp antes de enviar?+

La API tiene verificación de número. Correr esa comprobación sobre la base antes del envío elimina los inválidos, y eso importa porque en la API oficial un intento de entrega fallido cuenta en contra de la reputación de tu número.

¿Cómo funciona el opt-out automático?+

Detectas en el webhook las palabras de baja (salir, parar, baja, cancelar), marcas al contacto como opt-out antes de cualquier otro procesamiento y se lo confirmas. El punto crítico es que la comprobación de opt-out ocurra al momento de encolar cada envío, no solo al armar la campaña.

¿Necesito opt-in también en la API no oficial?+

Legalmente sí: las leyes de protección de datos de la región regulan el tratamiento de datos personales, no la tecnología que uses para enviar. En la práctica también, porque los bloqueos y las denuncias de usuarios tumban cualquier número. La API no oficial no fiscaliza el opt-in, lo que aumenta tu responsabilidad en lugar de reducirla.

¿Cuántos mensajes aguanta mi base antes de quemarse?+

No es el volumen lo que quema, es la tasa de rechazo. Una base de 50 mil contactos con opt-in reciente y contenido esperado tiene menos problemas que una de 500 comprada. Sigue los bloqueos y denuncias por campaña: cuando ese número sube, el problema es la lista o el contenido, y enviar más solo acelera el daño.

Sigue leyendo