Criar conta grátis
Todos os textos

A resposta da operadora é sua, não nossa

Quando um envio falha, a operadora devolve um motivo. Guardar esse motivo e entregar só a etiqueta transforma diagnóstico em chamado de suporte — e transfere para o seu time um trabalho que já estava feito. Este texto é sobre por que a plataforma faz isso, por que a gente parou de fazer, e o que muda no plantão.

Engenharia CCXPlataforma · Engenharia · 28 ago 2026 · 8 min de leitura
Duas placas iguais sob a mesma luz: uma lisa, a outra gravada de ponta a ponta. O mesmo evento, com e sem a evidência.
Duas placas iguais sob a mesma luz: uma lisa, a outra gravada de ponta a ponta. O mesmo evento, com e sem a evidência. Ilustração.

O que você leva

  • O erro normalizado é contrato, a resposta crua é evidência, e os dois cabem no mesmo payload. Escolher um dos dois é que era o erro.
  • Número inválido, bloqueio da operadora e aparelho sem sinal são três problemas com três respostas diferentes. Numa etiqueta só, os três viram a mesma linha de log.
  • Normalizar não é preguiça: é o que permite trocar de provedor sem reescrever a sua lógica. O que era preguiça era descartar o original depois de normalizar.
  • Campo novo não quebra integração, mas quebra parser configurado para recusar chave desconhecida. É por isso que existe prazo, e por isso o prazo é anunciado antes.

O evento que não explica nada

Toda plataforma de mensagem recebe duas coisas quando um envio falha. A primeira é a resposta crua do provedor de canal, com o código e o texto que ele emitiu. A segunda é a classificação que a plataforma faz dessa resposta, para caber num vocabulário estável. Quase todas entregam a segunda e ficam com a primeira.

O efeito aparece no plantão. O evento diz que falhou e para por aí. Não dá para distinguir número que não existe de bloqueio da operadora de aparelho sem sinal desde ontem. São três problemas com três respostas diferentes — o primeiro pede correção de cadastro, o segundo pede conversa com quem cuida da rota, o terceiro pede só esperar — e nenhum deles aparece no payload.

Então o time abre chamado. Do outro lado, alguém consulta um log e devolve por escrito, horas depois, um dado que já estava no sistema no instante exato da falha. O trabalho não foi feito nesse intervalo: ele foi transferido. E foi transferido para quem tem menos contexto sobre a operação do cliente do que o próprio cliente.

O detalhe que torna isso incômodo é que a informação nunca esteve perdida. Ela chegou, foi lida, foi usada para decidir a classificação, e foi descartada logo depois. Não é um caso de dado que ninguém tinha; é um caso de dado que alguém teve por um instante e não passou adiante.

Por que a plataforma normaliza, e por que isso não é preguiça

Vale defender a normalização antes de criticar o que se faz com ela, porque ela existe por um motivo bom. Cada provedor de canal tem o próprio dicionário de erros, com códigos próprios, agrupamentos próprios e mensagens em idiomas diferentes. Escrever lógica de negócio direto em cima disso significa reescrever essa lógica toda vez que a rota muda — e a rota muda por motivo comercial, por qualidade, por disponibilidade, muitas vezes sem que quem integra fique sabendo.

A classificação resolve isso. Ela dá um vocabulário que não depende de quem entregou a mensagem, e é sobre esse vocabulário que a retentativa, o alerta e o relatório devem ser escritos. Uma integração acoplada ao campo normalizado sobrevive a uma troca de provedor sem uma linha alterada, e essa é uma promessa que vale muito mais do que parece no dia em que precisa ser cumprida.

O que era preguiça, então, não é normalizar: é descartar o original depois de normalizar. As duas coisas resolvem problemas diferentes e não competem por espaço. A classificação existe para a máquina decidir; o original existe para a pessoa entender. Entregar só a primeira é assumir que ninguém nunca vai precisar da segunda — e a existência da fila de chamados prova que essa suposição está errada.

Dois campos, dois trabalhos

Existe um argumento honesto contra expor o código do provedor, e ele merece ser levado a sério. Expor acopla quem integra a um fornecedor que essa pessoa não escolheu, e que pode ser trocado sem aviso. Um código que hoje significa bloqueio pode não existir no provedor seguinte, e uma lógica escrita em cima dele quebra na migração — silenciosamente, o que é pior.

O argumento está certo, e é exatamente por isso que a resposta é dois campos e não um. O campo normalizado carrega a classe do erro e não muda de significado nunca. O campo cru carrega provedor, código e mensagem como vieram. Um serve para a aplicação decidir, o outro para uma pessoa entender. Nomear os dois separadamente é o que impede que se confundam.

A separação é o que torna a troca de provedor um evento sem drama. A evidência muda, porque ela é do provedor. O contrato fica, porque ele é nosso. Quem escreveu a lógica em cima do campo normalizado não precisa reescrever nada, e quem estava lendo o campo cru no plantão vai ver códigos diferentes — o que é correto, porque quem respondeu de fato é outro.

