Torna al blog Connettività e operazioni SMS

Identificatori dei messaggi A2P SMS: come normalizzare i riferimenti senza perdere tracciabilità

Progetta un modello di identificatori per separare l’evento di business, il tentativo tecnico e i riferimenti esterni di HTTP, SMPP, provider e DLR. Evita sovrascritture, collisioni e associazioni errate durante cambi di instradamento o stati tardivi.

Diagramma di tracciabilità tra un evento di business, tentativi tecnici, identificatori del provider e DLR SMS

Il problema: un SMS può accumulare diversi riferimenti

Un singolo invio logico può generare vari identificatori durante il suo percorso operativo. L’applicazione mittente può creare un proprio riferimento; un’API HTTP può restituire un ID risorsa; un SMSC o MC può restituire un message_id in submit_sm_resp; il provider può inviare un riferimento diverso in un DLR; e il sistema che riceve il callback può assegnare il proprio ID evento.

Questi riferimenti non sono intercambiabili. Un identificatore restituito da un provider appartiene generalmente all’ambito di quella piattaforma, account, integrazione e ambiente. Non deve essere trattato come chiave primaria globale di business né si deve presumere che sia univoco tra provider, instradamenti, account o ambienti.

Il rischio emerge quando un sistema sovrascrive un ID con un altro, converte in modo irreversibile valori esterni oppure collega eventi basandosi solo su una corrispondenza testuale. Il risultato può essere l’attribuzione di un vecchio DLR a un reinvio, la commistione di stati di due provider o la perdita delle evidenze necessarie per indagare un problema.

  • L’ID dell’evento di business identifica l’intenzione: per esempio, una richiesta OTP o una notifica transazionale autorizzata.
  • L’ID del tentativo tecnico identifica una specifica esecuzione di invio verso un determinato account, provider e instradamento.
  • L’ID esterno identifica la risorsa o il messaggio nel sistema che lo ha emesso.
  • L’ID dell’evento di callback identifica la notifica ricevuta e non necessariamente il messaggio SMS a cui si riferisce.
Il problema: un SMS può accumulare diversi riferimenti

Quali identificatori possono esistere in HTTP, SMPP e DLR

L’inventario esatto dipende dal contratto di ciascuna integrazione, ma è utile modellare categorie stabili. L’obiettivo non è imporre una nomenclatura universale, bensì registrare cosa rappresenta ciascun riferimento, chi lo ha emesso e in quale contesto può essere utilizzato.

In SMPP, submit_sm_resp restituisce un message_id assegnato dall’MC o dall’SMSC. La specifica lo colloca nell’ambito del sistema che accetta il submit e consente di usarlo in operazioni successive, come interrogazione, sostituzione o associazione a una ricevuta. Per questo deve essere conservato come riferimento esterno del tentativo, non come identificatore globale del messaggio.

Una ricevuta SMPP può arrivare tramite deliver_sm o data_sm. Quando presente, il TLV receipted_message_id trasporta il riferimento del messaggio originale restituito in precedenza dall’MC. Possono inoltre esserci dati della ricevuta in altri campi o formati definiti dall’integrazione. Conserva il PDU o la sua rappresentazione grezza, oltre all’estrazione normalizzata.

In HTTP, una risposta di creazione o accettazione può restituire un identificatore del messaggio o della risorsa. La sua accettazione sincrona non dimostra la consegna al terminale. Le modifiche successive possono arrivare tramite callback, interrogazione della risorsa o report di consegna, con un proprio riferimento evento e propri timestamp.

  • internal_event_id: ID immutabile dell’evento logico di business.
  • send_attempt_id: ID immutabile di ciascun tentativo tecnico di invio.
  • client_reference: riferimento opzionale fornito dal cliente o dal sistema di origine.
  • external_message_id: ID restituito dal provider, MC, SMSC o API.
  • dlr_reference: riferimento trasportato nel report di consegna, come receipted_message_id quando applicabile.
  • callback_event_id: ID della notifica ricevuta tramite webhook o infrastruttura di eventi.
  • provider_account_scope: account, tenant, integrazione, ambiente e provider che delimitano il significato di un riferimento.
