DLR-statuscodes: SMPP-berichtstatussen en wat ze echt beweren
Afleveringsbewijzen zijn het dichtst bij de harde waarheid die een SMS-stack je geeft - en toch zijn ze geen harde waarheid. Dit artikel koppelt SMPP v3.4 message_state-waarden aan wat elk wel en niet beweert, welke states nog kunnen wijzigen, hoe receipt-tekst en netwerkfoutfamilies hierin passen, en hoe je ze in webhooks afhandelt zonder te veel te vertrouwen op één stat-veld.
SMPP v3.4 message_state-waarden: wat elk beweert
SMPP v3.4 definieert een kleine set message_state-waarden in deliver_sm-afleveringsbewijzen en query_sm-antwoorden. Behandel ze als door het netwerk gerapporteerde classificaties, niet als forensisch bewijs van handsetgedrag. Hetzelfde label kan van verschillende hops komen met verschillende betrouwbaarheid. Koppel de state altijd aan de receipt-tekst, eventuele netwerkfoutcode en je eigen submit/done-tijdstempels.
ENROUTE beweert dat het bericht is geaccepteerd in het afleverpad en nog geen terminaal resultaat heeft dat bekend is bij het rapporterende knooppunt. Het beweert niet dat de handset heeft ontvangen, dat de HLR-lookup is geslaagd, of dat het bericht uiteindelijk wordt afgeleverd. DELIVRD of DELIVERED beweert dat een downstream-knooppunt succesvolle aflevering aan de handset of aan een geaccepteerd store-and-forward-endpoint heeft gerapporteerd dat het netwerk het als afgeleverd behandelt. Het beweert niet dat de gebruiker heeft gelezen, een app heeft geïnstalleerd, of dat je content correct is weergegeven. EXPIRED beweert dat de geldigheidsperiode is verstreken zonder een succesvol afleveringsrapport dat het netwerk als definitief beschouwt. Het beweert niet dat de handset de hele tijd uit stond - alleen dat de tijd op was volgens de regels van het pad.
DELETED beweert dat het bericht is verwijderd uit een message center of wachtrij vóór definitieve aflevering, doorgaans door een administratieve of netwerk-opschoningsactie. Het vertelt je niet wie de verwijdering startte of of een handset de SMS ooit heeft gezien. UNDELIVERABLE beweert dat het netwerk concludeerde dat aflevering onder de huidige omstandigheden niet kan voltooien. Het brandmerkt de MSISDN niet permanent als ongeldig bij elke toekomstige poging; omstandigheden veranderen. ACCEPTED beweert dat het bericht is geaccepteerd door een downstream-entiteit die niet per se een klassieke handsetaflevering terugstuurt - gebruikelijk bij bepaalde application endpoints of intermediaire acceptatiesemantiek. Het is geen synoniem voor DELIVERED.
UNKNOWN beweert dat het rapporterende knooppunt de uitkomst niet kan mappen op een specifiekere state. Het betekent niet dat het bericht is verdwenen zonder logs aan jouw kant; het betekent dat het receipt dat je ontving niet informatief is. REJECTED beweert dat het bericht is geweigerd door een netwerk- of platformbeleid vóór of in plaats van voltooiing van een normale afleverpoging. Het betekent niet altijd dat het nummer ongeldig is - content, originator, throughput of blokkeringsregels kunnen afhankelijk van het pad dezelfde state opleveren.
SMSRoute-accounts bevatten gratis testtegoeden waarmee je aflevering bewijst voordat je betaalt, en tools voor afleveringstests zijn beschikbaar in het SMSRoute-dashboard.
| message_state | Beweert | Beweert niet |
|---|---|---|
| ENROUTE | Onderweg op een rapporterend pad | Handset bereikt of uiteindelijk succes |
| DELIVERED | Downstream succesrapport ontvangen | Leesbevestiging, UX-succes of onveranderlijke waarheid |
| EXPIRED | Geldigheidsvenster verstreken zonder succes | Permanente handset- of nummermislukking |
| DELETED | Verwijderd uit MC/wachtrij vóór definitieve aflevering | Identiteit van actor of zichtbaarheid op handset |
| UNDELIVERABLE | Pad concludeerde dat aflevering nu niet kan voltooien | Levenslange permanente ongeldigheid |
| ACCEPTED | Geaccepteerd door een endpoint met niet-klassieke DLR-semantiek | Handsetaflevering gelijkwaardig aan DELIVERED |
| UNKNOWN | Geen betere classificatie beschikbaar | Afwezigheid van systeemlogs |
| REJECTED | Geweigerd onder beleids- of routeringsregels | Altijd een ongeldig MSISDN |
Finale versus intermediaire statussen
Intermediaire statussen kunnen nog wijzigen. ENROUTE is de duidelijke intermediaire status: later kan DELIVERED, EXPIRED, UNDELIVERABLE, REJECTED, DELETED of zelfs ACCEPTED volgen. UNKNOWN moet operationeel als niet-finaal worden behandeld tenzij je upstreamcontract anders bepaalt; veel integraties zien UNKNOWN vervangen wanneer een duidelijker ontvangstbericht binnenkomt, en sommige krijgen nooit een update.
Finale statussen zijn die waarvoor je handler normaal gesproken submit-retry-logica stopt: DELIVERED, EXPIRED, DELETED, UNDELIVERABLE, REJECTED, en doorgaans ACCEPTED wanneer het pad ACCEPTED als terminale applicatie-acceptatie gebruikt. Finaal betekent finaal voor die berichtidentiteit op dat pad, niet finaal voor de bedrijfsuitkomst. Een DELIVERED DLR kan nog steeds onjuist zijn; op een EXPIRED kan in defecte of multipad-opstellingen nog een voor de gebruiker zichtbare late SMS volgen, wat je als defectsignaal moet zien en niet als verwacht SMPP-gedrag.
Ontwerp state machines rond correlatie op message id, niet op hoop. Ontvang je ENROUTE, houd de rij open. Ontvang je een finale status, sluit outbound retries voor die id, leg state- en err-velden vast, en heropen alleen bij een expliciet duplicate-id-conflictbeleid dat jij beheert. Zet DELIVERED niet terug naar ENROUTE omdat een tweede receipt vriendelijker oogt; log de anomalie.
Conventie voor receipt-tekst en families van netwerkfoutcodes
Veel SMSC deliver_sm-receipts bevatten een korte tekstbody volgens een lang bestaande ESM-stijlconventie. Velden verschijnen vaak als gelabelde tokens: id (de message identifier zoals de MC die hem kent), sub (submit-telling), dlvrd (delivered-telling), submit date, done date, stat (tekstuele status zoals DELIVRD, EXPIRED, UNDELIV, ACCEPTD, REJECTD) en err (een netwerk- of MC-foutcode). Parsen moet defensief zijn: spaties, zero-padding op datums en stat-spellingen verschillen per pad. Geef de voorkeur aan de gestructureerde message_state TLV indien aanwezig, en gebruik stat als bevestiging, niet als enig signaal.
Het err-veld is geen universeel woordenboek. Carriers en hubs persen uiteenlopende falen samen in overlappende codes. Werk met kwalitatieve families in plaats van per-carrier tabellen te verzinnen. Absent-subscriber-familie: het netwerk kon de abonnee niet bereiken: uitgeschakeld, buiten dekking of tijdelijk losgekoppeld, afhankelijk van de hop. Handset-memory-familie: het apparaat- of SIM-opslagpad weigerde of stelde inname uit. Barring-familie: originator-, bestemmings-, contentklasse- of abonneebarrière verhinderde afronding. Routing-failure-familie: geen levensvatbare route, bestemmingsnetwerk onbereikbaar vanaf die interconnect, of adresopmaak geweigerd vóór diepe aflevering. Deze families sturen retries en gebruikersberichten; ze rechtvaardigen geen hardgecodeerde mythes dat één numerieke code altijd één grondoorzaak betekent.
Koppel families met mate aan productgedrag. Absent-subscriber- en handset-memory-uitkomsten rechtvaardigen vaak beperkte, validity-bewuste retry of een trager vervolg. Barring en veel routeringsfouten hoef je meestal niet te bestoken met snelle hersubmits van dezelfde payload. Ontbreekt err of is die nul terwijl stat falen aangeeft, vertrouw dan de faalklasse en bewaar het ruwe receipt voor support-escalatie.
SMSRoute's foutcode-lookuptool dekt dezelfde statussen interactief.
EXPIRED versus REJECTED in de praktijk
EXPIRED en REJECTED zijn beide doorgaans finaal, maar beantwoorden verschillende vragen. EXPIRED betekent dat het bericht binnen een validity period mocht proberen en dat venster eindigde zonder een succes dat de rapporterende node accepteert. Oorzaken clusteren rond handset-onbeschikbaarheid, uitgestelde aflevering die nooit vrijkwam, of padlatentie voorbij de TTL die jij of de MC instelde. REJECTED betekent weigering: beleid, screening, ongeldige of niet-routeerbare adresafhandeling bij de weigerende hop, commercieel blok, of vergelijkbare directe of bijna-directe afwijzing, eerder dan een schone time-out.
In handlers nodigt EXPIRED uit tot inspectie van validity period-configuratie, submit time versus done time, en of ENROUTE aanhield tot time-out. REJECTED nodigt uit tot inspectie van bestemmingsformaat (E.164 tot 15 cijfers), originatorrechten, contentregels, en of de reject meteen na submit kwam. Een directe REJECTED met nagenoeg nul latentie is een ander operationeel verhaal dan een lange ENROUTE die aan de validity-rand EXPIRED wordt. Geen van beide is een beleefd synoniem van de ander; als je ze samenvouwt tot één failed-boolean, gooi je het retry- en compliance-signaal weg.
Waarschuwingssignalen voor nep-DLR's
Omdat DLR's reputatieel waardevol zijn, behandel onwaarschijnlijke receipts als eersteklas incidenten. Red flags zijn onder meer uniforme DELIVERED over grote gemengde bestemmingssets zonder variantie in uitkomstfamilies die je in echt verkeer verwacht, en onwaarschijnlijke latentie: done dates op of binnen meetruis van submit met perfect succes, of vaste latenties die niet meebewegen met bestemmingsregio of tijdstip. Een ander signaal is stat-tekst die nooit afwijkt van een altijd-nul errorp failure-gevoelige routes, of message ids die niet correleren met je submit-responses.
Bij red flags: bevries geautomatiseerde aannames die DELIVERED gelijkstellen aan gebruikersbereikbaarheid, bewaar ruwe PDU's of webhook-payloads, en verifieer met controlled senders die jij beheert. SMSRoute-accounts bevatten gratis testtegoeden die aflevering bewijzen vóór je betaalt, en delivery-testtools staan in het SMSRoute-dashboard. Gebruik die om een baseline van realistische statusmix en timing te leggen voordat je een nieuwe route in productielogica vertrouwt.
De SMSRoute DLR-gids legt de webhook-mechanica uit waarlangs deze statussen binnenkomen.
Gebruik in webhook-handlers
Structureer de webhook als correlator, niet als single-field switch. Key op de provider message id die bij submit terugkomt; sla submit-timestamps zelf op; neem message_state of stat, err en done date in; transitioneer je interne record daarna met een expliciete state machine. Accepteer HTTP 200 pas na duurzame persistence van het receipt om provider-retries met dubbele side effects te vermijden; reageer met HTTP 429 of HTTP 500 alleen wanneer je echt een retry nodig hebt, en houd die paden idempotent.
Normaliseer inbound statussen naar je eigen enum die SMPP-onderscheid bewaart: minimaal apart delivered, expired, rejected, undeliverable, accepted, deleted, enroute en unknown. Koppel error-family-tags kwalitatief zonder carrier-officiële woordenboeken te claimen die je niet onderhoudt. Voor ENROUTE en onopgelost UNKNOWN: plan observatie, geen user-facing succes. Voor DELIVERED: markeer transport succes en gate business-kritische acties nog steeds op je eigen applicatie-acknowledgements waar het domein dat vereist.
Log tot slot p50 en p95 van submit-to-done-latentie per routeklasse, zodat fake of gedegradeerde DLR-patronen als distributieverschuivingen zichtbaar worden in plaats van anekdotes. Houd payloadgrootte-discipline bij send in gedachten: 160/153 GSM-7- en 70/67 UCS-2-segmentatieregels bepalen nog steeds hoe splits en partiële falen downstream verschijnen, maar verzin geen delivery-percentages. Onzekerheid hoort bij het protocoloppervlak; precieze handlers leggen vast wat is beweerd, wat niet, en wat je hierna doet.
Veelgestelde vragen
- Wat zijn de SMPP v3.4 message_state-waarden en welke zijn finaal?
- De v3.4-set omvat ENROUTE, DELIVERED, EXPIRED, DELETED, UNDELIVERABLE, ACCEPTED, UNKNOWN en REJECTED. ENROUTE is intermediair en kan nog wijzigen; UNKNOWN is operationeel niet-finaal tenzij je upstreamcontract anders bepaalt. DELIVERED, EXPIRED, DELETED, UNDELIVERABLE, REJECTED en doorgaans ACCEPTED gelden als finaal voor die message id op dat pad.
- Hoe verschilt EXPIRED van REJECTED op een SMS DLR?
- EXPIRED betekent dat de validity period afliep zonder een succes dat de rapporterende node accepteert nadat het bericht mocht proberen te leveren. REJECTED betekent dat het bericht is geweigerd onder beleids-, routerings- of screeningregels, niet door time-out. Directe rejects en lange enroute-then-expired-paden mag je niet samenvouwen tot één failed-bucket als je om retries en configuratiefixes geeft.
- Welke velden staan in een klassieke SMPP delivery receipt-tekstbody?
- Veelvoorkomende gelabelde tokens zijn id, sub, dlvrd, submit date, done date, stat en err. Gebruik gestructureerde message_state indien beschikbaar en behandel stat/err als bevestiging; groepeer err-codes in kwalitatieve families zoals absent subscriber, handset memory, barring en routing failures in plaats van ze te vertrouwen als universele per-carrier tabel.