Cobrança falhou: como usar o Jev para decidir entre retentar, trocar o método ou avisar o cliente?

fluxograma de decisão do Jev dunning para cobrança falhada com opções de retentar, trocar método ou avisar cliente
Resposta rápida

No dunning com Jev, a falha de cobrança deixa de ser uma régua de dias e vira uma decisão tipada: você monta um state JSON (conversation, record, policy) com attempt_count, código de recusa e histórico do cliente, e faz uma pergunta Choice com um conjunto fechado de ações (retentar, agendar, pedir novo método, avisar, escalar). O Jev, primeiro modelo público da classe System One da TypeSafe AI, devolve a opção escolhida, a distribuição de probabilidades e um confidence de 0 a 1, e o seu código executa. O preço listado do jev-1.13 é US$ 0,042 por milhão de tokens de entrada, com saída gratuita

Fala aí, beleza? A fatura recusou

O webhook invoice.payment_failed disparou, o attempt_count subiu pra 3 e agora o seu sistema tem que decidir alguma coisa: retenta agora, pede outro cartão, manda um e-mail ou passa pro humano?

A maioria dos SaaS resolve isso com régua fixa: retenta no dia 1, no dia 3, no dia 7 e desiste

Funciona. Até o dia em que tu percebe que a régua não olha o MOTIVO da recusa, não olha o histórico do cliente e não olha aquele ticket em que ele já avisou que ia trocar de banco

O Jev, lançado em early access pela TypeSafe AI, propõe um caminho diferente pra esse tipo de bifurcação: em vez de gerar texto, ele recebe um estado e perguntas tipadas e devolve valores tipados com probabilidades e confiança, feitos pra serem consumidos por código

É o primeiro modelo público da classe System One da casa (nome vindo do conceito de pensamento rápido e intuitivo, o System 1 popularizado por Daniel Kahneman em "Thinking, Fast and Slow")

Traduzindo pro nosso problema: dunning deixa de ser cronograma e passa a ser decisão

Bora ver na prática?

Régua fixa, Smart Retries e decisão tipada: onde cada uma perde dinheiro

Antes do código, vale entender o que cada abordagem realmente resolve. Elas não competem no mesmo campo, e é exatamente aí que o dinheiro escapa

Abordagem O que decide O que ignora Onde perde dinheiro
Régua customizada do Stripe Quantos dias esperar entre tentativas (até três retentativas, cada uma com um número de dias após a anterior) Motivo da recusa, histórico de pagamentos e qualquer sinal vindo do suporte Em hard decline a fatura não pode ser retentada sem um novo método de pagamento: as retentativas seguem agendadas e o attempt_count continua subindo, mas só executam depois que um novo método é detectado
Smart Retries Os melhores momentos de retentar, usando IA; a doc do Stripe aponta que são mais eficazes que retentativas em régua fixa agendada, com janelas de 1 semana, 2 semanas, 3 semanas, 1 mês ou 2 meses e teto recomendado de oito retentativas Qual AÇÃO tomar quando retentar não é a ação certa Otimiza o QUANDO, não o O QUÊ: quem precisa de cartão novo ou de um aviso humano continua rodando na fila de retentativa
Decisão tipada com Jev A ação, dentro de um conjunto fechado, lendo motivo da recusa, histórico e conversa como state Agendamento e execução da cobrança, que seguem no gateway A calibração é medida em grupos de previsões e não garante que uma resposta individual esteja correta, então o roteamento precisa de fallback
Domine o Jev e coloque decisões de IA dentro do seu sistema
Pré-inscrição Curso Jev

Domine o Jev e coloque decisões de IA dentro do seu sistema

Você vai aprender a usar o Jev, o System One Model da TypeSafe AI, pra automatizar decisões com resposta tipada e confiança medida, sem depender de chat nem de alguém revisando cada passo. Entre na lista de espera para garantir a condição de lançamento!

Se liga no detalhe que mais dói na régua rígida: boa parte das recusas do Stripe cai em categorias genéricas, e a mais comum é do_not_honor

Esse código não te diz absolutamente nada sobre intenção do cliente. Pode ser limite estourado, pode ser antifraude do emissor, pode ser cartão que ele já abandonou

A régua trata os três igual. O modelo tipado trata diferente porque tu ENTREGA o resto do contexto pra ele

O que você precisa antes de começar

Do lado da TypeSafe:

  • Conta com API key exportada em TYPESAFE_API_KEY (o client lê essa variável de ambiente e chama jev-latest por padrão)
  • SDK Python instalado com pip install typesafe-sdk ou uv add typesafe-sdk, exigindo Python 3.10 ou superior
  • Ou o SDK JavaScript, com npm install @typesafe-ai/sdk
  • Ou nada de SDK: HTTP puro em POST https://api.typesafe.ai/v1/systemone, com header Authorization: Bearer <API_KEY> e Content-Type: application/json
  • Se tu já tem tudo plugado na OpenRouter, o modelo typesafe/jev-1.13 também está lá, ao mesmo preço de US$ 0,042 por milhão de tokens de entrada, com saída gratuita