Quali identificatori possono esistere in HTTP, SMPP e DLR

Principio di progettazione: ID interno immutabile e riferimenti esterni versionati

La base della progettazione è semplice: genera identificatori interni controllati dalla tua organizzazione e non riutilizzarli. Quindi, tratta ogni riferimento esterno come un’evidenza attribuita a una fonte e a un momento di osservazione.

Separare l’evento logico dal tentativo tecnico è essenziale. Un evento di business può generare un tentativo iniziale, un nuovo tentativo controllato o un failover verso un altro instradamento. Ogni tentativo deve disporre del proprio send_attempt_id, anche se dipendono tutti dallo stesso internal_event_id. In questo modo si evita di interpretare una nuova emissione come aggiornamento dell’invio precedente.

Anche i riferimenti esterni non devono essere sovrascritti. Un singolo tentativo può ricevere un riferimento di accettazione, un altro riferimento in un DLR e un riferimento aggiuntivo in un’interrogazione successiva. Registra ciascuno come riga o evento indipendente, con tipo, valore originale, valore di confronto e contesto operativo.

L’interpretazione operativa può invece evolvere. Per esempio, uno stato derivato può passare da in attesa a consegnato o non consegnato con l’arrivo di nuove evidenze. Tuttavia, l’evento ricevuto e l’evidenza che ha motivato tale interpretazione devono rimanere intatti.

  • Usa UUID o un altro schema interno stabile per internal_event_id e send_attempt_id.
  • Non usare un message_id del provider come chiave primaria del dominio di business.
  • Mantieni una relazione uno-a-molti tra tentativo tecnico e riferimenti esterni.
  • Mantieni una relazione uno-a-molti tra tentativo tecnico e osservazioni di stato.
  • Conserva la versione dell’integrazione che ha elaborato ogni risposta o callback.
  • Distingui lo stato osservato dallo stato derivato usato dalle tue operazioni.

Normalizzazione pratica senza distruggere il valore ricevuto

Normalizzare non significa sostituire il valore originale. La regola sicura è memorizzare sempre la rappresentazione esatta ricevuta e creare separatamente una rappresentazione di confronto. Questa seconda rappresentazione serve soltanto per ricerche e regole di collegamento documentate.

Il confronto può richiedere di verificare codifica, lunghezza, spazi, distinzione tra maiuscole e minuscole, prefissi o troncamento. Non applicare trasformazioni universali: modificare le maiuscole può essere innocuo per un’integrazione e distruttivo per un’altra; troncare una stringa può creare una collisione; convertire byte in testo senza conoscere la codifica può alterare l’identificatore.

Ogni normalizzazione deve essere riproducibile. Conserva il nome della regola, la sua versione e il risultato. Se l’integrazione modifica il formato di un riferimento, potrai riesaminare i valori originali senza perdere evidenze.

  • external_value_raw: valore esatto ricevuto, preservato senza trasformazioni.
  • external_value_compare: valore derivato per il confronto secondo una regola esplicita.
  • normalization_rule_version: versione della regola applicata.
  • external_id_type: ad esempio, submit_sm_resp_message_id, receipted_message_id o http_message_id.
  • observed_at: momento in cui il sistema ha ricevuto o osservato il valore.
  • source_payload_id: collegamento al payload, PDU o evento grezzo memorizzato in modo controllato.
  • Non eliminare spazi, zeri, prefissi o caratteri non alfanumerici senza una regola specifica del provider.

Modello dati minimo per indagare senza sovrascrivere le evidenze

Un modello relazionale minimo può risolvere la maggior parte delle indagini se mantiene la separazione tra intenzione, esecuzione, riferimenti e osservazioni. Non deve imporre che tutti i provider restituiscano gli stessi campi; deve registrare in modo esplicito ciò che è stato ricevuto e in quale ambito.

