SMS 7 min de lectura

Enviar SMS desde Node.js: Tutorial Práctico con API REST

Aprende a integrar el envío de SMS en tus aplicaciones Node.js con ejemplos prácticos, manejo de errores y mejores prácticas.

En este tutorial te mostraremos cómo enviar SMS desde Node.js usando la API de LETEL. Cubriremos desde la configuración básica hasta ejemplos avanzados con Express.

Requisitos Previos

Antes de comenzar, asegúrate de tener:

node --version  # Version 18+ recomendada
npm --version   # Version 9+

Instalar Dependencias

Instala las dependencias necesarias:

const axios = require('axios');

const sendSMS = async (phone, message) => {
  try {
    const response = await axios.post(
      process.env.LETEL_API_URL || 'https://api.letel.cl/v1/sms/send',
      { phone, message },
      {
        headers: {
          'Authorization': `Bearer ${process.env.LETEL_API_KEY}`,
          'Content-Type': 'application/json'
        }
      }
    );
    
    console.log('SMS enviado:', response.data);
    return response.data;
  } catch (error) {
    console.error('Error al enviar SMS:', error.response?.data || error.message);
    throw error;
  }
};

// Uso
sendSMS('+56912345678', 'Hola desde Node.js');

Configuración Básica

Crea un archivo .env con tus credenciales:

# .env
LETEL_API_KEY=tu_api_key_aqui
LETEL_API_URL=https://api.letel.cl/v1/sms/send

Código Básico

Ejemplo mínimo para enviar un SMS:

const axios = require('axios');
require('dotenv').config();

async function sendSMS(phone, message) {
  try {
    const response = await axios.post(
      process.env.LETEL_API_URL,
      {
        phone: phone,
        message: message
      },
      {
        headers: {
          'Authorization': `Bearer ${process.env.LETEL_API_KEY}`,
          'Content-Type': 'application/json'
        }
      }
    );

    console.log('SMS enviado:', response.data);
    return response.data;
  } catch (error) {
    console.error('Error al enviar SMS:', error.response?.data || error.message);
    throw error;
  }
}

// Ejemplo de uso
sendSMS('56912345678', 'Hola desde Node.js!');

Servicio SMS Avanzado

Clase completa con manejo de errores, reintentos y métodos útiles:

const axios = require('axios');
require('dotenv').config();

class SmsService {
  constructor(apiKey, apiUrl) {
    this.apiKey = apiKey;
    this.apiUrl = apiUrl;
    this.client = axios.create({
      baseURL: this.apiUrl,
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      }
    });
  }

  async send(phone, message, options = {}) {
    const payload = {
      phone: this.formatPhone(phone),
      message,
      ...options
    };

    try {
      const response = await this.client.post('/send', payload);
      return {
        success: true,
        messageId: response.data.messageId,
        status: response.data.status,
        timestamp: response.data.timestamp
      };
    } catch (error) {
      return {
        success: false,
        error: error.response?.data?.message || error.message
      };
    }
  }

  async sendVerificationCode(phone, code) {
    const message = `Tu código de verificación LETEL es: ${code}. Válido por 10 minutos.`;
    return this.send(phone, message);
  }

  async sendBulk(recipients) {
    const results = await Promise.allSettled(
      recipients.map((r) => this.send(r.phone, r.message))
    );

    return {
      total: recipients.length,
      successful: results.filter(
        (r) => r.status === 'fulfilled' && r.value?.success
      ).length,
      failed: results.filter(
        (r) => r.status === 'fulfilled' && !r.value?.success
      ).length,
      details: results
    };
  }

  formatPhone(phone) {
    // Eliminar caracteres no numéricos
    let cleaned = phone.replace(/\D/g, '');

    // Agregar código de país si no lo tiene
    if (!cleaned.startsWith('56')) {
      cleaned = '56' + cleaned;
    }

    return cleaned;
  }
}

// Uso
const smsService = new SmsService(
  process.env.LETEL_API_KEY,
  process.env.LETEL_API_URL
);

// Enviar SMS simple
await smsService.send('912345678', 'Mensaje de prueba');

// Enviar código de verificación
await smsService.sendVerificationCode('912345678', '123456');

// Envío masivo
await smsService.sendBulk([
  { phone: '912345678', message: 'Hola cliente 1' },
  { phone: '987654321', message: 'Hola cliente 2' }
]);

Integración con Express

Cómo crear una API REST con endpoints para enviar SMS:

const express = require('express');
const SmsService = require('./smsService');

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

const smsService = new SmsService(
  process.env.LETEL_API_KEY,
  process.env.LETEL_API_URL
);