Do lado do billing:

  • O webhook invoice.payment_failed já chegando no seu backend (é ele que serve pra monitorar falhas de pagamento de assinatura e as atualizações das tentativas de retentativa)
  • As suas configurações atuais de retentativa localizadas no Dashboard do Stripe, em Billing > Revenue recovery > Retries

E um número que vai guiar toda a modelagem: o orçamento de contexto

O Jev ingere o estado uma vez e avalia todas as perguntas contra ele em paralelo. O orçamento de 64k cobre o estado mais todas as perguntas somadas, e o de 32k vale pro estado mais a pergunta mais longa

Guarda isso, porque o erro número um aqui é achar que "mais contexto é sempre melhor" e despejar o objeto inteiro do gateway dentro do state

Passo a passo: modelando o dunning como decisão tipada

1. Montar o state com as três partes

O estado enviado ao modelo costuma ser um objeto JSON com partes como uma conversa, um registro e uma política. No dunning isso cai redondo:

  • conversation: trocas anteriores de cobrança e suporte
  • record: attempt_count, código de recusa, valor da fatura, tempo de casa, histórico de pagas e falhas
  • policy: as regras do SEU negócio
{
  "conversation": [
    {"role": "customer", "text": "troquei de banco, vou atualizar o cartao essa semana"},
    {"role": "support", "text": "beleza, deixo a fatura aberta ate sexta"}
  ],
  "record": {
    "invoice_id": "in_1Q...",
    "amount_cents": 24900,
    "currency": "brl",
    "attempt_count": 3,
    "decline_code": "do_not_honor",
    "hard_decline": false,
    "customer_since_days": 612,
    "paid_invoices": 19,
    "failed_invoices": 2,
    "plan_interval": "month"
  },
  "policy": {
    "max_retries": 8,
    "email_cooldown_hours": 48,
    "escalate_above_cents": 100000
  }
}

Repara no max_retries: 8: o Stripe recomenda no máximo oito retentativas pra cobranças que permitem retentativa, então a política vira parte do estado em vez de virar if espalhado pelo código

O erro comum deste passo: jogar o payload bruto do evento inteiro no state. Ele vem com metadados que não decidem nada e come o teu orçamento de contexto rapidinho. State é resumo de decisão, não log

2. Definir o Choice com o conjunto fechado de ações

Agora a parte boa. A TypeSafe expõe três tipos de pergunta (primitivas): Choice, Score e Noul

Choice escolhe uma opção dentro de um conjunto definido e aceita no máximo 255 opções por pergunta. Pra dunning tu não precisa nem de 10:

{
  "type": "choice",
  "name": "next_action",
  "prompt": "Qual a proxima acao de cobranca para esta fatura?",
  "options": [
    "retentar_agora",
    "agendar_retentativa",
    "solicitar_novo_metodo",
    "avisar_cliente",
    "escalar_humano"
  ]
}

Esse é o coração do troço: saída pequena, fechada e mapeável direto pra função no teu código. É o mesmo desenho que aparece em moderação de conteúdo com Jev, onde o conjunto de ações possíveis também é curto e cada opção tem um handler do outro lado

O erro comum deste passo: criar opções que se sobrepõem. Se retentar_agora e agendar_retentativa significam quase a mesma coisa pra quem lê o prompt, a probabilidade se espalha entre as duas e a confiança despenca. Opção boa é opção que exclui as outras

3. Somar Noul e Score onde eles cabem

Noul é uma pergunta de sim ou não que devolve a probabilidade de a resposta ser sim, em um valor de 0 a 1

Score devolve a posição em uma régua de níveis descrita em degraus. A própria documentação orienta: Noul pra sim ou não, Score quando a resposta é posição em um espectro (tipo gravidade de um bug ou satisfação do cliente)

{
  "questions": [
    {
      "type": "noul",
      "name": "intends_to_stay",
      "prompt": "Este cliente tem intencao de continuar assinando?"
    },
    {
      "type": "score",
      "name": "urgencia_contato",
      "prompt": "Qual a urgencia de falar com este cliente agora?",
      "levels": [
        "0: nenhuma, o gateway resolve sozinho",
        "1: baixa, cabe um e-mail automatico",
        "2: media, e-mail hoje com link de atualizacao",
        "3: alta, contato ativo do time",
        "4: critica, risco de churn imediato"
      ]
    }
  ]
}

O erro comum deste passo: usar Noul pra algo que é espectro. "O cliente está insatisfeito?" parece sim ou não, mas na prática é degrau. Vira Score e tu ganha granularidade de graça

