Volver al blog Conectividad

Callbacks de DLR en A2P SMS: diseño idempotente ante duplicados y desorden

Diseñe la recepción de callbacks DLR como un historial auditable y una proyección de estado idempotente, capaz de manejar reintentos, duplicados, eventos tardíos y secuencias fuera de orden.

Diagrama de eventos DLR de SMS con duplicados y estados fuera de orden

La pregunta operativa: ¿qué ocurre si el callback llega dos veces, tarde o en otro orden?

Un callback de delivery receipt (DLR) no debe tratarse como una actualización única, ordenada y definitiva. En una integración HTTP, una comunicación puede fallar antes de que el emisor obtenga la respuesta y la solicitud puede repetirse. RFC 9110 define la idempotencia como la propiedad por la que múltiples solicitudes idénticas producen el mismo efecto previsto que una sola.

En la práctica, el receptor debe poder aceptar una misma notificación más de una vez sin contabilizarla varias veces ni modificar indebidamente el estado del mensaje. También debe poder registrar un recibo que llegue después de otro evento observado previamente, sin borrar la evidencia ni asumir que el orden de recepción equivale al orden real de procesamiento en la cadena de mensajería.

La regla operativa es simple: guarde cada recepción, deduzca el estado actual con reglas explícitas y mantenga separadas ambas cosas. El historial responde qué se recibió y cuándo; la proyección de estado responde cuál es el resultado operativo que el sistema calcula con la evidencia disponible.

  • No asuma entrega porque un callback fue recibido por su endpoint.
  • No suponga que el último callback recibido representa necesariamente el último hecho ocurrido en la red.
  • No aplique una actualización irreversible basándose sólo en destino, contenido o una coincidencia aproximada.
  • Responda de forma segura a repeticiones: el reintento no debe generar efectos contables, analíticos u operativos duplicados.
La pregunta operativa: ¿qué ocurre si el callback llega dos veces, tarde o en otro orden?

Qué representa un DLR y qué no representa

En SMPP, un DLR se solicita en submit_sm mediante registered_delivery. El recibo puede devolverse al ESME mediante deliver_sm o data_sm. Por ello, la disponibilidad de recibos depende de que se hayan solicitado y de la configuración o comportamiento de la plataforma de mensajería.

SMPP distingue un SMSC Delivery Receipt de una notificación intermedia mediante la codificación de esm_class. Las notificaciones intermedias son un tipo distinto y su soporte depende de la implementación del SMSC; no deben interpretarse automáticamente como resultados finales.

Un DLR informa del estado que reporta la cadena de mensajería dentro de ese flujo. No equivale a una verificación independiente de que una persona ha visto, leído o comprendido el contenido en su terminal. En la semántica SMPP, DELIVERED indica entrega al destino; no existe en el estándar citado un estado de lectura humana o de contenido visualizado.

  • Diferencie aceptación de submit_sm, que confirma la presentación al sistema que responde, de un DLR posterior.
  • Almacene si se solicitó DLR y con qué modalidad de registered_delivery cuando ese dato esté disponible.
  • Etiquete el resultado como estado reportado por la mensajería, no como prueba de lectura por el destinatario.
  • Trate las notificaciones intermedias como eventos separados de los estados finales.
Qué representa un DLR y qué no representa

Modelo mínimo de datos para una trazabilidad útil

El diseño debe preservar los datos necesarios para reconstruir una decisión. Para cada mensaje enviado, conserve un identificador interno inmutable y el identificador externo devuelto por el sistema de mensajería. En SMPP, receipted_message_id identifica el mensaje objeto del recibo y corresponde al identificador opaco message_id retornado al confirmar la presentación original.

Guarde el destino en una representación normalizada y mantenga también el contexto de direccionamiento recibido o enviado. E.164 define el plan público internacional de numeración; su normalización ayuda a evitar variaciones de formato, pero no debe reemplazar los valores originales necesarios para depuración.

El formato histórico de DLR en short_message puede incluir identificador, fechas de presentación y finalización, estado y error. Sin embargo, sus particularidades pueden ser específicas de la pasarela o SMSC. Por eso, extraiga campos normalizados para operar, pero conserve siempre la carga original.

  • Mensaje: ID interno, ID externo o message_id, origen, destino normalizado, valores originales de direccionamiento, ruta o contexto de envío y fecha de presentación.
  • Evento recibido: ID de evento del proveedor si existe, fecha y hora de recepción, fecha y hora reportadas por el emisor cuando existan, tipo de evento, estado bruto, código de error bruto y payload original.
  • Proyección: estado calculado, motivo de la decisión, evento o eventos que sustentan esa decisión y fecha de actualización.
  • Auditoría: versión del parser o reglas aplicadas, resultado de la correlación y cualquier excepción detectada.

