Análisis técnico: arquitectura y algoritmos de ruteo SMS por coincidencia de operador
Introducción: detrás de escena del ruteo SMS por coincidencia de operador
La mayoría de los proveedores de SMS describen el ruteo como una caja negra:
"Usaremos la mejor ruta según calidad y precio."
Si eres un ingeniero o arquitecto responsable del uptime, eso no es suficiente. Necesitas saber:
- Qué ruta siguió un mensaje.
- Qué remitente utilizó.
- Por qué el sistema eligió esa combinación.
- Cómo se comportará ante fallas y picos de tráfico.
El ruteo por coincidencia de operador es una de las razones clave por las que algunos gateways alcanzan de forma consistente más de 99.4% de entregabilidad en verticales difíciles, mientras otros se estancan en la franja baja o media del 90%. En este artículo profundizaremos en la arquitectura:
- La capa de inteligencia (detección de operador y tipo de línea).
- El motor de decisión de ruteo.
- La selección de pools/grids y la lógica de rotación.
- Las estrategias de fallback.
- La observabilidad y depuración.
Esto no es marketing de proveedor. Es la arquitectura práctica que hemos visto funcionar a través de millones de mensajes por día.
Sección 1: Qué significa realmente el "carrier matching"
A grandes rasgos, el carrier matching consiste en lo siguiente:
Para cada número de destino, elegir un remitente y una ruta que coincidan de la mejor manera con el operador y el contexto del destino.
En lugar de:
- Enviar todo a través de las rutas genéricas más baratas.
- Mezclar todos los operadores y casos de uso en los mismos remitentes.
El carrier matching busca:
- Usar remitentes validados por Verizon para suscriptores de Verizon.
- Usar remitentes validados por AT&T para suscriptores de AT&T.
- Mantener la reputación por operador aislada y predecible.
Beneficios que vemos en la práctica:
- Mejoras de 3 a 12 puntos en entregabilidad en operadores específicos, comparado con el ruteo genérico.
- Menor varianza en el rendimiento a lo largo del tiempo.
- Análisis de causa raíz más limpio cuando surgen problemas (un operador, un grid).
Sección 2: Arquitectura de alto nivel
Un gateway de SMS con coincidencia de operador típicamente tiene estos componentes:
-
API de ingreso (Ingress API)
- Recibe las solicitudes de mensajes (
/messages). - Valida el payload, la autenticación y el esquema básico.
- Recibe las solicitudes de mensajes (
-
Normalización y enriquecimiento
- Normaliza los números de teléfono (E.164).
- Los enriquece con:
- Información de operador.
- País/región.
- Tipo de línea (móvil, VoIP, fija, cuando está disponible).
- Señales de riesgo.
-
Motor de decisión de ruteo
- Dado el contexto enriquecido + los metadatos de la aplicación:
- Elige el perfil de ruta (por ejemplo, OTP_US, Promo_EU).
- Selecciona un pool/grid.
- Elige un remitente dentro de ese grid.
- Aplica reglas por operador y por grid.
- Dado el contexto enriquecido + los metadatos de la aplicación:
-
Encolado y despacho
- Coloca los mensajes en colas por ruta.
- Aplica:
- Límites de tasa (rate limiting).
- Control de ráfagas (burst control).
- Estrategias de reintento.
-
Recibos de entrega y retroalimentación
- Ingiere los DLR (recibos de entrega).
- Actualiza:
- La salud del pool/grid.
- Las métricas de reputación del remitente.
- Retroalimenta las decisiones de ruteo.
-
Plano de observabilidad
- Métricas, logs, trazas.
- Consultable por:
- Operador.
- Pool/grid.
- Remitente.
- Campaña.
Sección 3: La capa de inteligencia de operador
Antes de poder hacer coincidir operadores, necesitas conocerlos.
Entradas
- Número de teléfono en formato E.164.
- Opcionalmente:
- Código de país proveniente del contexto de la aplicación.
- Metadatos conocidos del usuario (por ejemplo, operador resuelto previamente).
Fuentes
- Proveedores de HLR / lookup de operador.
- APIs de inteligencia de números telefónicos.
- Cachés internas (números resueltos recientemente).
Salidas
Para un destino dado:
carrier_id: por ejemplo,verizon_us,att_us,tmobile_us,o2_uk, etc.country_code:US,GB,DE, etc.line_type:mobile,fixed,voip(cuando está disponible).risk_flags: portado recientemente, rangos sospechosos, etc. (opcional).
Estrategia de caché
- Precalentar (warm) las cachés en:
- Destinos de alto tráfico.
- Remitentes frecuentes conocidos (por ejemplo, usuarios con mucho tráfico de OTP).
- Respetar:
- Los límites de tasa del proveedor de lookup.
- Las restricciones de frescura de los datos.
Ejemplo (pseudocódigo):
type CarrierInfo = {
carrierId: string;
country: string;
lineType?: string;
lastUpdated: number;
};
async function resolveCarrier(msisdn: string): Promise<CarrierInfo> {
const cached = await carrierCache.get(msisdn);
if (cached && Date.now() - cached.lastUpdated < CACHE_TTL_MS) {
return cached;
}
const lookup = await externalLookup(msisdn);
const info: CarrierInfo = {
carrierId: lookup.carrierId,
country: lookup.countryCode,
lineType: lookup.lineType,
lastUpdated: Date.now(),
};
carrierCache.set(msisdn, info);
return info;
}
Sección 4: Diseño del motor de decisión de ruteo
Dado:
- El contexto del mensaje enriquecido (
CarrierInfo, país, metadatos de la aplicación). - El tipo de mensaje (OTP, transaccional, marketing).
- La configuración de la cuenta/cliente.
El motor de ruteo debe elegir:
-
Perfil de ruta
- Por ejemplo,
OTP_US,PROMO_US,ALERT_EU, etc. - Encapsula:
- Operadores/rutas preferidos.
- Límites de throughput.
- Tipos de remitente permitidos.
- Por ejemplo,
-
Pool / grid
- Por ejemplo,
US_OTP_VERIZON_GRID_A,US_PROMO_ATT_GRID_B. - Cada grid:
- Representa una colección de SIM/números.
- Tiene capacidad y métricas de salud por operador.
- Por ejemplo,
-
Remitente dentro del grid
- Basado en:
- Estrategia de rotación.
- Salud (health).
- Restricciones locales.
- Basado en:
Flujo de decisión (simplificado)
function routeMessage(msg: Message, carrier: CarrierInfo): RouteDecision {
const profile = selectProfile(msg, carrier);
const candidateGrids = findEligibleGrids(profile, carrier);
const grid = selectBestGrid(candidateGrids);
const sender = pickSenderFromGrid(grid, msg);
return { profileId: profile.id, gridId: grid.id, senderId: sender.id };
}
Donde:
-
selectProfileutiliza:- El tipo de mensaje (OTP vs. promocional).
- País/región.
- Riesgo/vertical (por ejemplo, cripto/adultos).
-
findEligibleGridsfiltra por:- País.
- Compatibilidad con el operador.
- Umbrales de salud.
-
selectBestGridpuede:- Preferir grids con:
- Tasas de error/quejas saludables.
- Capacidad disponible.
- Evitar:
- Grids que se acercan a los umbrales.
- Preferir grids con:
-
pickSenderFromGrid:- Implementa rotación:
- Round-robin.
- Ponderada (weighted).
- Consciente de la salud (evita remitentes problemáticos).
- Implementa rotación:
Sección 5: Pools/grids y lógica de rotación
Los grids como unidad principal de aislamiento
Un grid puede definirse por:
- Región:
US. - Mezcla de operadores: solo Verizon, solo AT&T, multi-operador.
- Caso de uso:
OTP,PROMO,ALERT. - Nivel de prioridad.
Cada grid registra:
- Envíos totales.
- Desglose de entregados/fallidos.
- Códigos de fallo permanente (hard-fail).
- Tasas de quejas/baja (unsub).
Estrategias de rotación
La más simple:
- Round-robin entre remitentes activos.
Una mejor opción:
- Rotación consciente de la salud (health-aware):
- Omitir remitentes con:
- Tasas de error recientes elevadas.
- Proporciones altas de quejas.
- Dar más peso a:
- Remitentes nuevos y saludables.
- Omitir remitentes con:
Ejemplo:
function pickSenderFromGrid(grid: GridState): Sender {
const healthy = grid.senders.filter((s) => s.healthScore > MIN_HEALTH);
const weighted = buildWeightedList(healthy, (s) => s.weight);
return randomChoice(weighted);
}
Donde:
healthScorese basa en:- Tasa de entrega reciente.
- Tasa de fallo permanente (hard-fail).
- Tasa de quejas.
- Tiempo transcurrido desde la última verificación/calentamiento (warmup).
Retiro y enfriamiento
Implementa reglas como:
- Retirar o enfriar un remitente cuando:
- El hard-fail supera el 1–2% en los últimos N mensajes.
- Las quejas superan el 0.3–0.5% en un período.
- Los códigos de error específicos del operador se disparan.
Los remitentes retirados:
- Se sacan de la rotación activa.
- Pueden volver a probarse más adelante con tráfico pequeño y seguro.
Sección 6: Fallbacks, reintentos y modos de falla
Incluso con un buen ruteo, las cosas se rompen:
- Los operadores tienen cortes (outages).
- Rutas específicas se degradan.
- Un grid se "quema" temporalmente.
Principios de fallback
-
Preferir primero los fallbacks dentro de la misma familia
- Pasar del Grid A → Grid B dentro del mismo perfil/país.
- Mantener el OTP en grids de OTP, y los promocionales en grids promocionales.
-
Evitar reintentos instantáneos y repetidos sobre la misma ruta rota
- Aplicar backoff de forma agresiva:
- Backoff exponencial o lineal.
- Marcar las rutas/grids con fallas como degradadas.
- Aplicar backoff de forma agresiva:
-
Degradación controlada (graceful degradation)
- Para OTP:
- Probar un remitente alternativo dentro de la misma familia de operador.
- Considerar un fallback más lento, pero más confiable.
- Para promocionales:
- Reducir la tasa de envío.
- Posponer envíos si los operadores están claramente inestables.
- Para OTP:
Ejemplo de lógica de reintento (simplificada)
async function dispatchMessage(decision: RouteDecision, msg: Message) {
try {
const result = await sendToCarrier(decision, msg);
updateMetrics(decision, result);
return result;
} catch (err) {
markRouteAsDegraded(decision, err);
const fallbackDecision = findFallback(decision, msg);
if (!fallbackDecision) throw err;
const fallbackResult = await sendToCarrier(fallbackDecision, msg);
updateMetrics(fallbackDecision, fallbackResult);
return fallbackResult;
}
}
Sección 7: Observabilidad, logging y depuración
El ruteo por coincidencia de operador es tan bueno como su observabilidad.
Quieres poder preguntar:
- "Muéstrame todos los mensajes a Verizon en las últimas 24h ruteados por el Grid A frente al Grid B."
- "¿Qué remitentes del Grid C tienen la tasa de hard-fail más alta?"
- "¿Qué cambió alrededor del momento en que cayó la entregabilidad?"
Campos mínimos de log
Para cada mensaje:
message_idtimestampcustomer_id(o ID de proyecto/aplicación)destination_msisdn(hasheado/seudonimizado si es necesario)carrier_idcountry_codeprofile_idgrid_idsender_idroute_id/ ID upstreamstatus(en cola, enviado, entregado, fallido, desconocido)error_code(si lo hay)dlr_timestamplatency_mscampaign_idoflow_id(si corresponde)
Dashboards
- Mapas de calor operador × grid:
- Tasa de entrega.
- Tasa de hard-fail.
- Rankings de remitentes (leaderboards):
- Ordenados por salud y throughput.
- Detección de anomalías:
- Alertas cuando:
- La tasa de entrega del Operador X en el Grid Y cae por debajo del umbral.
- Los códigos de error se disparan.
- Alertas cuando:
Ejemplo de flujo de trabajo ante un incidente
- Alerta: "La entregabilidad de Verizon cayó más de 3 puntos en el Grid US_PROMO_A."
- Usar los logs:
- Revisar códigos de error y volúmenes.
- Comparar con otros grids.
- Mitigar:
- Mover temporalmente el tráfico promocional de Verizon al Grid US_PROMO_B.
- Reducir la tasa de envío.
- Investigar:
- Cambios recientes de contenido/plantillas.
- Cambios en la configuración de ruteo.
Preguntas frecuentes: ruteo por coincidencia de operador para desarrolladores
1. ¿Necesitamos hacer un lookup/HLR en cada mensaje?
No necesariamente.
Opciones:
- Cachear los resultados durante un TTL razonable.
- Resolver con anticipación para usuarios de alto tráfico.
- Hacer lookups por lotes (batch) al poblar los grids.
2. ¿Cómo manejamos la portabilidad numérica?
Los números portados pueden cambiar de operador. Buenas prácticas:
- Refrescar periódicamente la información de operador para:
- Destinos de alta frecuencia.
- Números con fallas repetidas.
3. ¿El carrier matching solo es relevante en EE. UU.?
No. Es especialmente útil:
- En cualquier lugar donde múltiples operadores se comporten de forma distinta.
- Donde los sender IDs y las plantillas son específicos de cada operador (muchos mercados de Europa y Asia-Pacífico).
4. ¿Cómo interactúa esto con A2P 10DLC y las campañas registradas?
El carrier matching:
- Usa correctamente las campañas y remitentes registrados por operador.
- Te ayuda a mantenerte dentro de las expectativas de throughput y contenido por campaña.
5. ¿Qué pasa con la privacidad y los datos personales (PII)?
Una implementación centrada en la privacidad:
- Hashea los MSISDN en los logs.
- Almacena el mínimo de datos necesarios.
- Conserva los metadatos de operador y ruteo, no el contenido sin procesar.
6. ¿Podemos añadir una capa de carrier matching sobre un CPaaS existente?
A veces:
- Si el CPaaS expone:
- Controles por operador.
- Estadísticas por remitente.
- Puedes construir una capa de meta-ruteo por encima.
Pero las formas más sólidas se logran con infraestructura propia (SIM, grids privados).
Conclusión: del ruteo de mejor esfuerzo al ruteo diseñado
La mayoría de los programas de SMS viven con un ruteo de mejor esfuerzo (best-effort):
- El proveedor elige rutas baratas/disponibles.
- Obtienes 1 o 2 métricas.
- Solo esperas que todo salga bien.
El ruteo por coincidencia de operador convierte al SMS en un sistema diseñado (engineered):
- Elecciones de ruta deterministas por operador.
- Grids y pools aislados.
- Rotación y fallbacks conscientes de la salud.
- Observabilidad rica para incidentes.
Si te importa:
- Alcanzar y sostener una entregabilidad superior al 99.4%.
- Sobrevivir picos promocionales y casos de uso de alto riesgo.
- Darle a tu equipo de SRE/infraestructura palancas que puedan entender y en las que puedan confiar.
…entonces implementar o elegir un gateway con una arquitectura de carrier matching seria no es un "nice-to-have": es la única estrategia sensata a largo plazo.
Dach SMS Lab