score-juridico-api-modelo-risco-processos

Score Jurídico: Como Criar um Modelo de Riscos

Score Jurídico via API: Como Construir um Modelo de Risco com Dados de Processos 2026 | Judit Blog
API e Tecnologia

Score Jurídico via API: Como Construir um Modelo de Risco com Dados de Processos

Abril 2026 9 min de leitura

O bureau de crédito tradicional enxerga histórico de pagamentos — o score jurídico revela o que está prestes a acontecer. O judiciário revela o que está prestes a acontecer: execuções em curso, ações trabalhistas em andamento, recuperações judiciais que ainda não chegaram ao Serasa. Um modelo de risco que ignora essa camada está tomando decisões com informação incompleta.

A Judit API expõe dados suficientes para construir um score jurídico robusto

Score jurídico via API: do CPF/CNPJ ao modelo de risco em uma pipeline com a Judit
— tanto para uso direto em motores de crédito quanto como input adicional em modelos de machine learning. Este artigo mostra como fazer isso na prática.


O que é um score jurídico e como calculá-lo via API

Um score jurídico é uma pontuação que resume o perfil de risco processual de uma entidade (CPF ou CNPJ) com base em dados de processos judiciais. Diferente do score de crédito tradicional — que olha para o passado financeiro — o score jurídico olha para o passivo judicial atual e o padrão litigioso histórico.

O score pode ser usado de formas distintas dependendo do contexto:

  • Fintech/crédito: como variável adicional no motor de aprovação, sinalizando risco de insolvência não capturado pelo bureau
  • RH: como critério de triagem em contratações para cargos sensíveis
  • Compliance/KYC: como gatilho para due diligence aprofundada em onboarding de alto risco
  • Imobiliário: como verificação prévia de locatários, compradores e fiadores
  • M&A: como componente do passivo contingente em due diligence

O modelo que você constrói depende do seu caso de uso — mas os dados disponíveis via Judit são os mesmos. A diferença está nas features que você extrai e nos pesos que atribui a cada uma.


Os três endpoints para construção de features

A Judit API tem três modos de consulta, cada um com um trade-off diferente entre velocidade e profundidade:

Endpoint Latência Profundidade Ideal para
Hot Storage (síncrono) Imediata Capa dos processos + partes Onboarding, decisões em tempo real
Consulta Agrupada (síncrona) Imediata Contagens por dimensão Score rápido, features sintéticas
Consulta Assíncrona Segundos–minutos Andamentos, fases, documentos Análise aprofundada, modelos ML

Para um score de risco operacional, a combinação mais eficiente é começar pela consulta agrupada para o perfil sintético e enriquecer com hot storage filtrado para as features que precisam de detalhe de processo.


Features disponíveis e como extraí-las

Features da consulta agrupada

