Aceite não significa entregue: como reconciliar estados de envio e DLR em SMS A2P
Um framework operacional para separar confirmações de API e SMPP, aceitação de fornecedores, estados de rede e DLR finais, sem transformar evidência de transporte em uma afirmação de recebimento no terminal.

A pergunta operacional: que evidência existe realmente depois de enviar um SMS?
Em SMS A2P, a palavra “enviado” pode descrever factos muito diferentes. Pode significar que uma aplicação criou um pedido HTTP, que um ESME recebeu um submit_sm_resp com êxito, que um fornecedor aceitou o tráfego, que uma operadora upstream aceitou a mensagem ou que foi recebido um comprovativo de entrega. Estes sinais não têm o mesmo valor probatório.
A regra prática é simples: cada estado deve expressar a evidência disponível, e não a conclusão mais favorável. Uma resposta bem-sucedida de uma API ou um command_status correto em submit_sm_resp confirma o resultado dessa interação técnica. Por si só, não confirma que a mensagem tenha chegado à rede de destino nem que o assinante a tenha recebido no terminal.
Esta distinção é especialmente importante em fluxos de OTP e transacionais. Um sistema pode decidir efetuar novas tentativas, mostrar um ecrã ao utilizador ou abrir uma investigação operacional com base no estado errado se transformar “aceite” em “entregue”. Em marketing legítimo, o mesmo erro pode distorcer métricas e decisões sobre a qualidade da rota.
Um DLR também não deve ser apresentado automaticamente como uma verificação independente do recebimento físico no terminal. É evidência de estado comunicada através da cadeia de mensagens. O seu significado concreto depende do evento recebido, dos campos disponíveis e das garantias oferecidas por cada participante da rota.
- Confirmação de pedido: evidência de que uma entidade processou um pedido técnico.
- Aceitação de fornecedor ou operadora: evidência de admissão para processamento posterior.
- Estado intermédio: evidência de fila, envio ou trânsito, mas não necessariamente de resultado final.
- DLR final: evidência comunicada de entrega ou não entrega; deve ser conservada com a respetiva origem e carga original.
- Silêncio: ausência de um evento observado, não prova de entrega nem de falha.

As quatro camadas que não devem ser misturadas
Uma arquitetura de reconciliação robusta separa pelo menos quatro camadas. A primeira é a aceitação local: a aplicação validou e enviou um pedido, ou colocou-o na sua própria fila. É um facto interno e não informa sobre o resultado remoto.
A segunda é a aceitação remota. Em SMPP, cada operação, exceto alert_notification, tem um pedido e uma resposta associados. Se o originador não receber a resposta, não tem confirmação de que o pedido foi aceite pela entidade remota e deve tratá-lo como não confirmado. Quando recebe submit_sm_resp, command_status comunica o êxito ou a falha do pedido submit_sm. Esta confirmação de transporte não é um DLR.
A terceira camada é o processamento pelo fornecedor ou pela rede. Em APIs HTTP, alguns fornecedores expõem estados como accepted, queued, sending ou sent. Estes podem ser valiosos para localizar uma fila, uma fase de despacho ou a aceitação por uma operadora upstream, mas continuam a ser diferentes de uma confirmação de entrega.
A quarta camada é o resultado comunicado. O SMPP define valores de message_state como ENROUTE, DELIVERED, EXPIRED, UNDELIVERABLE, UNKNOWN, ACCEPTED e REJECTED. A utilização e a semântica operacional destes valores dependem da implementação e da integração. Esta variedade demonstra por que motivo um único campo booleano de sucesso tende a perder informação crítica para operações, apoio ao cliente e análise de qualidade.
- Camada 1: pedido criado ou aceite pelo próprio sistema.
- Camada 2: pedido aceite ou rejeitado por API, SMSC ou fornecedor.
- Camada 3: processamento, fila, despacho ou trânsito comunicado pela cadeia de entrega.
- Camada 4: resultado final ou estado ainda não resolvido comunicado por DLR, consulta ou reconciliação.

