Sistema de blogs Diarium
Universidad de Salamanca
Blog de id00804972
Otro sitio más de Diarium
 

Como Enviar SMS Masivos con API: Tutorial Paso a Paso

Como Enviar SMS Masivos con API Tutorial Paso a Paso

 

Introduccion

El envio de SMS masivos sigue siendo una de las formas mas efectivas de comunicacion directa con clientes. Con tasas de apertura superiores al 98%, los SMS superan ampliamente a otros canales de comunicacion.

En este tutorial aprenderemos a implementar un sistema de envio de SMS masivos utilizando APIs modernas.

Por Que SMS Masivos?

Ventajas del Canal SMS

  • 98% tasa de apertura (vs 20% email)
  • 90% leidos en 3 minutos
  • No requiere internet ni app
  • Funciona en cualquier telefono
  • Alcance universal

Casos de Uso

  1. Marketing: Promociones, ofertas flash
  2. Transaccional: Confirmaciones, alertas
  3. Notificaciones: Recordatorios, actualizaciones
  4. Autenticacion: Codigos OTP, verificacion

Requisitos Previos

  • Cuenta en una plataforma de SMS API
  • Conocimientos basicos de programacion
  • Base de datos con numeros de telefono (con consentimiento)

Paso 1: Configuracion Inicial

npm init -y
npm install @zavudev/sdk dotenv csv-parser
// config.js
require('dotenv').config();

module.exports = {
  apiKey: process.env.ZAVUDEV_API_KEY,
  defaultSenderId: process.env.SENDER_ID
};

Paso 2: Preparar la Lista de Contactos

// src/contacts.js
const fs = require('fs');
const csv = require('csv-parser');
const { parsePhoneNumber, isValidPhoneNumber } = require('libphonenumber-js');

async function cargarContactosCSV(archivo) {
  return new Promise((resolve, reject) => {
    const contactos = [];

    fs.createReadStream(archivo)
      .pipe(csv())
      .on('data', (row) => {
        const telefono = normalizarTelefono(row.telefono);
        if (telefono) {
          contactos.push({
            telefono,
            nombre: row.nombre,
            // Campos adicionales para personalizacion
            ...row
          });
        }
      })
      .on('end', () => resolve(contactos))
      .on('error', reject);
  });
}

function normalizarTelefono(telefono, pais = 'ES') {
  try {
    const parsed = parsePhoneNumber(telefono, pais);
    if (parsed.isValid()) {
      return parsed.format('E.164'); // +34612345678
    }
  } catch (e) {
    // Telefono invalido
  }
  return null;
}

module.exports = { cargarContactosCSV, normalizarTelefono };

Paso 3: Sistema de Envio Masivo

// src/broadcast.js
const Zavu = require('@zavudev/sdk').default;
const config = require('./config');

const zavu = new Zavudev({
  apiKey: config.apiKey
});

class SMSBroadcast {
  constructor(opciones = {}) {
    this.rateLimit = opciones.rateLimit || 50; // Mensajes por segundo
    this.reintentos = opciones.reintentos || 3;
    this.resultados = {
      exitosos: 0,
      fallidos: 0,
      errores: []
    };
  }

  async enviar(contactos, mensaje, opciones = {}) {
    console.log(`Iniciando envio a ${contactos.length} contactos...`);

    const lotes = this.dividirEnLotes(contactos, this.rateLimit);

    for (let i = 0; i < lotes.length; i++) {
      const lote = lotes[i];
      console.log(`Procesando lote ${i + 1}/${lotes.length}`);

      await this.procesarLote(lote, mensaje, opciones);

      // Pausa entre lotes para respetar rate limits
      if (i < lotes.length - 1) {
        await this.esperar(1000);
      }
    }

    return this.resultados;
  }

  async procesarLote(contactos, plantilla, opciones) {
    const promesas = contactos.map(contacto =>
      this.enviarMensaje(contacto, plantilla, opciones)
    );

    await Promise.all(promesas);
  }

  async enviarMensaje(contacto, plantilla, opciones, intento = 1) {
    try {
      const mensaje = this.personalizarMensaje(plantilla, contacto);

      const resultado = await zavu.messages.send({
        to: contacto.telefono,
        text: mensaje,
        ...opciones
      });

      this.resultados.exitosos++;

      return {
        exito: true,
        contacto,
        messageId: resultado.id
      };

    } catch (error) {
      if (intento < this.reintentos && this.esErrorReintentable(error)) {
        await this.esperar(Math.pow(2, intento) * 1000);
        return this.enviarMensaje(contacto, plantilla, opciones, intento + 1);
      }

      this.resultados.fallidos++;
      this.resultados.errores.push({
        contacto,
        error: error.message
      });

      return {
        exito: false,
        contacto,
        error: error.message
      };
    }
  }

  personalizarMensaje(plantilla, contacto) {
    return plantilla.replace(/\{\{(\w+)\}\}/g, (match, campo) => {
      return contacto[campo] || match;
    });
  }

  esErrorReintentable(error) {
    const codigosReintentables = ['rate_limited', 'timeout', 'server_error'];
    return codigosReintentables.includes(error.code);
  }

  dividirEnLotes(array, tamanoLote) {
    const lotes = [];
    for (let i = 0; i < array.length; i += tamanoLote) {
      lotes.push(array.slice(i, i + tamanoLote));
    }
    return lotes;
  }