La tabella degli eventi di business rappresenta la richiesta funzionale autorizzata. La tabella dei tentativi rappresenta ciascun invio tecnico. I riferimenti esterni e gli eventi di stato sono associati al tentativo, non direttamente all’evento logico, salvo quando il contratto del provider permetta di dimostrare quella relazione.

Per ridurre al minimo l’esposizione, la destinazione deve essere trattata come dato sensibile. Registrala in una forma internazionale coerente quando necessario per indagini e chiavi composte, con controlli di accesso, conservazione proporzionata e, quando opportuno, tokenizzazione o protezione equivalente. Non è necessario memorizzare il contenuto completo del messaggio per risolvere tutti i problemi; un hash sicuro del payload o di una rappresentazione canonica può aiutare a distinguere i tentativi senza ampliare inutilmente l’esposizione dei dati.

  • business_event: internal_event_id, tenant_id, tipo di 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: destinazione protetta o tokenizzata, hash sicuro del payload, origine dell’invio e dati di audit necessari.

Quando il provider riutilizza, trasforma o non restituisce un riferimento correlabile

Non tutti i provider conservano un riferimento inviato dal cliente, restituiscono un ID stabile o includono lo stesso ID nei DLR. Il modello deve ammettere questo limite senza inventare una relazione che non può essere dimostrata.

Se un provider riutilizza gli identificatori, il riferimento può essere univoco solo all’interno di una chiave composta. Come minimo, includi tenant, provider, account del provider, ambiente, tipo di riferimento e un intervallo temporale di osservazione. Aggiungi instradamento e integrazione quando possono modificare il significato operativo del valore.

Se il provider trasforma l’identificatore, registra entrambi i valori e la regola di trasformazione nota. Se non esiste una regola contrattuale o tecnicamente verificabile, non creare un’associazione automatica basata su una somiglianza parziale. Contrassegna il caso come ambiguo e invialo a riconciliazione o indagine.

Quando non esiste un riferimento correlabile, la tracciabilità può proseguire fino al tentativo tecnico e all’evidenza di accettazione, ma il collegamento con uno specifico DLR rimarrà incerto. Questo limite deve essere visibile nella dashboard e nelle procedure operative.

  • Non deduplicare mai globalmente usando solo external_message_id.
  • Non usare corrispondenze per prefisso, suffisso o troncamento come prova di identità.
  • Richiedi una chiave composta con ambito operativo per ciascuna regola di ricerca.
  • Classifica i collegamenti come confermati, probabili o non correlabili; riserva le automazioni irreversibili ai collegamenti confermati.
  • Documenta quali riferimenti restituisce ciascun provider e quali possono comparire nei relativi DLR.

submit_sm_resp, DLR e stati asincroni: cosa può essere collegato

In un flusso SMPP tipico, l’ESME invia submit_sm e riceve submit_sm_resp. Il message_id nella risposta identifica il messaggio nell’MC o nell’SMSC che ha risposto. Se è stata richiesta una ricevuta tramite registered_delivery e il sistema emette un DLR, la ricevuta può indicare di essere un MC Delivery Receipt tramite esm_class e trasportare l’identificatore del messaggio ricevuto nel TLV receipted_message_id.

Questa relazione consente una correlazione forte quando receipted_message_id corrisponde al message_id registrato in precedenza, nello stesso provider, account, ambiente e integrazione. Tuttavia, conserva il DLR completo: stato, timestamp e campi disponibili fanno parte dell’evidenza e possono essere necessari in caso di duplicati o eventi fuori ordine.

In HTTP, l’identificatore restituito alla creazione di una risorsa può servire in seguito per interrogarne lo stato o collegare callback, secondo il contratto del provider. Un codice HTTP di creazione o accettazione indica che la piattaforma ha elaborato o messo in coda la richiesta secondo la sua semantica; non prova di per sé la consegna al terminale.

