Volver al blog Conectividad y operaciones SMS

Identificadores de mensaje A2P SMS: cómo normalizar referencias sin perder trazabilidad

Diseñe un modelo de identificadores para separar el evento de negocio, el intento técnico y las referencias externas de HTTP, SMPP, proveedores y DLR. Evite sobrescrituras, colisiones y asociaciones incorrectas durante cambios de ruta o estados tardíos.

Diagrama de trazabilidad entre un evento de negocio, intentos técnicos, identificadores de proveedor y DLR SMS

El problema: un SMS puede acumular varias referencias

Un único envío lógico puede generar varios identificadores durante su recorrido operativo. La aplicación emisora puede crear una referencia propia; una API HTTP puede devolver un ID de recurso; un SMSC o MC puede devolver un message_id en submit_sm_resp; el proveedor puede enviar una referencia distinta en un DLR; y el sistema receptor del callback puede asignar su propio ID de evento.

Estas referencias no son intercambiables. Un identificador devuelto por un proveedor suele pertenecer al ámbito de esa plataforma, cuenta, integración y entorno. No debe tratarse como una clave primaria global de negocio ni asumirse que será único entre proveedores, rutas, cuentas o entornos.

El riesgo aparece cuando un sistema sobrescribe un ID con otro, convierte valores externos de forma irreversible o vincula eventos solo por una coincidencia textual. El resultado puede ser atribuir un DLR antiguo a un reenvío, mezclar estados de dos proveedores o perder la evidencia necesaria para investigar una incidencia.

  • El ID del evento de negocio identifica la intención: por ejemplo, una solicitud OTP o una notificación transaccional autorizada.
  • El ID del intento técnico identifica una ejecución concreta de envío hacia una cuenta, proveedor y ruta determinados.
  • El ID externo identifica el recurso o mensaje dentro del sistema que lo emitió.
  • El ID del evento de callback identifica la notificación recibida y no necesariamente el mensaje SMS al que se refiere.
El problema: un SMS puede acumular varias referencias

Qué identificadores pueden existir en HTTP, SMPP y DLR

El inventario exacto depende del contrato de cada integración, pero conviene modelar categorías estables. El objetivo no es forzar una nomenclatura universal, sino registrar qué representa cada referencia, quién la emitió y en qué contexto puede usarse.

En SMPP, submit_sm_resp devuelve un message_id asignado por el MC o SMSC. La especificación lo sitúa en el ámbito del sistema que acepta el submit y permite usarlo en operaciones posteriores, como consulta, reemplazo o asociación con un recibo. Por ello, debe conservarse como una referencia externa del intento, no como el identificador global del mensaje.

Un recibo SMPP puede llegar mediante deliver_sm o data_sm. Cuando está presente, el TLV receipted_message_id transporta la referencia del mensaje original devuelta anteriormente por el MC. También puede haber datos de recibo en otros campos o formatos definidos por la integración. Guarde el PDU o su representación bruta, además de la extracción normalizada.

En HTTP, una respuesta de creación o aceptación puede devolver un identificador de mensaje o recurso. Su aceptación síncrona no demuestra entrega en terminal. Los cambios posteriores pueden llegar por callback, consulta del recurso o informe de entrega, con su propia referencia de evento y sus propias marcas temporales.

  • internal_event_id: ID inmutable del evento lógico de negocio.
  • send_attempt_id: ID inmutable de cada intento técnico de emisión.
  • client_reference: referencia opcional aportada por el cliente o sistema originador.
  • external_message_id: ID devuelto por el proveedor, MC, SMSC o API.
  • dlr_reference: referencia transportada en el informe de entrega, como receipted_message_id cuando aplique.
  • callback_event_id: ID de la notificación recibida por webhook o infraestructura de eventos.
  • provider_account_scope: cuenta, tenant, integración, entorno y proveedor que delimitan el significado de una referencia.
Qué identificadores pueden existir en HTTP, SMPP y DLR

Principio de diseño: ID interno inmutable y referencias externas versionadas

