Retour au blog Qualité et confiance

Accepté ne signifie pas livré : comment réconcilier les statuts d’envoi et les DLR en SMS A2P

Un cadre opérationnel pour distinguer les accusés de réception API et SMPP, l’acceptation par les fournisseurs, les statuts réseau et les DLR finaux, sans transformer une preuve de transport en affirmation de réception sur le terminal.

Schéma opérationnel des statuts d’envoi et des DLR pour un message SMS A2P

La question opérationnelle : quelles preuves existent réellement après l’envoi d’un SMS ?

En SMS A2P, le mot « envoyé » peut désigner des faits très différents. Il peut signifier qu’une application a créé une requête HTTP, qu’un ESME a reçu un submit_sm_resp avec succès, qu’un fournisseur a accepté le trafic, qu’un carrier en amont a accepté le message ou qu’un accusé de réception de livraison a été reçu. Ces signaux n’ont pas la même portée probatoire.

La règle pratique est simple : chaque statut doit exprimer les preuves disponibles, et non la conclusion la plus favorable. Une réponse positive d’une API ou un command_status correct dans submit_sm_resp confirme le résultat de cette interaction technique. Cela ne confirme pas, à lui seul, que le message a atteint le réseau de destination ni que l’abonné l’a reçu sur son terminal.

Cette distinction est particulièrement importante pour les flux OTP et transactionnels. Un système peut décider de relancer un envoi, d’afficher un écran à l’utilisateur ou d’ouvrir une investigation opérationnelle à partir du mauvais statut s’il transforme « accepté » en « livré ». Dans le marketing légitime, la même erreur peut fausser les métriques et les décisions relatives à la qualité des routes.

Un DLR ne doit pas non plus être présenté automatiquement comme une vérification indépendante de la réception physique sur le terminal. Il s’agit d’une preuve de statut communiquée à travers la chaîne de messagerie. Sa signification précise dépend de l’événement reçu, des champs disponibles et des garanties apportées par chaque participant de la route.

  • Accusé de réception de requête : preuve qu’une entité a traité une requête technique.
  • Acceptation par le fournisseur ou le carrier : preuve d’admission pour un traitement ultérieur.
  • Statut intermédiaire : preuve de mise en file, d’envoi ou de transit, mais pas nécessairement d’un résultat final.
  • DLR portant un statut final selon la source ou les règles d’intégration : preuve rapportée de livraison ou de non-livraison ; il doit être conservé avec sa source et sa charge utile d’origine.
  • Silence : absence d’événement observé, et non preuve de livraison ou d’échec.
La question opérationnelle : quelles preuves existent réellement après l’envoi d’un SMS ?

Les quatre couches à ne pas confondre

Une architecture de réconciliation robuste sépare au moins quatre couches. La première est l’acceptation locale : l’application a validé et envoyé une requête, ou l’a placée dans sa propre file d’attente. C’est un fait interne qui n’informe pas sur le résultat distant.

La deuxième est l’acceptation distante. Pour les opérations SMPP pertinentes de type requête/réponse, telles que submit_sm et submit_sm_resp, si l’émetteur ne reçoit pas la réponse, il doit supposer que le PDU n’a pas été reçu par l’entité distante. Lorsqu’il reçoit submit_sm_resp, command_status indique le succès ou l’échec de la requête submit_sm. Cet accusé de transport n’est pas un DLR.

La troisième couche est le traitement par le fournisseur ou le réseau. Dans les API HTTP, certains fournisseurs exposent des statuts tels que accepted, queued, sending ou sent. Ils peuvent être utiles pour localiser une file, une phase de dispatch ou l’acceptation par un carrier en amont, mais ils restent distincts d’une confirmation de livraison.

