Callbacks de DLR em SMS A2P: design idempotente perante duplicados e desordem
Conceba a receção de callbacks de DLR como um histórico auditável e uma projeção de estado idempotente, capaz de lidar com tentativas repetidas, duplicados, eventos tardios e sequências fora de ordem.

A questão operacional: o que acontece se o callback chegar duas vezes, tarde ou noutra ordem?
Um callback de delivery receipt (DLR) não deve ser tratado como uma atualização única, ordenada e definitiva. Numa integração HTTP, uma comunicação pode falhar antes de o emissor obter a resposta e o pedido pode ser repetido. A RFC 9110 define idempotência como a propriedade pela qual múltiplos pedidos idênticos produzem o mesmo efeito pretendido que um único pedido.
A RFC 9110 não garante que todos os emissores de webhook repitam pedidos nem define a semântica específica dos callbacks de DLR. Quando a documentação do emissor estiver disponível, use-a para definir a política de repetição. Ainda assim, o recetor deve conseguir aceitar a mesma notificação mais de uma vez sem a contabilizar várias vezes nem alterar indevidamente o estado da mensagem.
Deve também conseguir registar um recibo que chegue depois de outro evento observado anteriormente, sem apagar a evidência nem assumir que a ordem de receção corresponde à ordem real de processamento na cadeia de mensagens. A regra operacional é simples: guarde cada receção, deduza o estado atual através de regras explícitas e mantenha estas duas coisas separadas. O histórico responde ao que foi recebido e quando; a projeção de estado responde qual é o resultado operacional que o sistema calcula com a evidência disponível.
- Não assuma a entrega porque um callback foi recebido pelo seu endpoint.
- Não suponha que o último callback recebido representa necessariamente o último facto ocorrido na rede.
- Não aplique uma atualização irreversível com base apenas no destino, conteúdo ou numa correspondência aproximada.
- Responda de forma segura a repetições: uma nova tentativa não deve gerar efeitos duplicados ao nível contabilístico, analítico ou operacional.

O que um DLR representa e o que não representa
Em SMPP, um DLR é solicitado em submit_sm através de registered_delivery. O recibo pode ser devolvido ao ESME por deliver_sm ou data_sm. Por isso, a disponibilidade de recibos depende de terem sido solicitados e da configuração ou comportamento da plataforma de mensagens.
O SMPP distingue um SMSC Delivery Receipt de uma notificação intermédia através da codificação de esm_class. As notificações intermédias são um tipo diferente e o respetivo suporte depende da implementação do SMSC; não devem ser interpretadas automaticamente como resultados finais.
Um DLR informa o estado comunicado pela cadeia de mensagens nesse fluxo. Não equivale a uma verificação independente de que uma pessoa viu, leu ou compreendeu o conteúdo no seu terminal. Na semântica SMPP, DELIVERED indica entrega no destino; não existe no padrão referido um estado de leitura humana ou de conteúdo visualizado.
- Diferencie a aceitação de submit_sm, que confirma a apresentação ao sistema que responde, de um DLR posterior.
- Armazene se o DLR foi solicitado e com que modalidade de registered_delivery, quando essa informação estiver disponível.
- Identifique o resultado como um estado comunicado pela mensageria, e não como prova de leitura pelo destinatário.
- Trate as notificações intermédias como eventos separados dos estados finais.