Porque um message_id não é uma prova de entrega
Em SMPP, message_id é uma referência única atribuída pelo SMSC. A especificação descreve-o como um valor opaco e dependente da implementação. Pode ser utilizado como identificador em operações posteriores, por exemplo query_sm, cancel_sm ou replace_sm. A sua existência permite correlacionar operações; não comprova que o destinatário recebeu o SMS.
Uma integração não deve depender de um único identificador. O fornecedor pode gerar o seu próprio identificador, um SMSC pode devolver outro e um DLR pode incluir receipted_message_id. Além disso, uma mensagem de negócio pode necessitar do seu próprio identificador estável para relacionar a tentativa de envio com uma transação, uma sessão OTP ou um aviso transacional legítimo.
A prática recomendada é criar um identificador interno imutável antes do envio. Esse identificador deve ser associado a cada tentativa técnica, às respetivas referências externas, aos eventos recebidos e ao estado derivado. Se forem efetuadas novas tentativas, cada tentativa deve ter uma identidade própria e manter uma relação explícita com a mensagem lógica original.
Não utilize o número de destino, o texto completo da mensagem nem uma marca temporal aproximada como chave principal de correlação. Estes campos podem repetir-se, mudar devido à segmentação ou apresentar questões de privacidade. Conserve apenas os dados necessários para a operação e aplique os controlos de proteção de dados adequados.
- message_logical_id: identifica a mensagem ou ação de negócio.
- attempt_id: identifica cada tentativa técnica de envio.
- provider_message_id: identificador devolvido pela API ou pelo fornecedor.
- smpp_message_id: identificador devolvido por submit_sm_resp, quando aplicável.
- receipted_message_id: referência incluída num DLR SMPP, quando aplicável.
- event_id ou impressão digital do evento: permite detetar duplicados sem eliminar evidência.
- correlation_version: documenta as regras de correlação caso mudem ao longo do tempo.
Modelo de máquina de estados para um SMS A2P
A máquina de estados deve distinguir eventos observados de conclusões derivadas. Um evento é imutável: por exemplo, uma resposta HTTP aceite, um submit_sm_resp bem-sucedido, um deliver_sm com message_state ou uma consulta posterior. O estado atual é uma projeção calculada a partir de todos os eventos correlacionados e de regras explícitas.
Um modelo mínimo pode manter estados internos de criação, envio local e espera por resposta; estados de aceitação remota; estados transitórios de processamento; estados finais comunicados; e um estado de reconciliação que indique se o caso está encerrado, pendente de observação ou requer revisão. Não é necessário obrigar todos os fornecedores a encaixar numa taxonomia mais precisa do que a evidência disponível.
Para SMPP, trate o sucesso de submit_sm_resp como “aceite pela entidade SMPP”, e não como “entregue”. Quando chegarem comprovativos através de deliver_sm, registe os valores disponíveis, incluindo receipted_message_id, message_state e, se existir, network_error_code. As notificações intermédias e os DLR podem partilhar este mecanismo de transporte.
Uma projeção prudente pode apresentar “entrega comunicada” quando a evidência normalizada for DELIVERED ou equivalente comunicado pelo fornecedor. Pode apresentar “não entrega comunicada” para estados finais negativos como UNDELIVERABLE, EXPIRED ou REJECTED, sem eliminar o código original. Quando existe apenas um estado de trânsito, o resultado deve continuar pendente ou em curso.
- CREATED: foi criada a intenção de enviar, sem evidência remota.
- SUBMITTED_LOCAL: o sistema tentou transmitir o pedido.
- REMOTE_ACCEPTED: a API, o SMSC ou o fornecedor aceitou o pedido técnico.
- IN_PROGRESS: existe evidência de fila, despacho, trânsito ou ENROUTE.
- DELIVERED_REPORTED: foi recebido um estado final positivo comunicado pela cadeia de mensagens.
- FAILED_REPORTED: foi recebido um estado final negativo comunicado pela cadeia de mensagens.
- UNKNOWN_OR_UNRESOLVED: não existe evidência final suficiente ou foi recebido UNKNOWN.
- RECONCILIATION_PENDING: expirou uma janela operacional e o caso requer consulta ou classificação de encerramento.
Como normalizar HTTP, SMPP e DLR heterogéneos sem perder o dado original
A normalização serve para operar de forma consistente, não para substituir a semântica de origem. Guarde sempre o evento original juntamente com uma representação normalizada. A carga original é necessária para auditoria técnica, depuração de mapeamentos e adaptação quando um fornecedor adiciona ou altera campos em callbacks.
Para cada evento, registe no mínimo a origem, o tipo de interface, o momento de receção pela sua plataforma, o identificador externo disponível, o estado original, o estado normalizado, os códigos de erro e a carga original protegida. Aplique minimização de dados, controlos de acesso, retenção limitada e, quando adequado, seudonimização ou cifragem. Se o fornecedor comunicar uma data originada na rede, armazene-a separadamente da hora de receção: não representam necessariamente o mesmo instante.
Em SMPP, um deliver_sm pode transportar comprovativos de entrega. Para estes eventos, receipted_message_id e message_state são parâmetros relevantes; network_error_code pode estar presente. Não descarte um comprovativo porque falta um campo não garantido pela sua integração: classifique-o com a evidência disponível e marque-o para revisão se não puder ser correlacionado com segurança.
Em HTTP, não presuma que todos os callbacks contêm os mesmos campos nem que todos os estados existem para todos os canais ou configurações. Mantenha uma tabela de mapeamento versionada por fornecedor e interface. O mapeamento deve transformar estados externos em categorias operacionais amplas, preservando o valor externo literal.
- Não substitua o estado original pelo normalizado; conserve ambos.
- Diferencie event_received_at de event_reported_at quando a origem fornecer a sua própria data.
- Registe códigos e textos de erro sem os transformar em diagnósticos não confirmados.
- Aplique minimização de dados, controlos de acesso, retenção limitada e, quando adequado, seudonimização ou cifragem.
- Versione as regras de mapeamento e precedência.
- Mantenha eventos não correlacionados numa fila de investigação, em vez de os associar por semelhança fraca.
Estados transitórios, finais e desconhecidos: regras explícitas
Classifique os estados pela sua função operacional. Os transitórios indicam que pode chegar informação adicional; os finais comunicam um resultado e, de acordo com as suas regras, encerram a tentativa; os desconhecidos expressam falta de resolução e não devem ser convertidos em sucesso ou falha por conveniência analítica.
O SMPP enumera ENROUTE como estado de trânsito e DELIVERED, EXPIRED, DELETED, UNDELIVERABLE e REJECTED entre os valores de message_state que podem ter caráter final consoante o contexto da integração. UNKNOWN exige especial prudência: informa que o estado não pode ser determinado, não que a mensagem falhou nem que foi entregue.
Defina antecipadamente que estados encerram uma tentativa e quais permitem mais eventos. A regra não deve residir apenas em código implícito. Deve ser documentada, verificável e passível de alteração controlada se uma interface ou relação operacional mudar.
Não transforme a passagem do tempo numa prova de entrega. O tempo só pode desencadear uma ação de reconciliação ou um encerramento administrativo com incerteza declarada. Se não existir DLR, o resultado correto pode ser “sem estado final observado dentro da janela”, e não “entregue”.
- Transitórios: queued, sending, sent, ENROUTE ou equivalentes comunicados pela origem.
- Finais positivos comunicados: DELIVERED ou equivalente explícito do fornecedor.
- Finais negativos comunicados: EXPIRED, UNDELIVERABLE, REJECTED ou equivalentes explícitos.
- Indeterminados: UNKNOWN, erros de correlação, ausência de DLR e eventos incompletos.
- Administrativos: encerrado por política operacional, sempre separado do resultado de entrega comunicado.
DLR duplicados, fora de ordem ou contraditórios
Os sistemas de mensagens devem ser concebidos para eventos repetidos e ordenação imperfeita. O SMPP indica que um SMSC deve responder pela mesma ordem em que recebe os pedidos, mas não é obrigado a fazê-lo, e o ESME deve conseguir lidar com respostas fora de sequência. Esta precaução deve estender-se a callbacks HTTP, filas internas e processos de nova tentativa.
A resposta não é substituir o último dado recebido. Armazene cada evento como um facto imutável e derive o estado atual com uma política de precedência. A política deve considerar pelo menos a correlação, a categoria do estado, a hora comunicada pela origem quando fiável, a hora de receção, a origem e a versão da regra aplicada.
Um duplicado pode ser detetado com um identificador de evento, quando existir, ou através de uma impressão digital calculada sobre campos estáveis do evento. Marcá-lo como duplicado não implica eliminá-lo: conserva valor para diagnóstico e demonstra que o sistema recebeu mais de uma notificação.
Perante contradições, não invente uma resolução. Por exemplo, se surgir um sinal final positivo e depois um evento negativo que não possa ser explicado por uma tentativa diferente, preserve ambos, assinale o caso como contraditório e aplique uma regra de apresentação prudente. A vista operacional pode exigir revisão, enquanto a auditoria deve mostrar a sequência completa.
- Não utilize “o último evento prevalece” como única regra.
- Deduzir o estado por tentativa, e não apenas por mensagem lógica.
- Aplique deduplicação idempotente antes de atualizar projeções.
- Separe eventos tardios de eventos inválidos: um evento tardio pode ser legítimo.
- Escale contradições que não possam ser resolvidas para revisão operacional.
- Registe o motivo de cada alteração de estado derivado.
O que fazer quando não chega um DLR
A ausência de DLR não demonstra que a mensagem não foi processada nem que não foi entregue. Pode indicar que não foi recebido um callback, que a integração não o solicitou ou não o suporta para esse caso, que o evento não pôde ser correlacionado ou que a cadeia ainda não produziu um resultado observável.
Estabeleça janelas de espera por caso de uso, fornecedor e interface apenas quando tiver uma política operacional justificada. Evite transformar uma recomendação de um fornecedor numa regra universal. A documentação de registo de estados da Twilio recomenda consultar o recurso se não tiver sido recebido delivered ou undelivered em 12 horas e reconciliar estados pelo menos diariamente. Esta é uma orientação específica desse fornecedor, que deve ser adaptada e validada para cada integração, não assumida como comportamento de toda a indústria.
Para OTP legítimos, o objetivo do produto geralmente exige uma decisão rápida, mas o estado de entrega não deve ser substituído por uma suposição. Conceba alternativas de autenticação adequadas, controle novas tentativas para evitar duplicação e registe que o resultado ficou pendente se não chegar evidência final. Para mensagens transacionais, uma janela mais ampla pode ser adequada segundo a criticidade e as expectativas do processo. Para marketing com consentimento, separe a medição de entrega comunicada das métricas de interação e não infira leitura a partir do DLR.
O encerramento operacional deve distinguir entre “resultado final comunicado”, “resultado consultado”, “sem DLR observado” e “não correlacionável”. Esta precisão reduz disputas entre sistemas e evita que os relatórios de qualidade transformem incerteza numa taxa artificial de entrega.
- Verifique se o pedido de DLR ou callback está configurado quando a interface o permitir.
- Verifique a disponibilidade do endpoint recetor, a autenticação e o registo de erros de callback.
- Agende consultas de reconciliação quando o fornecedor oferecer uma fonte de estado consultável.
- Encerre com uma etiqueta de incerteza se não existir evidência final.
- Não atribua o silêncio a uma falha de rede, falha de fornecedor ou entrega sem dados que o sustentem.
- Meça separadamente DLR solicitados, DLR recebidos, DLR correlacionados e casos sem resolução final.
Perguntas frequentes
Um submit_sm_resp com command_status correto confirma a entrega do SMS?
Não. Confirma o resultado do pedido submit_sm junto da entidade SMPP remota. É uma aceitação técnica do pedido, e não uma confirmação de entrega ao assinante.
O que demonstra um message_id de SMPP?
Demonstra que o SMSC atribuiu uma referência para operações posteriores, como consulta, cancelamento ou substituição. É útil para correlação, mas não é uma prova de entrega.
Que estado deve ser apresentado se não chegar qualquer DLR?
Um estado de incerteza, como “sem estado final observado” ou “pendente de reconciliação”, de acordo com a sua política. A ausência de DLR não deve ser identificada como entrega nem como falha sem evidência adicional.
Como devem ser tratados DLR duplicados?
Registe cada evento de forma imutável, detete duplicados através de um identificador ou impressão digital estável e torne idempotente a atualização do estado derivado. Não elimine a evidência original.
Pode chegar um DLR fora de ordem?
Sim. O SMPP exige que uma implementação consiga lidar com respostas fora de sequência. Por isso, a ordem de chegada não deve ser a única base para decidir o estado final.
Um DLR delivered prova de forma independente que o utilizador viu ou leu a mensagem?
Não. Um DLR delivered é evidência de entrega comunicada pela cadeia de mensagens. Por si só, não demonstra leitura, atenção do utilizador nem consentimento.
Fontes consultadas
- SMPP Protocol Specification v3.4, Issue 1.2SMPP Developers Forum
- Outbound Message Status in Status CallbacksTwilio Documentation
- Messages resourceTwilio Documentation
- Best Practices for Messaging Delivery Status LoggingTwilio Documentation