4. Mandar as três perguntas na mesma chamada

Os três tipos podem ser misturados em uma única chamada de API. E tem um detalhe de arquitetura importante: cada pergunta é avaliada em paralelo e isoladamente contra o mesmo estado

O resultado de uma primitiva não vira contexto oculto que altera o resultado de outra

curl -X POST https://api.typesafe.ai/v1/systemone \
  -H "Authorization: Bearer $TYPESAFE_API_KEY" \
  -H "Content-Type: application/json" \
  -d @payload.json

O payload.json carrega o state do passo 1 mais as perguntas dos passos 2 e 3 (o formato exato do corpo tu confere na doc da API; o que importa aqui é a modelagem)

O erro comum deste passo: escrever o prompt do Score contando com o que o Choice "já decidiu". Não existe isso. Cada pergunta precisa se sustentar sozinha contra o state

5. Ler a resposta no código

Cada primitiva devolve um formato diferente, e todos eles são feitos pra if:

  • Choice: a opção selecionada, um valor de confiança e a distribuição de probabilidade entre as opções
  • Score: o valor do score, a confiança, uma legend que mapeia cada nível à sua descrição e as probabilities de cada nível, que somam 1
  • Noul: a probabilidade de "sim", entre 0 e 1

E como sai o score? Ele é a posição na régua de níveis, de 0 até o nível mais alto, calculada multiplicando cada número de nível pela sua probabilidade e somando

Ou seja: urgencia_contato = 2.7 não quer dizer "nível 2 e meio inventado", quer dizer que a massa de probabilidade está entre o degrau 2 e o 3. Isso é informação, não arredondamento

6. Escrever o roteamento determinístico

Aqui o modelo sai de cena e o teu código assume. Confidence é um número de 0 a 1 calculado a partir da dispersão das probabilidades: um pico único em um nível significa alta confiança, e o código pode usar esse valor pra decidir se e como agir sobre a resposta

# nomes dos campos seguem a doc da primitiva que voce estiver usando
CORTE = 0.7

def rotear(resp, hard_decline: bool, attempt_count: int):
    # guard-rail primeiro, modelo depois
    if hard_decline:
        return "solicitar_novo_metodo"

    if attempt_count >= 8:
        return "escalar_humano"

    acao = resp["next_action"]["selected"]
    conf = resp["next_action"]["confidence"]
    urgencia = resp["urgencia_contato"]["score"]
    quer_ficar = resp["intends_to_stay"]["probability"]

    if conf < CORTE:
        return "agendar_retentativa"  # fallback: sua regua de sempre

    if acao == "avisar_cliente" and urgencia >= 3 and quer_ficar > 0.6:
        return "escalar_humano"

    return acao

O erro comum deste passo: tratar confidence alta como garantia de acerto. A calibração é medida em grupos de previsões e não garante que uma resposta individual esteja correta. Confidence alta é sinal pra automatizar; não é promessa

7. Fechar o loop com o guard-rail de hard decline

Esse eu repeti de propósito no código do passo 6, porque é o único ponto onde a regra do gateway ganha do modelo, sempre

Se a falha retorna um código de hard decline, a cobrança da fatura não pode ser retentada sem um novo método de pagamento. As retentativas continuam agendadas e o attempt_count continua subindo, mas só executam depois que um novo método é detectado

Então pouco importa se o Jev sugeriu retentar_agora com confidence 0.95: hard decline vira solicitar_novo_metodo e ponto

Modelo decide onde existe ambiguidade. Onde a regra é determinística, o if manda 😀

Quatro situações em que a decisão tipada muda o resultado

1. O mesmo do_not_honor em dois clientes diferentes

Cliente de dois anos, 19 faturas pagas, duas falhas. E trial recém-convertido que nunca pagou nada. Código de recusa idêntico, situação oposta

A régua fixa dispara exatamente a mesma sequência pros dois. Aqui quem carrega a decisão é o Choice, porque o record com tempo de casa e histórico entra no state e muda a distribuição. É a mesma mecânica de qualificar leads no funil, só que olhando pra trás em vez de pra frente

2. Hard decline no meio de uma régua já agendada

A régua vai queimar os dias dela esperando execução que não acontece, enquanto o attempt_count sobe

Esse caso nem precisa de modelo: precisa do guard-rail do passo 7 rodando ANTES da régua. O ganho aqui é arquitetural, não estatístico

3. Assinatura anual de ticket alto

Quando a fatura é gorda, um e-mail automático malcalibrado custa mais caro que dez retentativas. É o cenário clássico de Score: urgência do contato em degraus, não booleano

Se o score encosta no topo da régua, tu rota pro humano em vez de pro template

4. O cliente que já avisou no suporte

"Troquei de banco, atualizo essa semana"