I callback possono arrivare in ritardo, duplicati o fuori ordine. Non scartare automaticamente un’osservazione solo perché è precedente rispetto all’ora di ricezione. Confronta il timestamp del provider, quello di ricezione e la sequenza nota; quindi applica regole di chiusura e riconciliazione verificabili.

  • Persisti submit_sm, submit_sm_resp e DLR come fasi separate.
  • Richiedi DLR tramite registered_delivery quando il contratto SMPP e il caso d’uso lo richiedono.
  • Non trasformare un DLR in prova di lettura umana o di qualità generale dell’instradamento.
  • Non presumere che uno stato terminale impedisca l’arrivo successivo di evidenze contraddittorie o duplicate.
  • Mantieni una politica documentata per decidere quale stato derivato mostrare, senza eliminare gli stati precedenti.

Cambi di instradamento, reinvii e duplicati: modellare per contesto

Un failover, un nuovo tentativo o una nuova emissione possono corrispondere allo stesso evento di business, ma non sono necessariamente lo stesso messaggio tecnico. La regola pratica è creare un nuovo send_attempt_id per ogni emissione verso una combinazione specifica di tenant, provider, account, instradamento, ambiente e integrazione.

Non consolidare i DLR di instradamenti diversi come aggiornamenti dello stesso tentativo. Uno stato di un instradamento precedente non deve essere attribuito a un nuovo instradamento solo perché destinazione, contenuto o riferimento esterno sembrano simili. La relazione corretta viene mantenuta tramite internal_event_id, mentre le evidenze di ciascun provider restano associate al proprio tentativo.

L’idempotenza deve essere applicata prima dell’invio. Per le richieste HTTP, POST non è idempotente per definizione; un nuovo tentativo dopo una risposta incerta può duplicare un invio se non esiste una chiave di idempotenza applicativa o una conferma affidabile che l’operazione precedente non sia stata eseguita. Non dipendere da un ID esterno che potrebbe non essere stato ancora restituito.

Anche un reinvio deliberato deve essere visibile come tale. Registra la causa: timeout di accettazione, guasto tecnico, policy di failover, decisione manuale o altro motivo autorizzato. Questo consente di distinguere una duplicazione accidentale da una seconda esecuzione controllata.

  • Chiave di contesto consigliata: tenant_id, provider_id, provider_account_id, route_id, environment, external_id_type e external_value_compare.
  • Aggiungi finestre temporali solo come vincolo aggiuntivo, non come prova unica di identità.
  • Usa idempotency_key per evento o intenzione di business prima di invocare il provider.
  • Registra retry_sequence, failover_reason e la relazione tra tentativo di origine e tentativo successore.
  • Evita di inviare contenuti sensibili non necessari a log, strumenti di ricerca o URL.
FAQ

Domande frequenti

Il message_id di submit_sm_resp può essere usato come ID globale del messaggio?

No. È un riferimento assegnato dall’MC o dall’SMSC che ha risposto e deve essere interpretato nel suo ambito operativo. Conservalo con provider, account, ambiente, integrazione, tipo di riferimento e momento di osservazione.

Un HTTP 202 o una risposta API riuscita conferma la consegna dell’SMS?

Non necessariamente. Un’accettazione o una creazione conferma il trattamento della richiesta secondo l’API, ma la consegna richiede l’osservazione di un report di stato successivo o l’interrogazione della risorsa quando il provider lo consente.

Cosa devo fare se il DLR arriva prima, dopo o duplicato rispetto ad altri eventi?

Conserva ogni osservazione senza sovrascriverla. Registra l’ora del provider e l’ora di ricezione, applica una regola di interpretazione versionata e mantieni l’evento grezzo per la riconciliazione.

Devo conservare il contenuto dell’SMS per correlare i messaggi?

Non è indispensabile in tutti i casi. Dai priorità alla minimizzazione dei dati. Se devi distinguere i tentativi, considera un hash sicuro di una rappresentazione controllata del payload e proteggi i dati di destinazione e i metadati associati.

Uno stato delivered dimostra la ricezione o la lettura da parte di una persona?

No. Rappresenta la conferma di consegna che il provider riceve dalla propria catena a monte e, quando disponibile, dal terminale. Non è una prova universale di lettura umana né una garanzia indipendente sulla qualità dell’instradamento.

Fonti consultate

  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