La quatrième couche est le résultat communiqué. SMPP définit notamment les états ENROUTE, DELIVERED, EXPIRED, UNDELIVERABLE, UNKNOWN, ACCEPTED et REJECTED. Cette diversité montre pourquoi un unique champ booléen de succès perd souvent des informations essentielles pour les opérations, le support client et l’analyse de qualité.

  • Couche 1 : requête créée ou acceptée par le système interne.
  • Couche 2 : requête acceptée ou rejetée par une API, un SMSC ou un fournisseur.
  • Couche 3 : traitement, file d’attente, dispatch ou transit communiqué par la chaîne de livraison.
  • Couche 4 : résultat final ou statut toujours non résolu communiqué par DLR, requête de statut ou réconciliation.
Les quatre couches à ne pas confondre

Pourquoi un message_id n’est pas une preuve de livraison

En SMPP, message_id est une référence attribuée par le SMSC, unique dans le périmètre défini par son implémentation. La spécification le décrit comme une valeur opaque et dépendante de l’implémentation. Il peut être utilisé comme identifiant dans des opérations ultérieures, par exemple query_sm, cancel_sm ou replace_sm. Son existence permet de corréler des opérations ; elle ne prouve pas que le destinataire a reçu le SMS.

Une intégration ne doit pas dépendre d’un seul identifiant. Le fournisseur peut générer son propre identifiant, un SMSC peut en renvoyer un autre et un DLR peut inclure receipted_message_id. De plus, un message métier peut nécessiter son propre identifiant stable afin de relier la tentative d’envoi à une transaction, une session OTP ou une notification transactionnelle légitime.

La pratique recommandée consiste à créer un identifiant interne immuable avant l’envoi. Cet identifiant doit être relié à chaque tentative technique, à ses références externes, aux événements reçus et au statut dérivé. En cas de nouvelles tentatives, chaque tentative doit disposer de sa propre identité et conserver un lien explicite avec le message logique d’origine.

N’utilisez pas le numéro de destination, le texte complet du message ou un horodatage approximatif comme clé principale de corrélation. Ces champs peuvent se répéter, changer en raison de la segmentation ou poser des problèmes de confidentialité. Ne conservez que les données nécessaires aux opérations et appliquez les contrôles de protection des données appropriés.

  • message_logical_id : identifie le message ou l’action métier.
  • attempt_id : identifie chaque tentative technique d’envoi.
  • provider_message_id : identifiant renvoyé par l’API ou le fournisseur.
  • smpp_message_id : identifiant renvoyé par submit_sm_resp, lorsqu’il s’applique.
  • receipted_message_id : référence incluse dans un DLR SMPP, lorsqu’elle s’applique.
  • event_id ou empreinte d’événement : permet de détecter les doublons sans effacer les preuves.
  • correlation_version : documente les règles de corrélation lorsqu’elles évoluent dans le temps.

Modèle de machine à états pour un SMS A2P

La machine à états doit distinguer les événements observés des conclusions dérivées. Un événement est immuable : par exemple, une réponse HTTP acceptée, un submit_sm_resp réussi, un deliver_sm avec message_state ou une requête ultérieure. Le statut actuel est une projection calculée à partir de tous les événements corrélés et de règles explicites.

Un modèle minimal peut conserver des états internes de création, d’envoi local et d’attente de réponse ; des états d’acceptation distante ; des états transitoires de traitement ; des états finaux rapportés ; et un état de réconciliation indiquant si le dossier est clos, en attente d’observation ou nécessite un examen. Il n’est pas nécessaire de forcer tous les fournisseurs à entrer dans une taxonomie plus précise que les preuves disponibles.

Pour SMPP, traitez le succès de submit_sm_resp comme « accepté par l’entité SMPP », et non comme « livré ». Lorsque des reçus arrivent via deliver_sm, enregistrez les valeurs disponibles, y compris receipted_message_id, message_state et, s’il est présent, network_error_code. Les notifications intermédiaires et les DLR peuvent partager ce même mécanisme de transport.

