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:

  1. API de ingreso (Ingress API)

    • Recibe las solicitudes de mensajes (/messages).
    • Valida el payload, la autenticación y el esquema básico.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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:

  1. Perfil de ruta

    • Por ejemplo, OTP_US, PROMO_US, ALERT_EU, etc.
    • Encapsula:
      • Operadores/rutas preferidos.
      • Límites de throughput.
      • Tipos de remitente permitidos.
  2. 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.
  3. Remitente dentro del grid

    • Basado en:
      • Estrategia de rotación.
      • Salud (health).
      • Restricciones locales.

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:

  • selectProfile utiliza:

    • El tipo de mensaje (OTP vs. promocional).
    • País/región.
    • Riesgo/vertical (por ejemplo, cripto/adultos).
  • findEligibleGrids filtra por:

    • País.
    • Compatibilidad con el operador.
    • Umbrales de salud.
  • selectBestGrid puede:

    • Preferir grids con:
      • Tasas de error/quejas saludables.
      • Capacidad disponible.
    • Evitar:
      • Grids que se acercan a los umbrales.
  • pickSenderFromGrid:

    • Implementa rotación:
      • Round-robin.
      • Ponderada (weighted).
      • Consciente de la salud (evita remitentes problemáticos).

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.

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:

  • healthScore se 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

  1. 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.
  2. 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.
  3. 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.

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_id
  • timestamp
  • customer_id (o ID de proyecto/aplicación)
  • destination_msisdn (hasheado/seudonimizado si es necesario)
  • carrier_id
  • country_code
  • profile_id
  • grid_id
  • sender_id
  • route_id / ID upstream
  • status (en cola, enviado, entregado, fallido, desconocido)
  • error_code (si lo hay)
  • dlr_timestamp
  • latency_ms
  • campaign_id o flow_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.

Ejemplo de flujo de trabajo ante un incidente

  1. Alerta: "La entregabilidad de Verizon cayó más de 3 puntos en el Grid US_PROMO_A."
  2. Usar los logs:
    • Revisar códigos de error y volúmenes.
    • Comparar con otros grids.
  3. Mitigar:
    • Mover temporalmente el tráfico promocional de Verizon al Grid US_PROMO_B.
    • Reducir la tasa de envío.
  4. 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

Dach SMS Lab