Codici di stato DLR: stati messaggio SMPP e cosa affermano davvero
Le ricevute di consegna sono quanto di più vicino alla verità fattuale uno stack SMS possa offrirti, eppure non sono ancora verità fattuale. Questo articolo mappa i valori message_state di SMPP v3.4 a ciò che ciascuno afferma e non afferma, quali stati possono ancora cambiare, come si inseriscono il testo della ricevuta e le famiglie di errori di rete, e come gestirli nei webhook senza fidarsi eccessivamente di un singolo campo di stato.
Valori message_state SMPP v3.4: cosa afferma ciascuno
SMPP v3.4 definisce un piccolo insieme di valori message_state usati nei delivery receipt deliver_sm e nelle risposte query_sm. Trattali come classificazioni segnalate dalla rete, non come prova forense del comportamento del handset. La stessa etichetta può arrivare da hop diversi con confidenza diversa. Abbina sempre lo stato al testo del receipt, a eventuali codici di errore di rete e ai tuoi timestamp submit/done.
ENROUTE afferma che il messaggio è stato accettato nel percorso di consegna e non è ancora in un esito terminale noto al nodo segnalante. Non afferma che il handset abbia squillato, che la lookup HLR sia riuscita o che il messaggio verrà infine consegnato. DELIVRD o DELIVERED afferma che un nodo a valle ha segnalato consegna riuscita al handset o a un endpoint store-and-forward accettato che la rete tratta come consegnato. Non afferma lettura utente, installazione app o rendering corretto dei contenuti. EXPIRED afferma che il periodo di validità è scaduto senza un report di consegna riuscita che la rete considera definitivo. Non afferma che il handset fosse spento per tutto il tempo, solo che il tempo è scaduto secondo le regole del percorso.
DELETED afferma che il messaggio è stato rimosso da un message center o da una coda prima della consegna finale, tipicamente da un'azione amministrativa o di purge di rete. Non indica chi ha avviato l'eliminazione né se un handset abbia mai visto l'SMS. UNDELIVERABLE afferma che la rete ha concluso che la consegna non può completarsi nelle condizioni attuali. Non marchia permanentemente l'MSISDN come non valido in ogni tentativo futuro; le condizioni cambiano. ACCEPTED afferma che il messaggio è stato accettato da un'entità a valle che non restituirà necessariamente una classica consegna al handset, comune per certi endpoint applicativi o semantiche di accettazione intermedie. Non è sinonimo di DELIVERED.
UNKNOWN afferma che il nodo segnalante non può mappare l'esito a uno stato più specifico. Non significa che il messaggio sia sparito senza log dalla tua parte; significa che il receipt ricevuto è poco informativo. REJECTED afferma che il messaggio è stato rifiutato da una policy di rete o di piattaforma prima o al posto del completamento del normale tentativo di consegna. Non significa sempre che il numero non sia valido: contenuti, originator, throughput o regole di barring possono produrre lo stesso stato a seconda del percorso.
Gli account SMSRoute includono crediti di test gratuiti che dimostrano la consegna prima del pagamento, e gli strumenti di test di consegna sono disponibili nella dashboard SMSRoute.
| message_state | Afferma | Non afferma |
|---|---|---|
| ENROUTE | In transito su un percorso segnalante | Handset raggiunto o successo finale |
| DELIVERED | Report di successo a valle ricevuto | Read receipt, successo UX o verità immutabile |
| EXPIRED | Finestra di validità terminata senza successo | Guasto permanente di handset o numero |
| DELETED | Rimosso da MC/coda prima della consegna finale | Identità dell'attore o visibilità al handset |
| UNDELIVERABLE | Il percorso ha concluso che la consegna non può completarsi ora | Invalidità permanente a vita |
| ACCEPTED | Accettato da un endpoint con semantiche DLR non classiche | Consegna al handset equivalente a DELIVERED |
| UNKNOWN | Nessuna classificazione migliore disponibile | Assenza di qualsiasi log di sistema |
| REJECTED | Rifiutato per policy o regole di routing | Sempre un MSISDN non valido |
Stati finali rispetto a intermedi
Gli stati intermedi possono ancora cambiare. ENROUTE è lo stato intermedio chiaro: può seguire un successivo DELIVERED, EXPIRED, UNDELIVERABLE, REJECTED, DELETED o anche ACCEPTED. UNKNOWN va trattato operativamente come non finale salvo diverso accordo upstream; molte integrazioni vedono UNKNOWN sostituito quando arriva una ricevuta più chiara, e alcune non ricevono mai un aggiornamento.
Gli stati finali sono quelli per cui l'handler dovrebbe normalmente smettere di ritentare la logica di submit: DELIVERED, EXPIRED, DELETED, UNDELIVERABLE, REJECTED e tipicamente ACCEPTED quando il path usa ACCEPTED come accettazione applicativa terminale. Finale significa finale per quell'identità di messaggio su quel path, non finale per l'esito di business. Un DLR DELIVERED può comunque essere errato; un EXPIRED può essere seguito da un SMS tardivo visibile all'utente solo in setup rotti o multi-path, da trattare come segnale di difetto e non come comportamento SMPP atteso.
Progetta le state machine sulla correlazione del message id, non sulla speranza. Se ricevi ENROUTE, tieni la riga aperta. Se ricevi uno stato finale, chiudi i retry in uscita per quell'id, registra i campi state e err, e riapri solo con una policy esplicita di conflitto duplicate-id che controlli tu. Non riportare DELIVERED a ENROUTE perché una seconda ricevuta sembra più amichevole: registra l'anomalia.
Convenzione del testo della ricevuta e famiglie di error-code di rete
Molte ricevute deliver_sm degli SMSC incorporano un breve corpo di testo secondo una consolidata convenzione in stile ESM. I campi compaiono di solito come token etichettati: id (l'identificatore del messaggio come lo conosce l'MC), sub (conteggio submit), dlvrd (conteggio delivered), submit date, done date, stat (stato testuale come DELIVRD, EXPIRED, UNDELIV, ACCEPTD, REJECTD) e err (un error code di rete o MC). Il parsing deve essere difensivo: spaziatura, zero-padding delle date e ortografia di stat variano per path. Preferisci il TLV message_state strutturato quando presente e usa stat come corroborazione, non come unico segnale.
Il campo err non è un dizionario universale. Carrier e hub comprimono fallimenti distinti in codici sovrapposti. Lavora per famiglie qualitative invece di inventare tabelle per-carrier. Famiglia absent-subscriber: la rete non ha potuto raggiungere l'abbonato (spento, fuori copertura o temporaneamente distaccato, a seconda dell'hop). Famiglia handset-memory: il dispositivo o il path di storage SIM ha rifiutato o differito la presa in carico. Famiglia barring: barring di originator, destinazione, content class o subscriber ha impedito il completamento. Famiglia routing-failure: nessuna route praticabile, rete di destinazione irraggiungibile da quell'interconnect, o formato indirizzo rifiutato prima della consegna profonda. Queste famiglie guidano retry e messaggi all'utente; non giustificano miti codificati rigidamente su un singolo codice numerico che significhi sempre una sola causa radice.
Mappa le famiglie al comportamento di prodotto con misura. Gli esiti absent-subscriber e handset-memory spesso giustificano retry limitati e sensibili alla validità, o un follow-up più lento. Barring e molti routing failure di solito non vanno martellati con resubmit rapidi dello stesso payload. Quando err manca o è zero mentre stat indica fallimento, credi alla classe di fallimento e conserva la ricevuta grezza per l'escalation al supporto.
Lo strumento di lookup error-code di SMSRoute copre gli stessi stati in modo interattivo.
EXPIRED rispetto a REJECTED in pratica
EXPIRED e REJECTED sono entrambi tipicamente finali, ma rispondono a domande diverse. EXPIRED significa che al messaggio è stato consentito di tentare entro un validity period e quella finestra è scaduta senza un successo accettato dal nodo che riporta. Le cause si concentrano su indisponibilità handset, delivery differita mai sbloccata o latenza di path oltre il TTL impostato da te o dall'MC. REJECTED significa rifiuto (policy, screening, gestione indirizzo non valido o non routabile all'hop che rifiuta, blocco commerciale o analoga negazione immediata o quasi) piuttosto che un semplice esaurimento del tempo.
Negli handler, EXPIRED invita a ispezionare la configurazione del validity period, submit time rispetto a done time e se ENROUTE è rimasto fino al timeout. REJECTED invita a ispezionare il formato destinazione (E.164 fino a 15 cifre), i permessi originator, le regole di contenuto e se il reject è avvenuto subito dopo il submit. Un REJECTED immediato con latenza quasi zero è una storia operativa diversa da un lungo ENROUTE che diventa EXPIRED al bordo della validity. Nessuno dei due stati è un sinonimo educato dell'altro; collassarli in un unico booleano di fallimento butta via segnale di retry e compliance.
Campanello d'allarme dei fake-DLR
Poiché i DLR hanno valore reputazionale, tratta le ricevute implausibili come incidenti di prima classe. Le red flag includono DELIVERED uniforme su grandi set di destinazioni miste senza varianza nelle famiglie di esito che sai dovrebbero apparire nel traffico reale, e latenza implausibile: fino al rumore di misura del submit o oltre con successo perfetto, o latenze fisse che non si muovono con la regione di destinazione o l'operatoreora del giorno. Un altro segnale è testo stat che non diverge mai da un err sempre zero su route propense al fallimento, o message id che non correlano con le tue risposte di submit.
Quando compaiono red flag, congela le assunzioni automatiche che equiparano DELIVERED alla raggiungibilità utente, conserva PDU grezze o payload webhook e verifica con sender controllati che operi tu. Gli account SMSRoute includono credit di test gratuiti che dimostrano la delivery prima di pagare, e strumenti di delivery-testing sono disponibili nella dashboard SMSRoute. Usali per stabilire una baseline di mix di stati e timing realistici prima di fidarti di una nuova route nella logica di produzione.
La guida DLR di SMSRoute spiega le meccaniche webhook attraverso cui arrivano questi stati.
Come usarli negli handler webhook
Struttura il webhook come correlatore, non come switch su un solo campo. Chiavi sul message id del provider restituito al submit; memorizza tu i timestamp di submit; ingerisci message_state o stat, err e done date; poi transiziona il record interno con una state machine esplicita. Accetta HTTP 200 solo dopo persistenza durevole della ricevuta per evitare che i retry del provider raddoppino gli side effect; rispondi con HTTP 429 o HTTP 500 solo quando ti serve davvero un retry, e tieni quei path idempotenti.
Normalizza gli stati inbound in un tuo enum che preservi le distinzioni SMPP: come minimo separa delivered, expired, rejected, undeliverable, accepted, deleted, enroute e unknown. Allega tag di famiglia errore in modo qualitativo senza rivendicare dizionari ufficiali carrier che non mantieni. Per ENROUTE e UNKNOWN non risolto, pianifica osservazione, non successo rivolto all'utente. Per DELIVERED, marca successo di trasporto e comunque subordina le azioni business-critical ai tuoi acknowledgement applicativi dove il dominio lo richiede.
Infine, registra p50 e p95 della latenza submit-to-done per classe di route così che pattern DLR fake o degradati emergano come shift di distribuzione anziché aneddoti. Tieni a mente la disciplina sulla size del payload al send (regole di segmentazione 160/153 GSM-7 e 70/67 UCS-2 modellano ancora come split e fallimenti parziali appaiono a valle) ma non inventare percentuali di delivery. L'incertezza fa parte della superficie del protocollo; handler precisi registrano cosa è stato asserito, cosa no e cosa farai dopo.
Domande frequenti
- Quali sono i valori message_state di SMPP v3.4 e quali sono finali?
- Il set v3.4 include ENROUTE, DELIVERED, EXPIRED, DELETED, UNDELIVERABLE, ACCEPTED, UNKNOWN e REJECTED. ENROUTE è intermedio e può ancora cambiare; UNKNOWN è operativamente non finale salvo diverso accordo upstream. DELIVERED, EXPIRED, DELETED, UNDELIVERABLE, REJECTED e tipicamente ACCEPTED sono trattati come finali per quel message id su quel path.
- In cosa EXPIRED differisce da REJECTED su un DLR SMS?
- EXPIRED significa che il validity period è scaduto senza un successo accettato dal nodo che riporta dopo che al messaggio è stato consentito di tentare la delivery. REJECTED significa che il messaggio è stato rifiutato per policy, routing o regole di screening piuttosto che per timeout. Reject immediati e path lunghi enroute-poi-expired non vanno collassati in un unico bucket failed se ti interessano retry e fix di configurazione.
- Quali campi compaiono nel corpo testo di una classica ricevuta di delivery SMPP?
- I token etichettati comuni includono id, sub, dlvrd, submit date, done date, stat e err. Usa message_state strutturato quando disponibile e tratta stat/err come corroborazione; i codici err vanno raggruppati in famiglie qualitative come absent subscriber, handset memory, barring e routing failure piuttosto che fidarsi di una tabella universale per-carrier.