Une projection prudente peut afficher « livraison rapportée » lorsque la preuve normalisée est DELIVERED ou un équivalent communiqué par le fournisseur. Elle peut afficher « non-livraison rapportée » pour les états finaux négatifs tels que UNDELIVERABLE, EXPIRED ou REJECTED, sans effacer le code d’origine. Lorsqu’il n’existe qu’un statut de transit, le résultat doit rester en attente ou en cours.

  • CREATED : l’intention d’envoyer a été créée, sans preuve distante.
  • SUBMITTED_LOCAL : le système a tenté de transmettre la requête.
  • REMOTE_ACCEPTED : l’API, le SMSC ou le fournisseur a accepté la requête technique.
  • IN_PROGRESS : une preuve de mise en file, de dispatch, de transit ou de ENROUTE existe.
  • DELIVERED_REPORTED : un statut final positif communiqué par la chaîne de messagerie a été reçu.
  • FAILED_REPORTED : un statut final négatif communiqué par la chaîne de messagerie a été reçu.
  • UNKNOWN_OR_UNRESOLVED : aucune preuve finale suffisante n’existe ou UNKNOWN a été reçu.
  • RECONCILIATION_PENDING : une fenêtre opérationnelle a expiré et le dossier requiert une requête de statut ou une classification de clôture.

Comment normaliser HTTP, SMPP et des DLR hétérogènes sans perdre les données d’origine

La normalisation sert à opérer de manière cohérente, et non à remplacer la sémantique de la source. Conservez toujours l’événement original avec une représentation normalisée. La charge utile d’origine est nécessaire pour l’audit technique, le débogage des mappings et l’adaptation lorsqu’un fournisseur ajoute ou modifie des champs dans ses callbacks.

Pour chaque événement, enregistrez au minimum la source, le type d’interface, le moment de réception par votre plateforme, l’identifiant externe disponible, le statut d’origine, le statut normalisé, les codes d’erreur et la charge utile d’origine protégée. Si le fournisseur communique une date provenant du réseau, stockez-la séparément de l’heure de réception : elles ne représentent pas nécessairement le même instant.

En SMPP, un deliver_sm peut transporter des accusés de réception de livraison. Pour ces événements, receipted_message_id et message_state sont des paramètres pertinents ; network_error_code peut être présent. Ne rejetez pas un reçu parce qu’un champ non garanti par votre intégration est absent : classez-le selon les preuves disponibles et marquez-le pour examen s’il ne peut pas être corrélé de façon fiable.

En HTTP, ne supposez pas que tous les callbacks contiennent les mêmes champs ni que tous les statuts existent pour tous les canaux ou toutes les configurations. Maintenez une table de mapping versionnée par fournisseur et par interface. Le mapping doit transformer les statuts externes en grandes catégories opérationnelles tout en préservant la valeur externe littérale.

  • Ne remplacez pas le statut d’origine par le statut normalisé ; conservez les deux.
  • Distinguez event_received_at de event_reported_at lorsque la source fournit son propre horodatage.
  • Enregistrez les codes et textes d’erreur sans les transformer en diagnostics non confirmés.
  • Versionnez les règles de mapping et de priorité.
  • Conservez les événements non corrélés dans une file d’investigation plutôt que de les associer sur la base d’une similarité faible.

Statuts transitoires, finaux et inconnus : des règles explicites

Classez les statuts selon leur fonction opérationnelle. Les statuts transitoires indiquent que des informations supplémentaires peuvent arriver ; les statuts finaux communiquent un résultat et, selon vos règles, clôturent la tentative ; les statuts inconnus expriment l’absence de résolution et ne doivent pas être transformés en succès ou en échec pour des raisons de commodité analytique.

SMPP répertorie ENROUTE comme un statut de transit et DELIVERED, EXPIRED, DELETED, UNDELIVERABLE et REJECTED parmi les statuts pouvant avoir un caractère final selon le contexte de l’intégration. UNKNOWN exige une prudence particulière : il indique que le statut ne peut pas être déterminé, et non que le message a échoué ou qu’il a été livré.

