Rodei jq no log de 4.317 atendimentos: nenhum dos três relógios que o SAC vai cobrar está lá
O TI Inside publicou hoje que as novas regras do SAC entram em vigor em 2 de novembro. O Decreto 13.135/2026 alcança empresas de setores regulados pelo governo federal e fornecedores de serviço financeiro, de pagamento e de crédito sob regulação do Banco Central, e trata de tempo de espera, acesso ao atendimento humano, uso de chatbot e IA e cancelamento de contrato.
A parte que interessa aqui não é jurídica. Três dessas exigências só existem se o sistema registrar o instante em que algo aconteceu. Peguei o log de um canal de atendimento que eu mantenho, 11 dias corridos, 4.317 conversas, e fui tentar responder três perguntas simples sobre esse período. Não consegui responder nenhuma com os campos que estavam lá.
O log que eu tinha
$ jq -r '.[0] | keys[]' conversas.json | tr '\n' ' '
agent_id bot_turns channel closed_at created_at customer_id
first_agent_reply_at handoff last_message_at rating status subject tags
$ jq '[.[] | select(.handoff)] | length' conversas.json
1613
Treze campos, e uns 37% das conversas passaram do bot para uma pessoa. O handoff é booleano. Ele diz que houve transferência. Não diz quando, nem por que, nem quem pediu.
Relógio 1: tempo de espera
O relógio do tempo de espera começa quando o consumidor pede atendimento e para quando alguém o atende. No meu log, o que existe é created_at (abertura da conversa) e first_agent_reply_at (primeira resposta humana). A diferença entre os dois carrega, dentro dela, todos os turnos de bot que vieram antes.
$ jq '[.[] | select(.handoff) | (.first_agent_reply_at|fromdate) - (.created_at|fromdate)] \
| sort | .[length/2|floor]' conversas.json
252
$ jq '[.[] | select(.handoff) | (.first_agent_reply_at|fromdate) - (.created_at|fromdate)] \
| sort | .[(length*0.95|floor)]' conversas.json
1143
Mediana de 4min12 e p95 de 19min03. Os dois números estão errados para o que o decreto pede, e errados para cima: quem conversou seis turnos com o bot antes de pedir gente aparece como se tivesse esperado o tempo todo na fila. Se eu precisasse defender esse número numa fiscalização, eu não teria como separar fila de conversa com robô. Falta um evento no meio.
Relógio 2: a saída do bot para o humano
Aqui o campo mente por omissão. Com handoff: true eu sei que a conversa trocou de mão, e paro aí. O decreto trata de acesso ao atendimento humano, e para mostrar que o acesso existe eu preciso saber se o cliente pediu uma pessoa e em quanto tempo ele conseguiu.
O log do bot registrava intent_not_recognized quando não entendia, e nada quando o cliente escrevia "quero falar com atendente". Rodei a busca ingênua no texto das mensagens e achei perto de 390 conversas com variação dessa frase, sendo que 71 delas terminaram sem nenhum first_agent_reply_at. Não é prova de nada, é string matching. Mas é o sintoma: a intenção mais sensível do fluxo era a única sem evento próprio.
Relógio 3: cancelamento no mesmo canal da contratação
Esse é o pior, porque não é falta de campo, é falta de chave. O pedido de cancelamento nasce no atendimento. A contratação nasceu no checkout, no app ou na mesa do vendedor, meses antes, em outro banco de dados. Para dizer "o cliente cancelou no mesmo canal em que contratou" eu preciso de um campo que guarde o canal de aquisição junto do contrato e viaje até o ticket.
$ jq '[.[] | select(.subject | test("cancel";"i")) | .customer_id] | length' conversas.json
208
$ jq '[.[] | select(.subject | test("cancel";"i")) | .contract_id] | map(select(.!=null)) | length' conversas.json
jq: error: Cannot index object with "contract_id"
208 conversas sobre cancelamento, zero amarradas a um contrato. O relatório que a diretoria olhava mostrava "tempo médio de resolução de cancelamento: 1,8 dia". Esse número existia, e não provava o que o decreto cobra.
O evento mínimo
O que eu passei a emitir tem quatro eventos, todos com protocolo e horário com fuso explícito. O primeiro é o que faltava para o relógio 1 existir:
{
"event": "sac.queue.entered",
"occurred_at": "2026-11-02T14:08:31.412-03:00",
"protocol": "2026110200031",
"customer_id": "c_8f21",
"channel": "whatsapp",
"requested_human": true,
"requested_by": "customer",
"bot_turns": 4,
"bot_exit_reason": "customer_request"
}
{
"event": "sac.queue.answered",
"occurred_at": "2026-11-02T14:09:19.882-03:00",
"protocol": "2026110200031",
"waited_ms": 48470,
"agent_id": "a_204",
"queue": "financeiro"
}
{
"event": "sac.cancellation.requested",
"occurred_at": "2026-11-02T14:14:02.019-03:00",
"protocol": "2026110200031",
"contract_id": "ct_1182",
"acquired_channel": "web_checkout",
"requested_channel": "whatsapp",
"same_channel_available": true,
"effective_at": "2026-11-02T14:14:02.019-03:00",
"receipt_sent_at": "2026-11-02T14:14:07.551-03:00"
}
O acquired_channel é o campo chato de nascer, porque ele não é do time de atendimento. Ele é do checkout, e alguém precisa carregá-lo até o contrato. Foi a única mudança dessa lista que exigiu migração de dados.
Com waited_ms no evento, a conta vira uma linha e não mistura mais bot com fila:
$ jq '[.[] | select(.event=="sac.queue.answered") | .waited_ms] \
| sort | (.[length/2|floor]/1000), (.[(length*0.95|floor)]/1000)' eventos.json
39.112
204.88
A parte que não é código
Um endpoint que devolve 200 e um painel com média mensal continuam sendo o jeito mais fácil de parecer medido. A diferença entre parecer e estar aparece no dia em que alguém pede o horário de um atendimento específico, com protocolo, e você precisa achar o registro.
Na montagem de estrutura metálica tem uma coisa parecida: o serviço feito e não anotado no diário de obra não é medido, e o que não é medido não entra no boletim. Ninguém discute se a solda ficou boa. Discute se existe registro dela. Log de atendimento virou isso em 2 de novembro, e as duas perguntas práticas que sobram são por quanto tempo você guarda o evento e se o relógio dele usa fuso ou usa UTC sem informar.
Três coisas que eu faria antes de segunda, se o seu produto estiver na lista de setores alcançados: separar o evento de entrada na fila do de abertura da conversa, dar evento próprio ao pedido de atendente dentro do bot, e carregar o canal de aquisição até o contrato.
Quem aqui já tem a entrada na fila separada da abertura da conversa no log? E como vocês amarram o canal de contratação ao de cancelamento quando checkout e atendimento vivem em sistemas diferentes, sem reescrever o cadastro inteiro?
Fonte: notícia do TI Inside sobre a vigência do Decreto 13.135/2026, publicada em 29/10/2026. Sobre medir o que importa em vez do que é fácil de contar, escrevi antes aqui: https://revin.com.br/pt/blog/mais-codigo-menos-produto-ia-o-que-medir