Esse sinal vive na conversation e nenhuma régua por dias enxerga ele. Aqui o Noul de intenção de continuar assinando é quem segura a mão do sistema: em vez de disparar aviso de cancelamento, agenda e espera

E o custo? O preço listado do jev-1.13 é US$ 0,042 por milhão de tokens de entrada, com tokens de saída gratuitos

Um state de dunning enxuto é pequeno. Compara isso com o valor de UMA fatura recuperada e tu entende por que vale rodar a pergunta antes de cada ação em vez de confiar na régua no piloto automático

Vídeo: integrando ferramentas de IA no seu fluxo com Claude Code

Pra começar do zero na parte de encaixar IA no fluxo de trabalho, este vídeo do canal mostra o método Karpathy com Claude Code e Obsidian:

E se tu quer montar esse fluxo de decisão tipada dentro do próprio agente, a TypeSafe tem skill oficial

No Claude Code:

claude plugin marketplace add typesafe-ai/skills
claude plugin install typesafe@typesafe-ai

Depois de instalada, tu invoca com /typesafe:typesafe-ai

Em outros agentes:

npx skills add typesafe-ai/skills --skill typesafe-ai

A instalação é local ao projeto por padrão. Se tu quer global, usa -g

Próximo passo: comece com uma pergunta, não com o fluxo inteiro

A ideia central é curta: o Jev não substitui o teu motor de cobrança

Quem agenda, quem executa a retentativa e quem emite a fatura continua sendo o gateway. O que muda é que a ESCOLHA da ação sai do if engessado e vira um valor tipado, com probabilidade e confiança, que o teu código consome

O próximo passo prático não é ligar isso em produção. É pegar os últimos invoice.payment_failed que tu já tem registrados, montar o state com eles e rodar UMA única pergunta Choice offline

Aí tu compara: o que o modelo escolheria versus o que a tua régua fez de fato

Essa comparação em lote é justamente o formato que faz sentido, porque a calibração é estatística e medida em grupos de previsões, não numa resposta individual. Por isso o roteamento sempre precisa de fallback: confidence abaixo do corte cai na régua de sempre, hard decline vai direto pro pedido de novo método

Decisão tipada com fallback burro é melhor que régua rígida sem leitura de contexto. E dá pra medir 🙂

Até o próximo post!

Perguntas frequentes

Quando usar Noul e quando usar Score numa decisão de dunning?

A documentação orienta Noul pra perguntas de sim ou não, tipo ‘devo escalar pro humano agora?’, que devolve a probabilidade de ‘sim’ entre 0 e 1. Score entra quando a resposta é uma posição num espectro em degraus, como o nível de risco de churn do cliente. Os dois tipos podem estar na mesma chamada, avaliados em paralelo e isolados um do outro.

O Jev substitui as Smart Retries do Stripe?

Não, eles resolvem problemas diferentes. As Smart Retries usam IA pra escolher o melhor momento de retentar, com janelas de 1 semana a 2 meses e teto recomendado de oito retentativas. O Jev decide a AÇÃO (retentar, trocar método, avisar o cliente), então o roteamento típico usa os dois juntos: Jev decide o quê, Smart Retries otimiza o quando.

Quanto custa rodar o Jev pra cada fatura recusada?

O preço listado do jev-1.13 é US$ 0,042 por milhão de tokens de entrada, com tokens de saída gratuitos. O mesmo modelo está disponível na OpenRouter como typesafe/jev-1.13, ao mesmo preço de entrada e saída gratuita.

O que fazer quando a fatura recusada é um hard decline?

No Stripe, hard decline significa que a cobrança não pode ser retentada sem um novo método de pagamento. As retentativas continuam agendadas e o attempt_count segue subindo, mas só executam depois que um cartão novo é detectado. Esse estado (hard_decline) entra no record do state pro Jev já saber que retentar sozinho não resolve.

Dá pra chamar o Jev sem instalar SDK nenhum?

Dá sim: o endpoint de avaliação é POST https://api.typesafe.ai/v1/systemone, com header Authorization: Bearer <API_KEY> e Content-Type: application/json. Se preferir SDK, o Python instala com pip install typesafe-sdk ou uv add typesafe-sdk (exige Python 3.10 ou superior) e o JavaScript com npm install @typesafe-ai/sdk. Nos dois casos o client lê a variável de ambiente TYPESAFE_API_KEY e chama jev-latest por padrão.

Quantas opções o Choice aceita pra decidir a ação de cobrança?

Choice aceita no máximo 255 opções por pergunta, então dá sobra pra modelar todo o fluxo de dunning: retentar, trocar método, enviar e-mail, escalar pro humano, cancelar e por aí vai. A resposta traz a opção escolhida, a confiança e a distribuição de probabilidade entre todas as opções avaliadas.




Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Claude Code

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 120 aulas
  • 4 projetos
  • 9h 45min

Blog | Mais populares