La base del diseño es sencilla: genere identificadores internos que controle su organización y no los reutilice. Después, trate toda referencia externa como evidencia atribuida a un origen y a un momento de observación.

Separar el evento lógico del intento técnico es esencial. Un evento de negocio puede provocar un intento inicial, un reintento controlado o un failover a otra ruta. Cada intento debe disponer de su propio send_attempt_id, aunque todos dependan del mismo internal_event_id. Así se evita interpretar una reemisión como una actualización del envío anterior.

Las referencias externas tampoco deben sobrescribirse. Un mismo intento puede recibir una referencia de aceptación, otra referencia en un DLR y una referencia adicional en una consulta posterior. Registre cada una como una fila o evento independiente, con su tipo, valor original, valor de comparación y contexto operativo.

La interpretación operativa sí puede evolucionar. Por ejemplo, un estado derivado puede pasar de pendiente a entregado o no entregado al llegar nueva evidencia. Sin embargo, el evento recibido y la evidencia que motivó esa interpretación deben permanecer intactos.

  • Use UUID u otro esquema interno estable para internal_event_id y send_attempt_id.
  • No use un message_id de proveedor como clave primaria del dominio de negocio.
  • Mantenga una relación uno a muchos entre intento técnico y referencias externas.
  • Mantenga una relación uno a muchos entre intento técnico y observaciones de estado.
  • Conserve la versión de integración que procesó cada respuesta o callback.
  • Distinga el estado observado del estado derivado que usa su operación.

Normalización práctica sin destruir el valor recibido

Normalizar no significa reemplazar el valor original. La regla segura es almacenar siempre la representación exacta recibida y crear, por separado, una representación de comparación. Esta segunda representación solo sirve para búsquedas y reglas de vinculación documentadas.

La comparación puede requerir revisar codificación, longitud, espacios, sensibilidad a mayúsculas y minúsculas, prefijos o truncamiento. No aplique transformaciones universales: un cambio de mayúsculas puede ser inocuo para una integración y destructivo para otra; recortar una cadena puede crear una colisión; convertir bytes a texto sin conocer la codificación puede alterar el identificador.

Cada normalización debe ser reproducible. Guarde el nombre de la regla, su versión y el resultado. Si la integración cambia el formato de una referencia, podrá reexaminar los valores originales sin perder evidencia.

  • external_value_raw: valor exacto recibido, preservado sin transformación.
  • external_value_compare: valor derivado para comparación bajo una regla explícita.
  • normalization_rule_version: versión de la regla aplicada.
  • external_id_type: por ejemplo, submit_sm_resp_message_id, receipted_message_id o http_message_id.
  • observed_at: momento en que el sistema recibió u observó el valor.
  • source_payload_id: vínculo al payload, PDU o evento bruto almacenado de forma controlada.
  • No elimine espacios, ceros, prefijos ni caracteres no alfanuméricos sin una regla específica del proveedor.

Modelo de datos mínimo para investigar sin sobrescribir evidencia

Un modelo relacional mínimo puede resolver la mayoría de las investigaciones si conserva la separación entre intención, ejecución, referencias y observaciones. No necesita imponer que todos los proveedores devuelvan los mismos campos; necesita registrar de forma explícita qué se recibió y bajo qué ámbito.

La tabla de eventos de negocio representa la solicitud funcional autorizada. La tabla de intentos representa cada envío técnico. Las referencias externas y los eventos de estado se relacionan con el intento, no directamente con el evento lógico, salvo que el contrato del proveedor permita demostrar esa relación.

