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

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
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 chamajev-latestpor padrão) - SDK Python instalado com
pip install typesafe-sdkouuv 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 headerAuthorization: Bearer <API_KEY>eContent-Type: application/json - Se tu já tem tudo plugado na OpenRouter, o modelo
typesafe/jev-1.13també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_failedjá 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 suporterecord:attempt_count, código de recusa, valor da fatura, tempo de casa, histórico de pagas e falhaspolicy: 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
legendque mapeia cada nível à sua descrição e asprobabilitiesde 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.
Formações
Formação Claude Code
Domine Claude Code do absoluto zero até o avançado
- 120 aulas
- 4 projetos
- 9h 45min
Blog | Mais populares

Como montar um workflow de automação combinando decisões do Jev em código
Aprenda a montar um workflow com Jev: decisões tipadas encadeadas em código, alta confiança agindo sozinha e casos incertos escalando para revisão.

Jev decide, LLM escreve: como dividir os papéis dentro de um agente de IA
Jev é o modelo que decide, não escreve: entenda como dividir papéis entre Jev e LLM dentro de um agente de IA e quando usar cada um.

Para quem o Jev serve (e para quem não serve)?
Jev serve pra roteamento, scoring e guardrails em IA, não pra texto ou código. Veja pra quem o Jev serve e quando evitar.