Há uma responsabilidade que vem junto e vale dizer em voz alta: quem lê o campo cru precisa saber que ele não é contrato. Está escrito na documentação e está escrito na lista acima, mas repetir aqui não custa nada. Se você escrever uma condicional sobre o código do provedor, ele vai funcionar por meses e falhar no dia da migração, e o defeito vai parecer nosso.

O que muda no plantão

O ganho não é filosófico e aparece rápido. Com o provedor e o código originais no mesmo evento, a primeira pessoa que olha já separa o que é problema do cadastro do que é problema da rota. Cadastro se resolve internamente e agora; rota vira conversa com quem opera. São caminhos diferentes, e escolher o caminho errado custa o tempo inteiro do primeiro turno.

Muda também o que acontece quando o chamado é mesmo necessário. Em vez de abrir com "as mensagens estão falhando", abre com o provedor, o código, a mensagem original e o horário. Quem recebe do outro lado não precisa pedir informação de volta antes de começar a olhar, e a primeira resposta deixa de ser uma pergunta.

E muda o que dá para automatizar. Um alerta que dispara em cima do campo normalizado é estável, mas grosso: ele avisa que a classe de erro subiu. Com o campo cru no mesmo evento, o mesmo alerta pode carregar no corpo qual código específico subiu — sem que ninguém escreva lógica em cima dele. Contexto na notificação e acoplamento na lógica são coisas diferentes, e só a segunda é perigosa.

Por que campo novo ainda precisa de prazo

A promessa publicada é que campo novo pode aparecer e campo existente não muda de tipo nem de significado. Ela funciona porque quem consome tolera chave desconhecida. E nem todo mundo tolera: um webhook que valida payload contra schema estrito rejeita o evento inteiro por causa de um campo que ele nem queria ler.

Isso não é um erro de quem integrou. Validação estrita é uma escolha defensável, e em alguns setores ela é exigida. O que ela significa, na prática, é que "adicionar campo é retrocompatível" é verdade do ponto de vista do formato e não é verdade do ponto de vista de todo consumidor real — e a diferença entre essas duas coisas é onde as integrações quebram.

Por isso o campo foi anunciado em 18 de julho para valer em 18 de agosto, no changelog e por e-mail, com a ação necessária escrita na própria entrada. Um mês não é cortesia. É o reconhecimento de que a compatibilidade é um contrato de dois lados, e que o lado do cliente às vezes precisa mexer em código antes de a mudança chegar.

A regra vale igual na direção contrária. Nada é removido sem seis meses de aviso, com substituto documentado desde o primeiro dia e com as duas rotas funcionando lado a lado durante todo o período. Depreciação anunciada sem coexistência real não é depreciação: é remoção com aviso prévio, e a diferença entre as duas é sentida por quem tem de reescrever integração com prazo.

O princípio, para quando o caso for outro

A regra que sai daqui é mais geral que este campo. Quando a plataforma processa um dado de terceiro para produzir uma abstração, ela fica com duas coisas nas mãos, e a abstração é a menos informativa das duas. Entregar só a abstração é confortável para quem a construiu e caro para quem depende dela num momento ruim.

O teste é simples e serve para qualquer campo: se alguém do seu time precisou abrir um chamado para descobrir algo que a plataforma já sabia no instante do evento, esse algo devia estar no evento. Não como contrato, não como promessa de estabilidade — como evidência, nomeada de forma que ninguém confunda uma coisa com a outra.

É por isso que este é um post sobre um campo e não sobre uma funcionalidade. O campo é pequeno. O que ele diz sobre quem constrói a plataforma é que o dado do cliente é do cliente, inclusive quando é feio, inclusive quando vem em outro idioma, e inclusive quando entregá-lo significa admitir que a rota falhou por um motivo que a gente preferiria não mostrar.

Como ler um evento de falha

01statusO que aconteceu, em uma palavra. É o que dispara o seu fluxo de retentativa, e é o único campo que a maioria das integrações lê no primeiro dia.
02error.codeA classe do erro no nosso vocabulário. Estável entre provedores, e é sobre ele que a sua lógica deve ser escrita. Se você só puder acoplar a um campo, acople a este.
03channel_response.providerQuem respondeu. Sem ele, o código do item seguinte não tem contexto para ser interpretado: o mesmo número significa coisas diferentes em provedores diferentes.
04channel_response.code e .messageO que o provedor respondeu, cru. É o que a pessoa de plantão lê, e é o que você cola no chamado quando o chamado for mesmo necessário.
05O que NÃO fazer com o campo cruEscrever lógica de negócio em cima dele. Ele é evidência para gente ler, não contrato para máquina obedecer — e ele muda quando o provedor muda, que é exatamente o ponto.
Ver a referência de webhooks

Quer ver isso funcionando no seu número?

Criando a conta pelo site, você ganha R$ 10 de crédito de teste.