Para minimizar exposición, el destino debe tratarse como dato sensible. Regístrelo en una forma internacional coherente cuando sea necesario para investigación y claves compuestas, con controles de acceso, retención proporcionada y, cuando proceda, tokenización o protección equivalente. No es necesario almacenar el contenido completo del mensaje para resolver todas las incidencias; un hash seguro del payload o de una representación canónica puede ayudar a distinguir intentos sin ampliar innecesariamente la exposición de datos.

  • business_event: internal_event_id, tenant_id, tipo de evento, idempotency_key, created_at.
  • send_attempt: send_attempt_id, internal_event_id, provider_id, provider_account_id, route_id, environment, integration_version, submitted_at.
  • external_reference: reference_id, send_attempt_id, external_id_type, raw_value, compare_value, normalization_rule_version, observed_at.
  • status_observation: observation_id, send_attempt_id, callback_event_id, raw_status, normalized_status, provider_timestamp, received_at, payload_reference.
  • investigation_context: destino protegido o tokenizado, hash seguro del payload, origen de envío y datos de auditoría necesarios.

Cuando el proveedor reutiliza, transforma o no devuelve una referencia correlacionable

No todos los proveedores preservan una referencia enviada por el cliente, devuelven un ID estable o incluyen el mismo ID en los DLR. El modelo debe admitir esta limitación sin inventar una relación que no puede demostrarse.

Si un proveedor reutiliza identificadores, la referencia solo puede ser única dentro de una clave compuesta. Como mínimo, incorpore tenant, proveedor, cuenta de proveedor, entorno, tipo de referencia y un intervalo temporal de observación. Añada ruta e integración cuando puedan cambiar el significado operativo del valor.

Si el proveedor transforma el identificador, registre ambos valores y la regla conocida de transformación. Si no existe una regla contractual o técnicamente verificable, no establezca una asociación automática por similitud parcial. Marque el caso como ambiguo y envíelo a reconciliación o investigación.

Cuando no haya referencia correlacionable, la trazabilidad puede continuar hasta el intento técnico y la evidencia de aceptación, pero el vínculo con un DLR concreto quedará incierto. Ese límite debe estar visible en el dashboard y en los procedimientos operativos.

  • Nunca deduplique globalmente por external_message_id aislado.
  • No use coincidencias por prefijo, sufijo o truncamiento como prueba de identidad.
  • Exija una clave compuesta con ámbito operativo para cada regla de búsqueda.
  • Clasifique los vínculos como confirmado, probable o no correlacionable; reserve automatizaciones irreversibles para vínculos confirmados.
  • Documente qué referencias devuelve cada proveedor y cuáles pueden aparecer en sus DLR.

submit_sm_resp, DLR y estados asíncronos: qué puede vincularse

En un flujo SMPP habitual, el ESME envía submit_sm y recibe submit_sm_resp. El message_id de la respuesta identifica el mensaje en el MC o SMSC que respondió. Si se ha solicitado un recibo mediante registered_delivery y el sistema emite un DLR, el recibo puede indicar que es un MC Delivery Receipt mediante esm_class y transportar el identificador del mensaje recibido en el TLV receipted_message_id.

Esta relación permite una correlación fuerte cuando el receipted_message_id coincide con el message_id previamente registrado, dentro del mismo proveedor, cuenta, entorno e integración. Aun así, conserve el DLR completo: el estado, marcas temporales y campos disponibles forman parte de la evidencia y pueden ser necesarios si hay duplicados o eventos fuera de orden.

En HTTP, el identificador devuelto al crear un recurso puede servir para consultar posteriormente su estado o vincular callbacks, según el contrato del proveedor. Un código HTTP de creación o aceptación indica que la plataforma procesó o encoló la solicitud según su semántica; no prueba por sí mismo la entrega en terminal.

Los callbacks pueden llegar tarde, repetidos o fuera de orden. No descarte automáticamente una observación solo porque sea antigua respecto a la hora de recepción. Compare el timestamp del proveedor, el timestamp de recepción y la secuencia conocida; después aplique reglas de cierre y reconciliación auditables.

  • Persista submit_sm, submit_sm_resp y DLR como etapas separadas.
  • Solicite DLR mediante registered_delivery cuando el contrato SMPP y el caso de uso lo requieran.
  • No convierta un DLR en prueba de lectura humana ni de calidad general de ruta.
  • No suponga que un estado terminal impide la llegada posterior de evidencia contradictoria o duplicada.
  • Mantenga una política documentada para decidir qué estado derivado se muestra, sin borrar estados previos.

