Voltar ao blog Conectividade e operações SMS

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.

Diagrama de rastreabilidade entre um evento de negócio, tentativas técnicas, identificadores de fornecedor e DLR SMS

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.
O problema: um SMS pode acumular várias referências

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.
Que identificadores podem existir em HTTP, SMPP e DLR

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.
FAQ

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

  1. SMPP v3.4 specificationSMPP Developers Forum
  2. SMPP Delivery Receipt FormatSMPP Developers Forum
  3. SMPP protocol overviewSMPP Developers Forum
  4. Message resourceTwilio
  5. Outbound Message Status in Status CallbacksTwilio
  6. Best Practices for Messaging Delivery Status LoggingTwilio
  7. Operations and Message TrackingTwilio
  8. Delivery Reports - Get - REST APIMicrosoft Learn
  9. Azure Communication Services SMS eventsMicrosoft Learn
  10. SMS logsMicrosoft Learn
  11. ITU-T Recommendation E.164International Telecommunication Union
  12. RFC 9110: HTTP SemanticsIETF / RFC Editor