Définissez à l’avance quels statuts clôturent une tentative et quels statuts permettent l’arrivée d’autres événements. La règle ne doit pas résider uniquement dans du code implicite. Elle doit être documentée, vérifiable et modifiable de manière contrôlée lorsqu’une interface ou une relation opérationnelle évolue.

Ne transformez pas le passage du temps en preuve de livraison. Le temps peut uniquement déclencher une action de réconciliation ou une clôture administrative avec une incertitude déclarée. En l’absence de DLR, le résultat correct peut être « aucun statut final observé dans la fenêtre », et non « livré ».

  • Transitoires : queued, sending, sent, ENROUTE ou équivalents communiqués par la source.
  • Finaux positifs rapportés : DELIVERED ou équivalent explicite du fournisseur.
  • Finaux négatifs rapportés : EXPIRED, UNDELIVERABLE, REJECTED ou équivalents explicites.
  • Indéterminés : UNKNOWN, erreurs de corrélation, absence de DLR et événements incomplets.
  • Administratifs : clôturé par politique opérationnelle, toujours distinct du résultat de livraison rapporté.

DLR dupliqués, hors séquence ou contradictoires

Les systèmes de messagerie doivent être conçus pour des événements répétés et un ordre imparfait. SMPP indique qu’un SMSC devrait répondre dans le même ordre que celui de réception des requêtes, mais qu’il n’y est pas obligé, et qu’un ESME doit pouvoir gérer des réponses hors séquence. Cette exigence concerne les réponses SMPP ; elle ne garantit pas un ordre particulier pour les DLR ou les callbacks HTTP. Il reste néanmoins prudent de concevoir les callbacks HTTP, les files internes et les processus de nouvelle tentative pour tolérer un ordre imparfait.

La solution ne consiste pas à écraser la dernière donnée reçue. Stockez chaque événement comme un fait immuable et déduisez le statut actuel à l’aide d’une politique de priorité. Cette politique doit prendre en compte au minimum la corrélation, la catégorie du statut, l’heure rapportée par la source lorsqu’elle est fiable, l’heure de réception, la source et la version de la règle appliquée.

Un doublon peut être détecté à l’aide d’un identifiant d’événement lorsqu’il existe, ou au moyen d’une empreinte calculée à partir de champs stables de l’événement. Le marquer comme doublon ne signifie pas le supprimer : il conserve une valeur pour le diagnostic et démontre que le système a reçu plus d’une notification.

Face à des contradictions, n’inventez pas de résolution. Par exemple, si un signal final positif apparaît puis qu’un événement négatif survient sans pouvoir être expliqué par une tentative distincte, conservez les deux, signalez le dossier comme contradictoire et appliquez une règle de présentation prudente. La vue opérationnelle peut exiger un examen, tandis que l’audit doit montrer la séquence complète.

  • N’utilisez pas « le dernier événement l’emporte » comme seule règle.
  • Déduisez le statut par tentative, et non uniquement par message logique.
  • Appliquez une déduplication idempotente avant de mettre à jour les projections.
  • Distinguez les événements tardifs des événements invalides : un événement tardif peut être légitime.
  • Transmettez les contradictions non résolubles à un examen opérationnel.
  • Enregistrez la raison de chaque changement de statut dérivé.

Que faire lorsqu’aucun DLR n’arrive

L’absence de DLR ne prouve pas que le message n’a pas été traité ni qu’il n’a pas été livré. Elle peut indiquer qu’aucun callback n’a été reçu, que l’intégration ne l’a pas demandé ou ne le prend pas en charge pour ce cas, que l’événement n’a pas pu être corrélé ou que la chaîne n’a pas encore produit de résultat observable.