Cambios de ruta, reenvíos y duplicados: modelar por contexto

Un failover, un reintento o una reemisión pueden corresponder al mismo evento de negocio, pero no son necesariamente el mismo mensaje técnico. La regla práctica es crear un nuevo send_attempt_id para cada emisión hacia una combinación concreta de tenant, proveedor, cuenta, ruta, entorno e integración.

No consolide los DLR de rutas distintas como actualizaciones del mismo intento. Un estado de una ruta anterior no debe atribuirse a una nueva ruta solo porque el destino, el contenido o una referencia externa parezcan similares. La relación correcta se mantiene a través del internal_event_id, mientras que la evidencia de cada proveedor permanece asociada a su propio intento.

La idempotencia debe aplicarse antes de enviar. Para solicitudes HTTP, POST no es idempotente por definición; un reintento ante una respuesta incierta puede duplicar un envío si no existe una clave de idempotencia de aplicación o una confirmación fiable de que la operación anterior no se aplicó. No dependa de un ID externo que quizá todavía no se haya devuelto.

Un reenvío deliberado también debe ser visible como tal. Registre la causa: timeout de aceptación, fallo técnico, política de failover, decisión manual u otra razón autorizada. Esto permite distinguir una duplicación accidental de una segunda ejecución controlada.

  • Clave recomendada de contexto: tenant_id, provider_id, provider_account_id, route_id, environment, external_id_type y external_value_compare.
  • Añada ventanas temporales solo como restricción adicional, no como prueba única de identidad.
  • Use idempotency_key por evento o intención de negocio antes de invocar al proveedor.
  • Registre retry_sequence, failover_reason y relación entre intento origen e intento sucesor.
  • Evite enviar contenido sensible innecesario a logs, herramientas de búsqueda o URLs.
FAQ

Preguntas frecuentes

¿El message_id de submit_sm_resp puede usarse como ID global del mensaje?

No. Es una referencia asignada por el MC o SMSC que respondió y debe interpretarse dentro de su ámbito operativo. Guárdela con proveedor, cuenta, entorno, integración, tipo de referencia y momento de observación.

¿Un HTTP 202 o una respuesta exitosa de API confirma la entrega del SMS?

No necesariamente. Una aceptación o creación confirma el tratamiento de la solicitud según la API, pero la entrega requiere observar un informe de estado posterior o consultar el recurso cuando el proveedor lo permita.

¿Qué hago si el DLR llega antes, después o duplicado respecto a otros eventos?

Guarde cada observación sin sobrescribirla. Registre la hora del proveedor y la hora de recepción, aplique una regla de interpretación versionada y mantenga el evento bruto para reconciliación.

¿Debo guardar el contenido del SMS para correlacionar mensajes?

No es imprescindible en todos los casos. Priorice minimización de datos. Si necesita distinguir intentos, considere un hash seguro de una representación controlada del payload y proteja los datos de destino y metadatos asociados.

¿Un estado delivered demuestra recepción o lectura por una persona?

No. Representa la confirmación de entrega que el proveedor recibe de su cadena ascendente y, cuando esté disponible, del terminal. No es una prueba universal de lectura humana ni una garantía independiente sobre la calidad de ruta.

Fuentes consultadas

  1. SMPP v3.4 specificationSMPP Developers Forum
  2. SMPP Delivery Receipt FormatSMPP Developers Forum
  3. SMPP protocol overviewSMPP Developers Forum
  4. Message resourceTwilio
  5. Outbound Message Status in Status CallbacksTwilio
  6. Best Practices for Messaging Delivery Status LoggingTwilio
  7. Operations and Message TrackingTwilio
  8. Delivery Reports - Get - REST APIMicrosoft Learn
  9. Azure Communication Services SMS eventsMicrosoft Learn
  10. SMS logsMicrosoft Learn
  11. ITU-T Recommendation E.164International Telecommunication Union
  12. RFC 9110: HTTP SemanticsIETF / RFC Editor