Identificadores de mensagens A2P SMS: como normalizar referências sem perder a rastreabilidade
Conceba um modelo de identificadores para separar o evento de negócio, a tentativa técnica e as referências externas de HTTP, SMPP, fornecedores e DLR. Evite substituições, colisões e associações incorretas durante alterações de rota ou estados tardios.

O problema: um SMS pode acumular várias referências
Um único envio lógico pode gerar vários identificadores ao longo do seu percurso operacional. A aplicação emissora pode criar uma referência própria; uma API HTTP pode devolver um ID de recurso; um SMSC ou MC pode devolver um message_id em submit_sm_resp; o fornecedor pode enviar uma referência diferente num DLR; e o sistema recetor do callback pode atribuir o seu próprio ID de evento.
Estas referências não são intercambiáveis. Um identificador devolvido por um fornecedor pertence normalmente ao âmbito dessa plataforma, conta, integração e ambiente. Não deve ser tratado como uma chave primária global de negócio, nem se deve assumir que será único entre fornecedores, rotas, contas ou ambientes.
O risco surge quando um sistema substitui um ID por outro, converte valores externos de forma irreversível ou liga eventos apenas por uma correspondência textual. O resultado pode ser atribuir um DLR antigo a um reenvio, misturar estados de dois fornecedores ou perder as evidências necessárias para investigar um incidente.
- O ID do evento de negócio identifica a intenção: por exemplo, um pedido de OTP ou uma notificação transacional autorizada.
- O ID da tentativa técnica identifica uma execução concreta de envio para uma determinada conta, fornecedor e rota.
- O ID externo identifica o recurso ou a mensagem dentro do sistema que o emitiu.
- O ID do evento de callback identifica a notificação recebida e não necessariamente a mensagem SMS a que se refere.

Que identificadores podem existir em HTTP, SMPP e DLR
O inventário exato depende do contrato de cada integração, mas é recomendável modelar categorias estáveis. O objetivo não é forçar uma nomenclatura universal, mas registar o que cada referência representa, quem a emitiu e em que contexto pode ser utilizada.
Em SMPP, submit_sm_resp devolve um message_id atribuído pelo MC ou SMSC. A especificação coloca-o no âmbito do sistema que aceita o submit e permite a sua utilização em operações posteriores, como consulta, substituição ou associação com um recibo. Por isso, deve ser preservado como uma referência externa da tentativa, e não como o identificador global da mensagem.
Um recibo SMPP pode chegar através de deliver_sm ou data_sm. Quando presente, o TLV receipted_message_id transporta a referência da mensagem original anteriormente devolvida pelo MC. Também podem existir dados de recibo noutros campos ou formatos definidos pela integração. Guarde a PDU ou a sua representação bruta, além da extração normalizada.
Em HTTP, uma resposta de criação ou aceitação pode devolver um identificador de mensagem ou recurso. A sua aceitação síncrona não comprova a entrega no terminal. Alterações posteriores podem chegar por callback, consulta do recurso ou relatório de entrega, com a sua própria referência de evento e as suas próprias marcas temporais.
- internal_event_id: ID imutável do evento lógico de negócio.
- send_attempt_id: ID imutável de cada tentativa técnica de emissão.
- client_reference: referência opcional fornecida pelo cliente ou sistema de origem.
- external_message_id: ID devolvido pelo fornecedor, MC, SMSC ou API.
- dlr_reference: referência transportada no relatório de entrega, como receipted_message_id quando aplicável.
- callback_event_id: ID da notificação recebida por webhook ou infraestrutura de eventos.
- provider_account_scope: conta, tenant, integração, ambiente e fornecedor que delimitam o significado de uma referência.