// Endpoint para enviar SMS
app.post('/api/sms/send', async (req, res) => {
  const { phone, message } = req.body;
  
  if (!phone || !message) {
    return res.status(400).json({
      error: 'Phone and message are required'
    });
  }

  const result = await smsService.send(phone, message);
  res.json(result);
});

// Endpoint para codigo de verificacion
app.post('/api/sms/verify', async (req, res) => {
  const { phone, code } = req.body;
  
  const result = await smsService.sendVerificationCode(phone, code);
  res.json(result);
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Manejo de Errores

Ejemplo de implementación con reintentos:

// Manejo de errores recomendado
async function sendSMSWithRetry(phone, message, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await axios.post(
        process.env.LETEL_API_URL,
        { phone, message },
        {
          headers: {
            'Authorization': `Bearer ${process.env.LETEL_API_KEY}`,
            'Content-Type': 'application/json'
          },
          timeout: 10000 // 10 segundos de timeout
        }
      );
      
      return { success: true, data: response.data };
    } catch (error) {
      console.error(`Intento ${attempt} fallido:`, error.message);
      
      // No reintentar errores de cliente (4xx)
      if (error.response?.status >= 400 && error.response?.status < 500) {
        return { 
          success: false, 
          error: 'Error del cliente',
          details: error.response.data 
        };
      }
      
      // Esperar antes de reintentar (exponential backoff)
      if (attempt < maxRetries) {
        await new Promise(r => setTimeout(r, attempt * 1000));
      }
    }
  }
  
  return { success: false, error: 'Maximos reintentos alcanzados' };
}

Validación de Números Telefónicos

Antes de enviar SMS, es crítico validar que el número de teléfono sea correcto. Un número inválido resultará en error y desperdiciará créditos. Aquí te mostramos cómo implementar validación robusta:

// Validador de números telefónicos para Chile
class PhoneValidator {
  // Valida formato de teléfono chileno
  static isValidChilePhone(phone) {
    // Eliminar espacios y caracteres especiales
    let cleaned = phone.replace(/\D/g, '');

    // Debe empezar con código de país (56) o ser número local
    if (cleaned.startsWith('56')) {
      // Formato internacional: 56 + 9 dígitos = 11 total
      return cleaned.length === 11;
    } else {
      // Formato local: 9 dígitos
      return cleaned.length === 9;
    }
  }

  // Normaliza el número al formato internacional
  static normalize(phone) {
    let cleaned = phone.replace(/\D/g, '');

    // Si no tiene código de país, agregarlo
    if (!cleaned.startsWith('56')) {
      cleaned = '56' + cleaned;
    }

    return cleaned;
  }

  // Validación completa con error messages
  static validate(phone) {
    if (!phone || phone.trim() === '') {
      return { valid: false, error: 'Número de teléfono requerido' };
    }

    if (!this.isValidChilePhone(phone)) {
      return { valid: false, error: 'Formato de teléfono inválido. Usa 9XXXXXXXX o +569XXXXXXXX' };
    }

    return { valid: true, normalized: this.normalize(phone) };
  }
}

// Uso en tu aplicación
const result = PhoneValidator.validate('912345678');
if (result.valid) {
  await smsService.send(result.normalized, 'Tu mensaje aquí');
} else {
  console.error(result.error);
}

Integración con Formulario HTML

Un caso real común es capturar números de teléfono desde un formulario HTML y enviar SMS. Aquí mostramos cómo implementarlo de forma segura con validación en cliente y servidor:

// Backend: Express endpoint para enviar SMS desde formulario
const express = require('express');
const axios = require('axios');
require('dotenv').config();

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

class PhoneValidator {
  static validate(phone) {
    const cleaned = phone.replace(/\D/g, '');
    if (cleaned.length !== 9 && cleaned.length !== 11) {
      return { valid: false, error: 'Teléfono inválido' };
    }
    return { valid: true, normalized: (cleaned.length === 9 ? '56' + cleaned : cleaned) };
  }
}