Établissez des fenêtres d’attente par cas d’usage, fournisseur et interface uniquement lorsque vous disposez d’une politique opérationnelle justifiée. Ne transformez pas une recommandation de fournisseur en règle universelle. Définissez des actions de réconciliation adaptées à chaque intégration lorsque celle-ci met à disposition une source de statut consultable, puis validez ces actions dans le cadre de votre politique opérationnelle.

Pour les OTP légitimes, l’objectif produit impose souvent une décision rapide, mais le statut de livraison ne doit pas être remplacé par une supposition. Concevez des alternatives d’authentification appropriées, contrôlez les nouvelles tentatives afin d’éviter les duplications et enregistrez que le résultat reste en attente lorsqu’aucune preuve finale n’arrive. Pour les messages transactionnels, une fenêtre plus large peut être appropriée selon la criticité et les attentes du processus. Pour le marketing avec consentement, séparez la mesure de livraison rapportée des métriques d’interaction et n’inférez pas une lecture à partir du DLR.

La clôture opérationnelle doit distinguer « résultat final rapporté », « résultat consulté », « aucun DLR observé » et « non corrélable ». Cette précision réduit les litiges entre systèmes et évite que les rapports de qualité transforment l’incertitude en taux de livraison artificiel.

  • Vérifiez que la demande de DLR ou de callback est configurée lorsque l’interface le permet.
  • Contrôlez la disponibilité de l’endpoint de réception, l’authentification et le journal des erreurs de callback.
  • Planifiez des requêtes de réconciliation lorsque le fournisseur propose une source de statut consultable.
  • Clôturez avec une étiquette d’incertitude en l’absence de preuve finale.
  • N’attribuez pas le silence à une défaillance du réseau, du fournisseur ou à une livraison sans données permettant de l’étayer.
  • Mesurez séparément les DLR demandés, les DLR reçus, les DLR corrélés et les cas sans résolution finale.
FAQ

Questions fréquentes

Un submit_sm_resp avec un command_status correct confirme-t-il la livraison du SMS ?

Non. Il confirme le résultat de la requête submit_sm auprès de l’entité SMPP distante. Il s’agit d’une acceptation technique de la requête, et non d’une confirmation de livraison à l’abonné.

Que prouve un message_id SMPP ?

Il prouve que le SMSC a attribué une référence, unique dans le périmètre défini par son implémentation, pour des opérations ultérieures telles que la consultation, l’annulation ou le remplacement. Il est utile pour la corrélation, mais ne constitue pas une preuve de livraison.

Quel statut afficher lorsqu’aucun DLR n’arrive ?

Un statut d’incertitude, tel que « aucun statut final observé » ou « en attente de réconciliation », selon votre politique. L’absence de DLR ne doit pas être étiquetée comme une livraison ou un échec sans preuve supplémentaire.

Comment traiter les DLR dupliqués ?

Enregistrez chaque événement de manière immuable, détectez les doublons à l’aide d’un identifiant ou d’une empreinte stable et rendez idempotente la mise à jour du statut dérivé. Ne supprimez pas les preuves d’origine.

Un DLR peut-il arriver hors séquence ?

Des réponses SMPP peuvent arriver hors séquence : un ESME doit pouvoir les gérer. Pour les DLR et les callbacks HTTP, concevez également le système pour tolérer un ordre imparfait, sans déduire le résultat à partir du seul ordre d’arrivée.

Un DLR delivered prouve-t-il de manière indépendante que l’utilisateur a vu ou lu le message ?

Non. Un DLR delivered est une preuve de livraison communiquée par la chaîne de messagerie. Il ne démontre pas à lui seul la lecture, l’attention de l’utilisateur ni le consentement.

Sources consultées

  1. SMPP Protocol Specification v3.4, Issue 1.2SMPP Developers Forum
  2. Outbound Message Status in Status CallbacksTwilio Documentation
  3. Messages resourceTwilio Documentation
  4. Best Practices for Messaging Delivery Status LoggingTwilio Documentation