Princípio de conceção: ID interno imutável e referências externas versionadas
A base da conceção é simples: gere identificadores internos controlados pela sua organização e não os reutilize. Depois, trate cada referência externa como evidência atribuída a uma origem e a um momento de observação.
Separar o evento lógico da tentativa técnica é essencial. Um evento de negócio pode provocar uma tentativa inicial, uma nova tentativa controlada ou um failover para outra rota. Cada tentativa deve ter o seu próprio send_attempt_id, mesmo que todas dependam do mesmo internal_event_id. Assim, evita-se interpretar uma reemissão como uma atualização do envio anterior.
As referências externas também não devem ser substituídas. A mesma tentativa pode receber uma referência de aceitação, outra num DLR e uma referência adicional numa consulta posterior. Registe cada uma como uma linha ou evento independente, com o seu tipo, valor original, valor de comparação e contexto operacional.
A interpretação operacional pode evoluir. Por exemplo, um estado derivado pode passar de pendente para entregue ou não entregue quando chegam novas evidências. No entanto, o evento recebido e as evidências que motivaram essa interpretação devem permanecer intactos.
- Utilize UUID ou outro esquema interno estável para internal_event_id e send_attempt_id.
- Não utilize um message_id de fornecedor como chave primária do domínio de negócio.
- Mantenha uma relação um-para-muitos entre a tentativa técnica e as referências externas.
- Mantenha uma relação um-para-muitos entre a tentativa técnica e as observações de estado.
- Conserve a versão da integração que processou cada resposta ou callback.
- Distinga o estado observado do estado derivado utilizado na operação.
Normalização prática sem destruir o valor recebido
Normalizar não significa substituir o valor original. A regra segura é armazenar sempre a representação exata recebida e criar, separadamente, uma representação para comparação. Esta segunda representação serve apenas para pesquisas e regras de associação documentadas.
A comparação pode exigir a análise da codificação, comprimento, espaços, distinção entre maiúsculas e minúsculas, prefixos ou truncamento. Não aplique transformações universais: uma alteração de maiúsculas pode ser inofensiva para uma integração e destrutiva para outra; truncar uma cadeia pode criar uma colisão; converter bytes em texto sem conhecer a codificação pode alterar o identificador.
Cada normalização deve ser reproduzível. Guarde o nome da regra, a sua versão e o resultado. Se a integração alterar o formato de uma referência, poderá reexaminar os valores originais sem perder evidências.
- external_value_raw: valor exato recebido, preservado sem transformação.
- external_value_compare: valor derivado para comparação segundo uma regra explícita.
- normalization_rule_version: versão da regra aplicada.
- external_id_type: por exemplo, submit_sm_resp_message_id, receipted_message_id ou http_message_id.
- observed_at: momento em que o sistema recebeu ou observou o valor.
- source_payload_id: ligação à payload, PDU ou evento bruto armazenado de forma controlada.
- Não elimine espaços, zeros, prefixos nem caracteres não alfanuméricos sem uma regra específica do fornecedor.
Modelo de dados mínimo para investigar sem substituir evidências
Um modelo relacional mínimo pode resolver a maioria das investigações se preservar a separação entre intenção, execução, referências e observações. Não precisa de impor que todos os fornecedores devolvam os mesmos campos; precisa de registar explicitamente o que foi recebido e em que âmbito.
A tabela de eventos de negócio representa o pedido funcional autorizado. A tabela de tentativas representa cada envio técnico. As referências externas e os eventos de estado relacionam-se com a tentativa, e não diretamente com o evento lógico, salvo se o contrato do fornecedor permitir demonstrar essa relação.
Para minimizar a exposição, o destino deve ser tratado como dado sensível. Registe-o num formato internacional consistente quando necessário para investigação e chaves compostas, com controlos de acesso, retenção proporcional e, quando adequado, tokenização ou proteção equivalente. Não é necessário armazenar o conteúdo completo da mensagem para resolver todos os incidentes; um hash seguro da payload ou de uma representação canónica pode ajudar a distinguir tentativas sem aumentar desnecessariamente a exposição de dados.
- business_event: internal_event_id, tenant_id, tipo de 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: destino protegido ou tokenizado, hash seguro da payload, origem de envio e dados de auditoria necessários.
Quando o fornecedor reutiliza, transforma ou não devolve uma referência correlacionável
Nem todos os fornecedores preservam uma referência enviada pelo cliente, devolvem um ID estável ou incluem o mesmo ID nos DLR. O modelo deve admitir esta limitação sem inventar uma relação que não pode ser demonstrada.
Se um fornecedor reutilizar identificadores, a referência só pode ser única dentro de uma chave composta. No mínimo, inclua tenant, fornecedor, conta do fornecedor, ambiente, tipo de referência e um intervalo temporal de observação. Adicione rota e integração quando puderem alterar o significado operacional do valor.
Se o fornecedor transformar o identificador, registe ambos os valores e a regra conhecida de transformação. Se não existir uma regra contratual ou tecnicamente verificável, não estabeleça uma associação automática por semelhança parcial. Marque o caso como ambíguo e encaminhe-o para reconciliação ou investigação.
Quando não existir referência correlacionável, a rastreabilidade pode continuar até à tentativa técnica e à evidência de aceitação, mas a ligação a um DLR específico ficará incerta. Esse limite deve estar visível no dashboard e nos procedimentos operacionais.
- Nunca desduplique globalmente por external_message_id isolado.
- Não utilize correspondências por prefixo, sufixo ou truncamento como prova de identidade.
- Exija uma chave composta com âmbito operacional para cada regra de pesquisa.
- Classifique as ligações como confirmada, provável ou não correlacionável; reserve automatizações irreversíveis para ligações confirmadas.
- Documente que referências cada fornecedor devolve e quais podem surgir nos seus DLR.
submit_sm_resp, DLR e estados assíncronos: o que pode ser associado
Num fluxo SMPP habitual, o ESME envia submit_sm e recebe submit_sm_resp. O message_id da resposta identifica a mensagem no MC ou SMSC que respondeu. Se tiver sido solicitado um recibo através de registered_delivery e o sistema emitir um DLR, o recibo pode indicar que é um MC Delivery Receipt através de esm_class e transportar o identificador da mensagem recebida no TLV receipted_message_id.
Esta relação permite uma correlação forte quando o receipted_message_id corresponde ao message_id previamente registado, no mesmo fornecedor, conta, ambiente e integração. Ainda assim, conserve o DLR completo: o estado, as marcas temporais e os campos disponíveis fazem parte da evidência e podem ser necessários se existirem duplicados ou eventos fora de ordem.
Em HTTP, o identificador devolvido ao criar um recurso pode servir para consultar posteriormente o seu estado ou associar callbacks, conforme o contrato do fornecedor. Um código HTTP de criação ou aceitação indica que a plataforma processou ou colocou o pedido em fila de acordo com a sua semântica; não comprova por si só a entrega no terminal.
Os callbacks podem chegar tarde, repetidos ou fora de ordem. Não descarte automaticamente uma observação apenas por ser antiga em relação à hora de receção. Compare a marca temporal do fornecedor, a marca temporal de receção e a sequência conhecida; em seguida, aplique regras auditáveis de fecho e reconciliação.
- Persista submit_sm, submit_sm_resp e DLR como etapas separadas.
- Solicite DLR através de registered_delivery quando o contrato SMPP e o caso de utilização o exigirem.
- Não transforme um DLR em prova de leitura humana nem de qualidade geral da rota.
- Não assuma que um estado terminal impede a chegada posterior de evidências contraditórias ou duplicadas.
- Mantenha uma política documentada para decidir que estado derivado é apresentado, sem apagar estados anteriores.
Alterações de rota, reenvios e duplicados: modelar por contexto
Um failover, uma nova tentativa ou uma reemissão podem corresponder ao mesmo evento de negócio, mas não são necessariamente a mesma mensagem técnica. A regra prática é criar um novo send_attempt_id para cada emissão para uma combinação concreta de tenant, fornecedor, conta, rota, ambiente e integração.
Não consolide os DLR de rotas diferentes como atualizações da mesma tentativa. Um estado de uma rota anterior não deve ser atribuído a uma nova rota apenas porque o destino, o conteúdo ou uma referência externa parecem semelhantes. A relação correta é mantida através do internal_event_id, enquanto as evidências de cada fornecedor permanecem associadas à sua própria tentativa.
A idempotência deve ser aplicada antes do envio. Para pedidos HTTP, POST não é idempotente por definição; uma nova tentativa perante uma resposta incerta pode duplicar um envio se não existir uma chave de idempotência da aplicação ou uma confirmação fiável de que a operação anterior não foi aplicada. Não dependa de um ID externo que talvez ainda não tenha sido devolvido.
Um reenvio deliberado também deve ser visível como tal. Registe a causa: timeout de aceitação, falha técnica, política de failover, decisão manual ou outra razão autorizada. Isto permite distinguir uma duplicação acidental de uma segunda execução controlada.
- Chave de contexto recomendada: tenant_id, provider_id, provider_account_id, route_id, environment, external_id_type e external_value_compare.
- Adicione janelas temporais apenas como restrição adicional, não como prova única de identidade.
- Utilize idempotency_key por evento ou intenção de negócio antes de invocar o fornecedor.
- Registe retry_sequence, failover_reason e a relação entre tentativa de origem e tentativa sucessora.
- Evite enviar conteúdo sensível desnecessário para logs, ferramentas de pesquisa ou URLs.
Perguntas frequentes
O message_id de submit_sm_resp pode ser utilizado como ID global da mensagem?
Não. É uma referência atribuída pelo MC ou SMSC que respondeu e deve ser interpretada dentro do seu âmbito operacional. Guarde-a com fornecedor, conta, ambiente, integração, tipo de referência e momento de observação.
Um HTTP 202 ou uma resposta bem-sucedida da API confirma a entrega do SMS?
Não necessariamente. Uma aceitação ou criação confirma o tratamento do pedido segundo a API, mas a entrega requer observar um relatório de estado posterior ou consultar o recurso quando o fornecedor o permitir.
O que devo fazer se o DLR chegar antes, depois ou duplicado em relação a outros eventos?
Guarde cada observação sem a substituir. Registe a hora do fornecedor e a hora de receção, aplique uma regra de interpretação versionada e mantenha o evento bruto para reconciliação.
Devo guardar o conteúdo do SMS para correlacionar mensagens?
Não é indispensável em todos os casos. Dê prioridade à minimização de dados. Se precisar de distinguir tentativas, considere um hash seguro de uma representação controlada da payload e proteja os dados de destino e os metadados associados.
Um estado delivered comprova a receção ou leitura por uma pessoa?
Não. Representa a confirmação de entrega que o fornecedor recebe da sua cadeia a montante e, quando disponível, do terminal. Não é uma prova universal de leitura humana nem uma garantia independente sobre a qualidade da rota.
Fontes consultadas
- SMPP v3.4 specificationSMPP Developers Forum
- SMPP Delivery Receipt FormatSMPP Developers Forum
- SMPP protocol overviewSMPP Developers Forum
- Message resourceTwilio
- Outbound Message Status in Status CallbacksTwilio
- Best Practices for Messaging Delivery Status LoggingTwilio
- Operations and Message TrackingTwilio
- Delivery Reports - Get - REST APIMicrosoft Learn
- Azure Communication Services SMS eventsMicrosoft Learn
- SMS logsMicrosoft Learn
- ITU-T Recommendation E.164International Telecommunication Union
- RFC 9110: HTTP SemanticsIETF / RFC Editor