// Endpoint para enviar SMS desde formulario
app.post('/api/contact/send-sms', async (req, res) => {
  try {
    const { phone, message, email } = req.body;

    // Validar entrada
    if (!phone || !message) {
      return res.status(400).json({
        error: 'Teléfono y mensaje requeridos'
      });
    }

    const validation = PhoneValidator.validate(phone);
    if (!validation.valid) {
      return res.status(400).json({
        error: validation.error
      });
    }

    // Enviar SMS
    const response = await axios.post(
      process.env.LETEL_API_URL,
      {
        phone: validation.normalized,
        message: message
      },
      {
        headers: {
          'Authorization': `Bearer ${process.env.LETEL_API_KEY}`,
          'Content-Type': 'application/json'
        },
        timeout: 10000
      }
    );

    // Guardar en logs (opcional)
    console.log(`SMS enviado a ${validation.normalized} desde ${email}`);

    res.json({
      success: true,
      messageId: response.data.messageId,
      message: 'SMS enviado correctamente'
    });

  } catch (error) {
    console.error('Error al enviar SMS:', error.message);
    res.status(500).json({
      error: 'Error al enviar SMS. Intenta nuevamente.'
    });
  }
});

app.listen(3000, () => {
  console.log('Servidor listo en puerto 3000');
});

Testing y Debugging

Durante el desarrollo, necesitas probar tus envíos SMS sin gastar créditos reales. Aquí mostramos cómo hacerlo y cómo debuggear errores comunes:

// Herramientas para testing y debugging

// 1. Mock de API para testing sin gastar créditos
class MockSmsService {
  async send(phone, message) {
    console.log('[MOCK] Enviando SMS');
    console.log('Teléfono:', phone);
    console.log('Mensaje:', message);
    return {
      success: true,
      messageId: 'mock-' + Date.now(),
      status: 'delivered'
    };
  }
}

// 2. Usar en testing
const isMockMode = process.env.NODE_ENV === 'test';
const smsService = isMockMode
  ? new MockSmsService()
  : new RealSmsService();

// 3. Logging detallado para debugging
function logSmsEvent(event, details) {
  const timestamp = new Date().toISOString();
  console.log(`[${timestamp}] SMS_${event}:`, JSON.stringify(details, null, 2));
}

// 4. Ejemplo de envío con logging
async function sendSmsWithDebug(phone, message) {
  logSmsEvent('SEND_START', { phone, messageLength: message.length });

  try {
    const result = await smsService.send(phone, message);
    logSmsEvent('SEND_SUCCESS', { phone, messageId: result.messageId });
    return result;
  } catch (error) {
    logSmsEvent('SEND_ERROR', {
      phone,
      error: error.message,
      status: error.response?.status,
      data: error.response?.data
    });
    throw error;
  }
}

// 5. Prueba con curl (desde terminal)
// curl -X POST http://localhost:3000/api/sms/send \
//   -H "Content-Type: application/json" \
//   -d '{"phone":"912345678","message":"Hola desde curl"}'

Mejores Prácticas

  • Nunca expongas tu API Key en código del cliente
  • Usa variables de entorno (.env) para credenciales
  • Implementa reintentos con exponential backoff
  • Valida números de teléfono antes de enviar
  • Monitorea tus envíos y costos

Integra SMS en tu App Node.js con LETEL

Ya tienes el código. Ahora necesitas un proveedor confiable. LETEL ofrece la API SMS más fácil de usar en Chile, con ejemplos en Node.js, soporte técnico dedicado y planes flexibles para crecer.

  • Ejemplos completamente documentados en Node.js
  • SDKs para Express, NestJS y cualquier framework
  • Entrega garantizada 98%+ en Chile
  • Soporte técnico en español 24/7

Conclusión

Integrar SMS en Node.js es sencillo con la API REST de LETEL. Este tutorial cubre los casos más comunes, pero puedes expandir las funcionalidades según las necesidades de tu aplicación.

¿Necesitas ayuda con la integración?

Nuestro equipo puede guiarte en la integración de SMS en Node.js. Contactar desarrolladores para resolver tus dudas.

LETEL

Expertos en telecomunicaciones y automatización empresarial

Compartir:

🔗 Continuá tu Aprendizaje

Después de dominar Node.js, explora estos temas relacionados:

Preguntas Frecuentes sobre SMS en Node.js

¿Puedo usar TypeScript en lugar de JavaScript?
Sí, puedes usar TypeScript. Solo necesitas instalar los tipos de axios y cambiar la extensión de archivos a .ts.
¿Qué hacer si el SMS no se envía?
Verifica que tu API Key sea correcta, el formato del teléfono sea válido (56912345678) y tengas saldo disponible.
¿Cómo manejo caracteres especiales como tildes?
La API normaliza el mensaje removiendo acentos para compatibilidad con todos los operadores.
¿Puedo enviar SMS internacionales?
Sí, solo agrega el código de país correspondiente. Los precios varían por destino.
¿Hay límite de mensajes por segundo?
El límite estándar es de 100 mensajes por segundo. Contactanos si necesitas mayor volumen.