Correlación: priorice claves estables y rechace coincidencias débiles

La correlación entre el mensaje original y el DLR debe basarse primero en el identificador externo asignado por el SMSC. SMPP define receipted_message_id como el identificador del mensaje al que corresponde el recibo. Además, query_sm utiliza el message_id asignado por el SMSC junto con la dirección de origen como mecanismo de coincidencia.

No use destino, texto, ventana temporal o remitente como sustitutos automáticos del ID externo. Esos atributos pueden repetirse entre mensajes legítimos y producir una atribución incorrecta. Una correlación errónea es más dañina que un evento pendiente: puede convertir un estado de otro mensaje en una entrega o fallo que nunca le correspondió.

Cuando la correlación no sea concluyente, registre el callback sin perderlo y diríjalo a una cola o registro de excepciones. Esta decisión permite investigar cambios de formato, IDs truncados, variaciones de codificación u otras particularidades de una integración sin contaminar la proyección de mensajes conocidos.

  • Primera opción: relacione receipted_message_id con el message_id almacenado al aceptar el envío.
  • Conserve el valor externo exactamente como se recibió, además de cualquier forma normalizada que requiera la integración.
  • Use origen, destino, fechas y ruta como validaciones auxiliares, no como clave única de asignación.
  • Si hay más de un candidato o ninguno, marque la correlación como no concluyente y no materialice el estado sobre un mensaje concreto.

Patrón idempotente para recibir y procesar callbacks

La idempotencia no exige ignorar toda repetición. Permite conservar cada solicitud recibida como evidencia y, a la vez, impedir que la repetición altere varias veces el resultado operativo. RFC 9110 aclara que un servidor puede registrar cada solicitud individualmente aunque el efecto previsto de la operación sea idempotente.

Implemente dos capas. La primera es una bitácora de recepciones, preferiblemente inmutable, que guarde el payload, cabeceras relevantes disponibles, momento de recepción y resultado del parser. La segunda es la aplicación de efectos: deduplicación, correlación y cálculo de estado. Sólo esta segunda capa debe estar protegida contra aplicar dos veces el mismo evento lógico.

Si el emisor proporciona un ID de evento estable, úselo como clave de deduplicación dentro del ámbito correcto de la integración. Si no existe, cree una huella a partir de atributos estables presentes en el callback, conserve los componentes utilizados y mantenga el payload original. No base la huella en campos que puedan cambiar por transformación local o en atributos ambiguos sin documentar el riesgo.

  • 1. Reciba el callback y persista la recepción antes de ejecutar efectos de negocio.
  • 2. Valide y extraiga los campos disponibles sin descartar el payload original.
  • 3. Determine si existe un ID de evento estable; si no, calcule una huella documentada para el evento lógico.
  • 4. Inserte o detecte el evento de forma atómica en el registro de eventos lógicos.
  • 5. Correlacione por ID externo y aplique las reglas de transición sólo una vez por evento lógico.
  • 6. Devuelva una respuesta HTTP coherente tras persistir el resultado necesario para que un reintento sea seguro.

Máquina de estados: haga explícitas las transiciones permitidas

Una máquina de estados evita que la lógica dependa del orden accidental de llegada. La guía de formato SMPP clasifica ENROUTE como estado intermedio y DELIVERED, EXPIRED, DELETED y UNDELIVERABLE como estados finales. También indica que un mensaje en reintento puede permanecer ENROUTE y terminar después en EXPIRED o DELIVERED.

Represente los estados recibidos sin reescribirlos y defina una capa de estado operativo calculado. En el modelo SMPP descrito, los estados finales no progresan a otros estados. Por tanto, un evento posterior que parezca contradecir un final ya calculado no debe reemplazarlo silenciosamente: debe conservarse y abrir una excepción de reconciliación o investigación.

No generalice los códigos de error de una plataforma a otra. Los códigos de red o SMSC pueden ser específicos de la pasarela o plataforma. Almacénelos como evidencia original y cree clasificaciones internas únicamente cuando sus reglas estén documentadas para la integración concreta.

  • Estado bruto: el valor recibido, sin reinterpretación destructiva.
  • Estado normalizado: una categoría interna documentada, si la integración permite mapearla de forma fiable.
  • Estado calculado: resultado de aplicar reglas de precedencia y transición sobre eventos correlacionados.
  • Excepción: conflicto entre eventos, evento final contradictorio, falta de correlación o formato no reconocido.
  • Regla conservadora: un estado final ya materializado no debe convertirse en otro estado final sólo porque llegue un callback posterior.