  esperar(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

module.exports = SMSBroadcast;

Paso 4: Usando la API de Broadcasts

Las APIs de SMS modernas ofrecen endpoints especificos para broadcasts que simplifican el proceso:

// src/broadcast-api.js
const Zavu = require('@zavudev/sdk').default;

const zavu = new Zavudev({
  apiKey: process.env.ZAVUDEV_API_KEY
});

async function crearCampana() {
  // Crear broadcast
  const broadcast = await zavu.broadcasts.create({
    name: 'Promocion Black Friday',
    channel: 'sms',
    text: 'Hola {{nombre}}! Solo hoy: 50% OFF en toda la tienda. Usa codigo FRIDAY50. Valido hasta medianoche.'
  });

  console.log('Broadcast creado:', broadcast.id);

  // Agregar contactos
  const contactos = await cargarContactosCSV('clientes.csv');

  const resultado = await zavu.broadcasts.addContacts(broadcast.id, {
    contacts: contactos.map(c => ({
      recipient: c.telefono,
      templateVariables: { nombre: c.nombre }
    }))
  });

  console.log(`Agregados: ${resultado.added}, Duplicados: ${resultado.duplicates}`);

  // Enviar
  await zavu.broadcasts.send(broadcast.id);

  console.log('Campana iniciada!');

  return broadcast.id;
}

async function monitorearProgreso(broadcastId) {
  const progreso = await zavu.broadcasts.getProgress(broadcastId);

  console.log(`
    Estado: ${progreso.status}
    Total: ${progreso.total}
    Entregados: ${progreso.delivered}
    Fallidos: ${progreso.failed}
    Progreso: ${progreso.percentComplete}%
  `);

  return progreso;
}

Paso 5: Script Principal

// index.js
const SMSBroadcast = require('./src/broadcast');
const { cargarContactosCSV } = require('./src/contacts');

async function main() {
  // Cargar contactos
  const contactos = await cargarContactosCSV('contactos.csv');
  console.log(`Cargados ${contactos.length} contactos validos`);

  // Crear instancia del broadcast
  const broadcast = new SMSBroadcast({
    rateLimit: 50,
    reintentos: 3
  });

  // Mensaje con personalizacion
  const mensaje = 'Hola {{nombre}}, aprovecha nuestra oferta especial del 30% de descuento. Valido solo hoy!';

  // Enviar
  const resultados = await broadcast.enviar(contactos, mensaje);

  console.log(`
    === RESUMEN ===
    Exitosos: ${resultados.exitosos}
    Fallidos: ${resultados.fallidos}
  `);

  if (resultados.errores.length > 0) {
    console.log('Errores:', resultados.errores);
  }
}

main().catch(console.error);

Mejores Practicas

1. Validar Consentimiento

async function verificarConsentimiento(telefono) {
  const contacto = await database.contactos.findOne({ telefono });

  return contacto?.consentimiento?.sms === true
    && contacto?.consentimiento?.fecha > hace30Dias();
}

2. Ofrecer Opcion de Baja

const mensajeConBaja = `${mensaje}

Responde BAJA para cancelar.`;

// Procesar respuestas de baja
async function procesarRespuesta(from, texto) {
  if (texto.toLowerCase() === 'baja') {
    await database.contactos.update(
      { telefono: from },
      { $set: { 'consentimiento.sms': false } }
    );
    await enviarConfirmacionBaja(from);
  }
}

3. Programar Envios

const cron = require('node-cron');

// Enviar promociones los viernes a las 10:00
cron.schedule('0 10 * * 5', async () => {
  const contactos = await obtenerContactosActivos();
  await enviarPromocionSemanal(contactos);
});

4. Monitorear Resultados

async function generarReporte(broadcastId) {
  const broadcast = await zavu.broadcasts.get(broadcastId);

  return {
    nombre: broadcast.name,
    total: broadcast.totalContacts,
    entregados: broadcast.deliveredCount,
    fallidos: broadcast.failedCount,
    tasaEntrega: (broadcast.deliveredCount / broadcast.totalContacts * 100).toFixed(2) + '%',
    costo: broadcast.actualCost
  };
}

Espana (LSSI)

  • Consentimiento previo obligatorio
  • Identificar al remitente
  • Ofrecer metodo de baja facil

Latinoamerica

Cada pais tiene regulaciones especificas. Consulta la normativa local antes de enviar.

Optimizacion de Costos

Estrategias

  1. Segmentar audiencia: Solo enviar a contactos relevantes
  2. Limpiar lista: Eliminar numeros invalidos
  3. Horarios optimos: Enviar cuando hay mayor engagement
  4. Mensajes concisos: Menos caracteres = menos costo
function optimizarMensaje(texto) {
  // Maximo 160 caracteres para un solo SMS
  if (texto.length > 160) {
    console.warn('Mensaje excede 160 caracteres. Se enviara como SMS concatenado.');
  }

  // Evitar caracteres especiales que aumentan longitud
  return texto
    .replace(/[""]/g, '"')
    .replace(/['']/g, "'");
}

Conclusion

El envio de SMS masivos con API permite comunicarse de forma efectiva con miles de clientes de manera programatica y escalable. Siguiendo las mejores practicas de validacion, personalizacion y cumplimiento legal, puedes maximizar el impacto de tus campanas.

Las plataformas de SMS API modernas simplifican enormemente este proceso, ofreciendo funcionalidades como broadcasts gestionados, personalizacion automatica y analytics en tiempo real.

Recursos

Aún no hay comentarios.

Deja un comentario


*

Política de privacidad
Studii Salmantini. Campus de excelencia internacional