A consulta agrupada (POST https://lawsuits.production.judit.io/requests/create/grouped) retorna contagens agrupadas que se traduzem diretamente em features:

import httpx
import os

API_KEY = os.environ['JUDIT_API_KEY']

def buscar_perfil_agrupado(documento: str, tipo: str) -> dict:
    res = httpx.post(
        'https://lawsuits.production.judit.io/requests/create/grouped',
        headers={'api-key': API_KEY},
        json={'search': {'search_type': tipo, 'search_key': documento}}
    )
    return res.json()

def extrair_features_agrupadas(perfil: dict) -> dict:
    def count(lst, val):
        return next((i['count'] for i in lst if i['value'] == val), 0)

    total = perfil.get('lawsuits_count', 0)

    # Features de polo
    como_passivo   = count(perfil.get('sides', []), 'PASSIVE')
    como_ativo     = count(perfil.get('sides', []), 'ACTIVE')

    # Features de área do direito
    trabalhistas   = count(perfil.get('areas', []), 'DIREITO DO TRABALHO')
    tributarios    = count(perfil.get('areas', []), 'DIREITO TRIBUTÁRIO')
    criminais      = count(perfil.get('areas', []), 'DIREITO PENAL')
    civis          = count(perfil.get('areas', []), 'DIREITO CIVIL')

    # Features de fase processual
    em_andamento   = count(perfil.get('phases', []), 'INICIAL')
    em_execucao    = (
        count(perfil.get('phases', []), 'EXECUÇÃO') +
        count(perfil.get('phases', []), 'EXECUÇÃO OU CUMPRIMENTO')
    )
    com_sentenca   = count(perfil.get('phases', []), 'SENTENÇA')
    transitados    = count(perfil.get('phases', []), 'TRÂNSITO EM JULGADO OU ACORDO')
    arquivados     = count(perfil.get('phases', []), 'ARQUIVADO')

    # Razões derivadas (evita divisão por zero)
    razao_passivo  = como_passivo / total if total > 0 else 0
    razao_execucao = em_execucao / total if total > 0 else 0
    razao_ativo    = em_andamento / total if total > 0 else 0

    return {
        # Volumetria
        'total_processos':       total,
        'processos_como_passivo': como_passivo,
        'processos_como_ativo':   como_ativo,

        # Distribuição por área
        'trabalhistas':          trabalhistas,
        'tributarios':           tributarios,
        'criminais':             criminais,
        'civis':                 civis,

        # Distribuição por fase
        'em_andamento':          em_andamento,
        'em_execucao':           em_execucao,
        'com_sentenca':          com_sentenca,
        'transitados':           transitados,
        'arquivados':            arquivados,

        # Razões derivadas
        'razao_passivo':         razao_passivo,
        'razao_execucao':        razao_execucao,
        'razao_ativo_andamento': razao_ativo,
    }

Features do hot storage filtrado

O hot storage (POST https://lawsuits.production.judit.io/lawsuits) permite consultas síncronas com filtros precisos. Útil para extrair features que dependem do valor da causa ou de tipo de tribunal específico:

def buscar_execucoes_fiscais(documento: str, tipo: str) -> dict:
    """Busca processos onde a entidade é passiva com valor acima de R$ 10k."""
    res = httpx.post(
        'https://lawsuits.production.judit.io/lawsuits',
        headers={'api-key': API_KEY},
        json={
            'search': {
                'search_type': tipo,
                'search_key': documento,
                'search_params': {
                    'filter': {
                        'side': 'Passive',
                        'amount_gte': 10000
                    }
                }
            },
            'process_status': True  # inclui phase e status no retorno
        }
    )
    return res.json()

def extrair_features_hotstorage(dados: dict) -> dict:
    processos = dados.get('response_data', [])

    # Filtra apenas ativos (não arquivados)
    ativos = [p for p in processos if p.get('status') == 'Ativo']

    # Valor total em execução
    valor_total_execucao = sum(
        p.get('amount', 0)
        for p in ativos
        if p.get('phase') in ('Execução', 'Execução ou cumprimento')
    )

    # Processos trabalhistas ativos como réu
    trabalhistas_ativos = [
        p for p in ativos
        if any(s.get('name', '').upper().startswith('TRABALH')
               for s in p.get('subjects', []))
    ]

    # Processos tributários ativos
    tributarios_ativos = [
        p for p in ativos
        if 'TRIBUTÁRIO' in p.get('area', '').upper()
        or any('FISCAL' in s.get('name', '').upper()
               for s in p.get('subjects', []))
    ]

    return {
        'valor_total_passivo_ativo': sum(p.get('amount', 0) for p in ativos),
        'valor_total_em_execucao':   valor_total_execucao,
        'qtd_trabalhistas_ativos':   len(trabalhistas_ativos),
        'qtd_tributarios_ativos':    len(tributarios_ativos),
        'tem_processo_ativo':        len(ativos) > 0,
    }

Calculando o score

Com as features extraídas, o score jurídico pode ser calculado de diferentes formas dependendo da maturidade do seu modelo:

Modelo de regras (baseline)

Para um primeiro modelo rápido, um sistema de pontuação baseado em regras já entrega valor imediato:

def calcular_score_juridico(features: dict) -> dict:
    """
    Retorna um score de 0 a 100.
    Menor score = maior risco judicial.
    """
    score = 100
    flags = []

    # --- Penalizações por volumetria ---

    total = features['total_processos']

    if total == 0:
        pass  # Sem histórico — sem penalização
    elif total <= 3:
        score -= 5
    elif total <= 10:
        score -= 15
    elif total <= 30:
        score -= 25
    else:
        score -= 40
        flags.append('alto_volume_processual')

    # --- Penalizações por polo ---

    if features['razao_passivo'] > 0.8 and total >= 3:
        score -= 15
        flags.append('predominantemente_passivo')

    # --- Penalizações por fase ---

    if features['em_execucao'] >= 3:
        score -= 20
        flags.append('multiplas_execucoes')
    elif features['em_execucao'] >= 1:
        score -= 10

    if features['valor_total_em_execucao'] > 100_000:
        score -= 15
        flags.append('alto_valor_em_execucao')
    elif features['valor_total_em_execucao'] > 30_000:
        score -= 8

    # --- Penalizações por área ---

    if features['tributarios'] >= 5:
        score -= 15
        flags.append('alto_passivo_tributario')

    if features['criminais'] >= 1:
        score -= 20
        flags.append('processo_criminal')

    if features['trabalhistas'] >= 10:
        score -= 10
        flags.append('alto_passivo_trabalhista')

    # --- Penalizações por dados cadastrais ---
    # (integrar com o campo special_status da consulta de entity)
    if features.get('recuperacao_judicial'):
        score -= 40
        flags.append('recuperacao_judicial')

    # Garante que o score não seja negativo
    score = max(0, score)

    # Faixa de risco
    if score >= 75:
        faixa = 'BAIXO'
    elif score >= 50:
        faixa = 'MEDIO'
    elif score >= 25:
        faixa = 'ALTO'
    else:
        faixa = 'CRITICO'

    return {
        'score': score,
        'faixa': faixa,
        'flags': flags
    }

Pipeline completo

def avaliar_risco_juridico(documento: str, tipo: str = 'cnpj') -> dict:
    """
    Pipeline completo: coleta features e retorna score.
    """
    # Etapa 1: perfil agrupado (síncrono, imediato)
    perfil = buscar_perfil_agrupado(documento, tipo)
    features = extrair_features_agrupadas(perfil)

    # Etapa 2: hot storage filtrado para features de valor
    dados_hotstorage = buscar_execucoes_fiscais(documento, tipo)
    features.update(extrair_features_hotstorage(dados_hotstorage))

    # Etapa 3: score
    resultado = calcular_score_juridico(features)

    return {
        'documento': documento,
        'tipo': tipo,
        'features': features,
        **resultado
    }


# Exemplo de uso
resultado = avaliar_risco_juridico('00.000.000/0001-00', 'cnpj')

print(f"Score: {resultado['score']}/100")
print(f"Faixa: {resultado['faixa']}")
print(f"Flags: {', '.join(resultado['flags']) or 'nenhuma'}")

Filtros avançados para features específicas

Para features que exigem recortes precisos, os filtros da consulta histórica permitem isolar exatamente o subconjunto de processos relevante:

Processos recentes como passivo (últimos 2 anos)

{
  "search": {
    "search_type": "cnpj",
    "search_key": "00.000.000/0001-00",
    "search_params": {
      "filter": {
        "side": "Passive",
        "distribution_date_gte": "2024-01-01T00:00:00.000Z"
      }
    }
  }
}

Processos trabalhistas de alto valor

{
  "search": {
    "search_type": "cnpj",
    "search_key": "00.000.000/0001-00",
    "search_params": {
      "filter": {
        "side": "Passive",
        "amount_gte": 50000,
        "tribunals": {
          "keys": ["TRT1","TRT2","TRT3","TRT4","TRT5","TRT6","TRT7","TRT8","TRT9",
                   "TRT10","TRT11","TRT12","TRT13","TRT14","TRT15","TRT16","TRT17",
                   "TRT18","TRT19","TRT20","TRT21","TRT22","TRT23","TRT24"],
          "not_equal": false
        }
      }
    }
  }
}

Excluir processos arquivados e focar apenas nos ativos

O hot storage com process_status: true retorna o campo phase por processo. Filtre no código:

processos_ativos = [
    p for p in response_data
    if p.get('status') == 'Ativo'
    and p.get('phase') not in ('Arquivado', 'Cancelado', 'Trânsito em julgado ou acordo')
]

Calibração do score jurídico por vertical de negócio

O score não tem peso universal. Os mesmos dados precisam ser interpretados de forma diferente dependendo do contexto de uso:

Crédito para PJ (fintechs, bancos): o peso maior vai para execuções fiscais ativas, valor total do passivo e presença de recuperação judicial. Processos trabalhistas de baixo valor são ruído para esse contexto.

RH e background check: o peso vai para processos criminais e mandados de prisão. Volume de processos cíveis é irrelevante aqui — um candidato pode ter muitos processos trabalhistas como autor (reclamante) sem que isso represente risco para o cargo.

Compliance/KYC: peso alto para processos criminais, PEP (pessoas politicamente expostas), lavagem de dinheiro e processos em instâncias superiores (STJ, STF).

Imobiliário: peso alto para ações de despejo (como passivo), execuções cíveis ativas e processos de cobrança recentes.

PESOS = {
    'credito_pj': {
        'em_execucao':               25,
        'tributarios':               20,
        'razao_passivo':             15,
        'alto_volume_processual':    15,
        'recuperacao_judicial':      40,
        'criminais':                  5,
    },
    'rh_background': {
        'criminais':                 40,
        'em_execucao':               10,
        'tributarios':                5,
        'razao_passivo':              5,
    },
    'compliance_kyc': {
        'criminais':                 35,
        'tributarios':               20,
        'em_execucao':               15,
        'alto_volume_processual':    10,
    },
    'imobiliario': {
        'em_execucao':               20,
        'civis':                     15,
        'razao_passivo':             15,
        'tributarios':               10,
    }
}

Features adicionais via entity data

A consulta de dados cadastrais (response_type: "entity") entrega um campo crítico para modelos de risco de PJ: special_status.

def buscar_dados_cadastrais(cnpj: str) -> dict:
    res = httpx.post(
        'https://lawsuits.prod.judit.io/requests/create',
        headers={'api-key': API_KEY},
        json={
            'search': {
                'search_type': 'cnpj',
                'search_key': cnpj,
                'response_type': 'entity'
            }
        }
    )
    dados = res.json()
    entidade = dados.get('response_data', [{}])[0]

    return {
        'recuperacao_judicial': entidade.get('special_status') == 'RECUPERACAO JUDICIAL',
        'falencia':             entidade.get('special_status') == 'FALENCIA',
        'inapta_receita':       not entidade.get('revenue_service_active', True),
        'capital_social':       entidade.get('share_capital', 0),
        'tem_processos':        dados.get('has_lawsuits', False),
    }

O campo has_lawsuits retornado nessa consulta é um boolean que pode ser usado como pré-filtro: se false, o modelo pode assumir score máximo sem acionar a consulta agrupada.


Boas práticas para produção

Cache agressivo para dados agrupados. O perfil litigioso de um CNPJ não muda em horas. Use cache_ttl_in_days: 7 para consultas de scoring em volume — economiza custo e reduz latência.

On-demand apenas para decisões críticas. O parâmetro on_demand: true força a busca direto no tribunal — mais preciso, mas mais lento. Reserve para aprovações de crédito de alto valor; para triagem em volume, o datalake é suficiente.

Separe features de score de features de auditoria. O request_id de cada consulta deve ser salvo junto com a decisão. Em caso de contestação regulatória, você precisa provar quais dados embasaram a recusa.

Trate ausência de dados como categoria. Um CPF sem histórico judicial não é necessariamente de baixo risco — pode ser uma conta recém-criada. Crie uma categoria explícita sem_historico e não confunda com risco_zero.

Monitore a distribuição do score ao longo do tempo. Se a proporção de entidades com score alto começar a cair sem mudança no critério de aprovação, provavelmente há uma mudança no padrão dos dados (novo sistema de tribunal, mudança de cobertura, etc.) que precisa ser investigada.


Próximos passos

Para implementar o score jurídico na sua operação:


Publicado em: abril de 2026 — Atualizado a cada trimestre

Quer saber como a JUDIT pode ajudar seu negócio?