Eventos tardíos y fuera de orden: preserve la evidencia y calcule con prudencia

Guarde al menos dos tiempos distintos: cuándo recibió su sistema el callback y qué tiempo reporta el callback, si lo reporta. También puede ser necesario conservar las fechas de presentación y finalización incluidas en formatos de recibo. Estas marcas no son intercambiables: una describe la observación local y otra el dato comunicado por la plataforma de mensajería.

Para decidir el estado calculado, no ordene ciegamente por el instante de llegada. Aplique una política documentada que considere el tipo de estado, su carácter intermedio o final y la calidad de la correlación. Si no existe una base fiable para ordenar dos eventos, no invente una secuencia: mantenga ambos y marque el conflicto.

La salida operativa debe distinguir entre el historial completo y el resumen actual. Un panel puede mostrar un estado calculado final y, simultáneamente, indicar que hubo eventos repetidos, tardíos o en conflicto. Este enfoque reduce la tentación de ocultar señales que más tarde resultan esenciales para soporte, conciliación o análisis de calidad.

  • Conserve orden de recepción local, tiempos reportados y payload original.
  • No borre un evento duplicado: señálelo como repetición del mismo evento lógico cuando corresponda.
  • No reemplace un final por otro final incompatible sin una regla contractual o técnica verificable para esa integración.
  • Use una cola de investigación para conflictos y mensajes no correlacionados.
  • Exponga en auditoría el evento que sustenta el estado calculado y los eventos que no pudieron aplicarse.

Lista de comprobación para producción

Antes de conectar callbacks de DLR a métricas, facturación, alertas o decisiones de campaña, pruebe la integración como un sistema de eventos y no sólo como una llamada HTTP exitosa. La prioridad es que un reintento, un payload inesperado o un recibo sin correlación no se conviertan en una afirmación errónea de entrega.

BulkSMSMarket describe conectividad HTTP y SMPP, además de herramientas orientadas a la calidad y consistencia de DLR. En cualquier conexión, confirme la semántica concreta de los campos, los formatos y las reglas de recibo de la contraparte antes de convertirlos en automatizaciones críticas.

  • Pruebe el mismo callback repetido y confirme que el estado calculado y los contadores no cambian por la repetición.
  • Pruebe un evento intermedio que llegue después de un estado final y confirme que no degrada ni sobrescribe el resultado final.
  • Pruebe dos finales incompatibles y verifique que el segundo queda preservado y marcado para investigación.
  • Pruebe callbacks sin ID de evento, con ID externo no encontrado y con formatos de fecha o payload no reconocidos.
  • Compruebe que los mensajes no correlacionados no se asignan por destino o contenido de forma automática.
  • Revise que los informes distinguen aceptación, estado DLR reportado y cualquier verificación independiente que pudiera existir fuera del DLR.
FAQ

Preguntas frecuentes

¿Un callback DLR duplicado significa que el SMS se entregó dos veces?

No. Puede tratarse de la repetición de una notificación. Debe conservarse como recepción observada, pero el efecto sobre el estado calculado y los contadores debe aplicarse una sola vez al evento lógico.

¿Puedo correlacionar un DLR usando sólo el número de destino?

No es recomendable. El destino puede repetirse en varios mensajes. La base principal de correlación debe ser el ID externo asignado al mensaje, como el message_id de SMPP relacionado con receipted_message_id. El destino sirve como validación auxiliar.

¿DELIVERED confirma que el destinatario leyó el mensaje?

No. DELIVERED es un estado de entrega reportado en el flujo de mensajería. No constituye una confirmación independiente de lectura humana ni de visualización del contenido en el terminal.

¿Qué hago si llega un estado final después de otro estado final distinto?

Conserve ambos eventos y marque el conflicto para investigación. No sustituya silenciosamente un estado final por otro final incompatible sólo por el orden de recepción del callback.

¿Es necesario guardar el payload original del DLR?

Sí. Los formatos y códigos pueden variar entre plataformas. El payload original permite auditar el parser, revisar campos no normalizados e investigar discrepancias sin perder la evidencia recibida.

Fuentes consultadas

  1. SMPP Protocol Specification v3.4, Issue 1.2SMPP Developers Forum
  2. SMPP Delivery Receipt FormatSMPP Developers Forum
  3. RFC 9110: HTTP SemanticsIETF / RFC Editor
  4. Recommendation ITU-T E.164: The international public telecommunication numbering planInternational Telecommunication Union