Modelo mínimo de dados para uma rastreabilidade útil
O design deve preservar os dados necessários para reconstruir uma decisão. Para cada mensagem enviada, mantenha um identificador interno imutável e o identificador externo devolvido pelo sistema de mensagens. Em SMPP, receipted_message_id identifica a mensagem objeto do recibo e corresponde semanticamente ao identificador opaco message_id devolvido na confirmação da apresentação original. O formato, a representação e a forma de conservação desse identificador podem variar por SMSC ou gateway; preserve o valor original e valide a implementação concreta.
Guarde o destino numa representação normalizada e mantenha também o contexto de endereçamento recebido ou enviado. A E.164 define o plano público internacional de numeração; a sua normalização ajuda a evitar variações de formato, mas não deve substituir os valores originais necessários para diagnóstico.
O formato histórico de DLR em short_message pode incluir identificador, datas de apresentação e conclusão, estado e erro. No entanto, as suas particularidades podem ser específicas da gateway ou do SMSC. Por isso, extraia campos normalizados para operar, mas conserve sempre a carga original.
- Mensagem: ID interno, ID externo ou message_id, origem, destino normalizado, valores originais de endereçamento, rota ou contexto de envio e data de apresentação.
- Evento recebido: ID de evento do fornecedor, se existir, data e hora de receção, data e hora comunicadas pelo emissor quando existirem, tipo de evento, estado bruto, código de erro bruto e payload original.
- Projeção: estado calculado, motivo da decisão, evento ou eventos que sustentam essa decisão e data de atualização.
- Auditoria: versão do parser ou das regras aplicadas, resultado da correlação e quaisquer exceções detetadas.
Correlação: dê prioridade a chaves estáveis e rejeite correspondências fracas
A correlação entre a mensagem original e o DLR deve basear-se primeiro no identificador externo atribuído pelo SMSC. O SMPP define receipted_message_id como o identificador da mensagem a que o recibo corresponde. Além disso, query_sm utiliza o message_id atribuído pelo SMSC juntamente com o endereço de origem como mecanismo de correspondência.
Não use destino, texto, janela temporal ou remetente como substitutos automáticos do ID externo. Estes atributos podem repetir-se entre mensagens legítimas e produzir uma atribuição incorreta. Uma correlação errada é mais prejudicial do que um evento pendente: pode transformar o estado de outra mensagem numa entrega ou falha que nunca lhe correspondeu.
Quando a correlação não for conclusiva, registe o callback sem o perder e encaminhe-o para uma fila ou registo de exceções. Esta decisão permite investigar alterações de formato, IDs truncados, variações de codificação ou outras particularidades de uma integração sem contaminar a projeção de mensagens conhecidas.
- Primeira opção: relacione receipted_message_id com o message_id armazenado ao aceitar o envio.
- Conserve o valor externo exatamente como foi recebido, além de qualquer forma normalizada exigida pela integração.
- Use origem, destino, datas e rota como validações auxiliares, e não como chave única de atribuição.
- Se houver mais de um candidato ou nenhum, marque a correlação como não conclusiva e não materialize o estado numa mensagem específica.
Padrão idempotente para receber e processar callbacks
A idempotência não exige ignorar todas as repetições. Permite conservar cada pedido recebido como evidência e, simultaneamente, impedir que a repetição altere várias vezes o resultado operacional. A RFC 9110 esclarece que um servidor pode registar cada pedido individualmente, ainda que o efeito pretendido da operação seja idempotente.
Implemente duas camadas. A primeira é um registo de receções, preferencialmente imutável, que guarde o payload, cabeçalhos relevantes disponíveis, momento de receção e resultado do parser. A segunda é a aplicação de efeitos: deduplicação, correlação e cálculo de estado. Apenas esta segunda camada deve estar protegida contra a aplicação repetida do mesmo evento lógico.
Se o emissor fornecer um ID de evento estável, utilize-o como chave de deduplicação no âmbito correto da integração. Se não existir, crie uma impressão digital a partir de atributos estáveis presentes no callback, conserve os componentes utilizados e mantenha o payload original. Não baseie a impressão digital em campos que possam mudar devido a transformação local ou em atributos ambíguos sem documentar o risco.
- 1. Receba o callback e persista a receção antes de executar efeitos de negócio.
- 2. Valide e extraia os campos disponíveis sem descartar o payload original.
- 3. Determine se existe um ID de evento estável; caso contrário, calcule uma impressão digital documentada para o evento lógico.
- 4. Insira ou detete o evento de forma atómica no registo de eventos lógicos.
- 5. Correlacione pelo ID externo e aplique as regras de transição apenas uma vez por evento lógico.
- 6. Devolva uma resposta HTTP coerente depois de persistir o resultado necessário para que uma nova tentativa seja segura.
Máquina de estados: torne explícitas as transições permitidas
Uma máquina de estados evita que a lógica dependa da ordem acidental de chegada. O guia de formato SMPP classifica ENROUTE como estado intermédio e DELIVERED, EXPIRED, DELETED e UNDELIVERABLE como estados finais. Também indica que uma mensagem em nova tentativa pode permanecer ENROUTE e terminar posteriormente em EXPIRED ou DELIVERED.
Represente os estados recebidos sem os reescrever e defina uma camada de estado operacional calculado. No modelo SMPP descrito, os estados finais não progridem para outros estados. Como política conservadora de design, um evento posterior que pareça contradizer um estado final já calculado não deve substituí-lo silenciosamente: deve ser conservado e abrir uma exceção de reconciliação ou investigação. Contratos ou extensões de fornecedor podem exigir regras de reconciliação específicas para a integração.
Não generalize os códigos de erro de uma plataforma para outra. Os códigos de rede ou SMSC podem ser específicos da gateway ou plataforma. Armazene-os como evidência original e crie classificações internas apenas quando as suas regras estiverem documentadas para a integração concreta.
- Estado bruto: o valor recebido, sem reinterpretação destrutiva.
- Estado normalizado: uma categoria interna documentada, se a integração permitir um mapeamento fiável.
- Estado calculado: resultado da aplicação de regras de precedência e transição sobre eventos correlacionados.
- Exceção: conflito entre eventos, evento final contraditório, falta de correlação ou formato não reconhecido.
- Regra conservadora de design: um estado final já materializado não deve converter-se noutro estado final apenas porque chega um callback posterior, salvo regra específica documentada para a integração.
Eventos tardios e fora de ordem: preserve a evidência e calcule com prudência
Guarde pelo menos dois tempos distintos: quando o seu sistema recebeu o callback e que hora o callback comunica, caso a comunique. Também pode ser necessário conservar as datas de apresentação e conclusão incluídas nos formatos de recibo. Estas marcas não são intercambiáveis: uma descreve a observação local e outra o dado comunicado pela plataforma de mensagens.
Para decidir o estado calculado, não ordene cegamente pelo momento de chegada. Aplique uma política documentada que considere o tipo de estado, o seu caráter intermédio ou final e a qualidade da correlação. Se não existir uma base fiável para ordenar dois eventos, não invente uma sequência: mantenha ambos e assinale o conflito.
O resultado operacional deve distinguir entre o histórico completo e o resumo atual. Um painel pode apresentar um estado calculado final e, ao mesmo tempo, indicar que ocorreram eventos repetidos, tardios ou conflituosos. Esta abordagem reduz a tentação de ocultar sinais que mais tarde se revelam essenciais para suporte, reconciliação ou análise de qualidade.
- Conserve a ordem de receção local, as horas comunicadas e o payload original.
- Não apague um evento duplicado: assinale-o como repetição do mesmo evento lógico quando aplicável.
- Não substitua um estado final por outro incompatível sem uma regra contratual ou técnica verificável e documentada para essa integração.
- Use uma fila de investigação para conflitos e mensagens não correlacionadas.
- Apresente na auditoria o evento que sustenta o estado calculado e os eventos que não puderam ser aplicados.
Lista de verificação para produção
Antes de ligar callbacks de DLR a métricas, faturação, alertas ou decisões de campanha, teste a integração como um sistema de eventos e não apenas como uma chamada HTTP bem-sucedida. A prioridade é garantir que uma nova tentativa, um payload inesperado ou um recibo sem correlação não se transformam numa afirmação errada de entrega.
A BulkSMSMarket descreve publicamente conectividade HTTP e SMPP. Antes de transformar campos ou recibos em automatizações críticas, confirme a semântica concreta, os formatos e as regras de DLR da contraparte.
- Teste o mesmo callback repetido e confirme que o estado calculado e os contadores não mudam devido à repetição.
- Teste um evento intermédio que chegue depois de um estado final e confirme que não degrada nem substitui o resultado final sem uma regra específica documentada.
- Teste dois estados finais incompatíveis e verifique que o segundo é preservado e assinalado para investigação.
- Teste callbacks sem ID de evento, com ID externo não encontrado e com formatos de data ou payload não reconhecidos.
- Confirme que mensagens não correlacionadas não são atribuídas automaticamente com base no destino ou conteúdo.
- Verifique que os relatórios distinguem aceitação, estado DLR comunicado e qualquer verificação independente que possa existir fora do DLR.
Perguntas frequentes
Um callback DLR duplicado significa que o SMS foi entregue duas vezes?
Não. Pode tratar-se da repetição de uma notificação. Deve ser conservado como uma receção observada, mas o efeito sobre o estado calculado e os contadores deve ser aplicado apenas uma vez ao evento lógico.
Posso correlacionar um DLR usando apenas o número de destino?
Não é recomendável. O destino pode repetir-se em várias mensagens. A base principal da correlação deve ser o ID externo atribuído à mensagem, como o message_id de SMPP relacionado com receipted_message_id. O destino serve como validação auxiliar.
DELIVERED confirma que o destinatário leu a mensagem?
Não. DELIVERED é um estado de entrega comunicado no fluxo de mensagens. Não constitui uma confirmação independente de leitura humana nem de visualização do conteúdo no terminal.
O que devo fazer se chegar um estado final depois de outro estado final diferente?
Conserve ambos os eventos e assinale o conflito para investigação. Como política conservadora de design, não substitua silenciosamente um estado final por outro incompatível apenas pela ordem de receção do callback. Aplique regras específicas quando estiverem documentadas para a integração.
É necessário guardar o payload original do DLR?
Sim. Os formatos e códigos podem variar entre plataformas. O payload original permite auditar o parser, rever campos não normalizados e investigar discrepâncias sem perder a evidência recebida.
Fontes consultadas
- SMPP Protocol Specification v3.4, Issue 1.2SMPP Developers Forum
- SMPP Delivery Receipt FormatSMPP Developers Forum
- RFC 9110: HTTP SemanticsIETF / RFC Editor
- Recommendation ITU-T E.164: The international public telecommunication numbering planInternational Telecommunication Union