Referência (Redoc)OpenAPIllms.txt

Guia da Debit API

Guia prático da Debit API — cálculos jurídicos e financeiros do Brasil (Lei 14.905/2024, manual CNJ 2022, EC 103/2019) via REST e MCP.

Base URL de produção: https://a-api.debit.com.br/debitapi (ajuste conforme o host/prefixo do seu ambiente). Nos exemplos abaixo use $BASE.


1. Autenticação

Toda rota (exceto /health e /v1/auth/*) exige Authorization: Bearer <credencial>. Há quatro tipos:

Tipo Formato Como obter Quando usar
Chave do site UUID (b62d5a5f-…) Painel da debit → menu → API → "Minhas chaves de API" O jeito mais simples: pega no site e usa
Chave de API dbt_live_… / dbt_test_… POST /v1/keys (exige JWT) Servidor↔servidor, automações, agentes; permite escopos
JWT eyJ… (~15 min) POST /v1/auth/login Sessão humana; obrigatório para gerenciar chaves
OAuth 2.1 Bearer via "Login com Debit" /authorize (PKCE + DCR) Conectores MCP (Claude, ChatGPT)

Chave criada no site (sem escrever código)

Basta logar em debit.com.br, abrir o menu API e criar uma chave. Ela vale em todas as rotas /v1/* e também no MCP (https://mcp.debit.com.br/mcp), sempre no cabeçalho:

curl -s "$BASE/v1/indices" -H "Authorization: Bearer b62d5a5f-....-............"

As chaves já criadas nesse painel continuam valendo aqui — é a mesma chave, não é preciso gerar outra.

Diferenças em relação à chave dbt_live_:

Em GET /v1/keys (com JWT) as chaves do site aparecem em frontendKeys, com o prefixo apenas. Chave inexistente, apagada no site ou fora da liberação de acesso → 401 INVALID_API_KEY.

Quem opera o servidor tem três chaves de configuração (.env) para essa via: LEGACY_KEYS_ENABLED (desliga), LEGACY_KEYS_REQUIRE_PLAN (só contas com plano, regra herdada do painel) e LEGACY_KEYS_SCOPES (restringe os escopos concedidos).

Login (JWT)

curl -X POST "$BASE/v1/auth/login" \
  -H 'Content-Type: application/json' \
  -d '{"identifier":"meu_login","senha":"minha_senha"}'
# → { "user": {...}, "accessToken": "eyJ...", "refreshToken": "..." }

Criar uma chave de API (usando o JWT)

curl -X POST "$BASE/v1/keys" \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"name":"Integração","scopes":["index:read","calc:read","calc:write","calc:export"]}'
# → { "id": 7, "key": "dbt_live_...", "warning": "Guarde a chave agora — não será exibida de novo." }

Escopos

index:read · calc:read · calc:write · calc:export · extract:run · account:read. Sem o escopo necessário → 403. Sem credencial válida → 401.

dbt_live_ e dbt_test_: não existe sandbox

POST /v1/keys aceita "env": "test" e devolve uma chave com prefixo dbt_test_. Isso é só um rótulo, para você separar credenciais nos seus próprios ambientes. Não há ambiente de testes: uma chave dbt_test_ fala com a mesma base de produção, cria cálculos reais na sua conta e eles aparecem no site como qualquer outro. As duas chaves são idênticas em permissão e efeito.

Para experimentar sem sujar a conta, use POST /v1/correct sem save (não persiste nada) e apague os cálculos de teste com DELETE /v1/calc/{type}/{id}.


2. Conceitos


3. Fluxo de um cálculo salvo

POST   /v1/calc                      → cria         → { calcId, hash }
PATCH  /v1/calc/{type}/{id}          → preenche     { "fields": { … } }
POST   /v1/calc/{type}/{id}/run      → roda         → totais + memória
GET    /v1/calc/{type}/{id}/export   → PDF/HTML/Excel
DELETE /v1/calc/{type}/{id}          → manda para a lixeira

Os campos vão dentro de fields. {"dia_atualiza": "25/08/2026"} na raiz do corpo não grava nada — e responde 400 NO_FIELDS dizendo exatamente isso. O correto é {"fields": {"dia_atualiza": "25/08/2026"}}. Mande todos os campos num PATCH só: além de mais rápido, é o que mantém o consumo de limite em 1 (ver seção 8).

Excluir um cálculo

DELETE /v1/calc/{type}/{id} (escopo calc:write) manda o cálculo para a lixeira — o mesmo soft-delete do site. Ele some de GET /v1/calc e continua recuperável pelo site; a API não tem desfazer.

Retomar um cálculo que já existe

Não precisa ter criado o cálculo por aqui, nem ter guardado nada: GET /v1/calc lista o que está na conta — inclusive o que foi feito no site — e o calcId de lá entra direto nas rotas acima.

# o que existe na conta (mesma lista do dashboard)
curl -s "$BASE/v1/calc?limit=20" -H "Authorization: Bearer $KEY"
# → { "calculations": [ { "calcId": 91234, "type": "pensaoAlimenticia",
#                         "name": "Cliente X", "createdAt": …, "updatedAt": … } ] }

# e daí em diante é só o calcId
curl -s -X POST "$BASE/v1/calc/pensaoAlimenticia/91234/run" \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{}'

Filtre por tipo com ?type=, pagine com ?limit= e ?offset=.

Cálculos de geração anterior (só leitura)

Contas antigas têm atualizacaoMonetaria criados em versões anteriores da calculadora. Quando elas foram migradas, o que ficou guardado foi o demonstrativo em PDF, não os dados de entrada — então esses cálculos aparecem na conta e na listagem, mas não são recalculáveis. No site eles abrem apenas para consulta.

Na API:

operação resposta
GET …/export?format=pdf o PDF arquivado — é o que existe do cálculo
run, PATCH, GET /v1/calc/{type}/{id} 409 LEGACY_CALC_READ_ONLY
export em html/excel 400 EXPORT_UNAVAILABLE

Quem exporta um acervo inteiro precisa tratar o 409 como "baixe o PDF", não como falha: numa conta real eles foram 731 de 4.186 (18%).

Exemplo completo (atualização monetária) — curl

# 1) criar
CREATE=$(curl -s -X POST "$BASE/v1/calc" -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' -d '{"type":"atualizacaoMonetaria","name":"Correção IPCA-E"}')
ID=$(echo "$CREATE" | jq -r .calcId)

# 2) preencher (campos do GET /v1/describe/atualizacaoMonetaria)
curl -s -X PATCH "$BASE/v1/calc/atualizacaoMonetaria/$ID" -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' -d '{
    "fields":{
      "indexador":45,
      "dia_atualiza":"01/06/2026",
      "lista":[{"dia":"01/03/2019","valor":10000,"desc":"Principal"}],
      "calc_jurosm":true, "juros_moratorios.percentual":"1"
    }}'

# 3) rodar
curl -s -X POST "$BASE/v1/calc/atualizacaoMonetaria/$ID/run" -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' -d '{}'

# 4) exportar PDF
curl -s "$BASE/v1/calc/atualizacaoMonetaria/$ID/export?format=pdf" \
  -H "Authorization: Bearer $KEY" -o calculo.pdf

O mesmo em JavaScript (fetch)

const h = { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' };
const base = process.env.BASE;

const { calcId } = await (await fetch(`${base}/v1/calc`, {
  method: 'POST', headers: h, body: JSON.stringify({ type: 'atualizacaoMonetaria', name: 'Correção' }),
})).json();

await fetch(`${base}/v1/calc/atualizacaoMonetaria/${calcId}`, {
  method: 'PATCH', headers: h, body: JSON.stringify({
    fields: {
      indexador: 45, dia_atualiza: '01/06/2026',
      lista: [{ dia: '01/03/2019', valor: 10000 }],
      calc_jurosm: true, 'juros_moratorios.percentual': '1',
    },
  }),
});

const resultado = await (await fetch(`${base}/v1/calc/atualizacaoMonetaria/${calcId}/run`, {
  method: 'POST', headers: h, body: JSON.stringify({}),
})).json();

Atalho: correção monetária em uma chamada

Para atualizacaoMonetaria, o POST /v1/correct faz criar→preencher→rodar de uma vez:

curl -s -X POST "$BASE/v1/correct" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{
  "index":"ipca_e", "updateTo":"01/06/2026",
  "items":[{"date":"01/03/2019","amount":10000,"description":"Principal"}],
  "juros":{"percentual":"1","from":"citacao"}, "save":false
}'

Para corrigir por uma tabela judicial em vez de por um índice, troque index por table (o id de GET /v1/tables). Os juros e os períodos de SELIC da tabela entram sozinhos:

curl -s -X POST "$BASE/v1/correct" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{
  "table":23176, "updateTo":"01/08/2026",
  "items":[{"date":"01/03/2019","amount":10000,"description":"Principal"}]
}'

Mandar index e table juntos é 400 CONFLICTING_FIELDS — os dois gravam o mesmo campo do motor.

SELIC: início, fim e base

A SELIC pode entrar por dois caminhos, e eles nunca somam:

Dois campos ajustam a rubrica nos dois casos:

campo efeito
selicUntil Data final da SELIC da EC 113 (só vale com "selic": true). Omitido = até a data de atualização. Preenchido, depois dele o índice volta a corrigir e os juros voltam a correr. Não recorta a SELIC de uma tabela judicial — lá o período é o que a tabela declara.
selicBase Base de incidência: a (valor atualizado), am (+multa), aj (+juros moratórios e compensatórios), ajm (+juros+multa).
selicVersion Como as datas da SELIC são lidas: 1 (ou omitido) conta por mês — o dia de selicFrom/selicUntil é ignorado e o mês entra inteiro; 2 conta por dia.

selicBase omitido em cálculo já existente vale ajm, que é o comportamento histórico — é o que garante que nenhum cálculo antigo mude de valor. Cálculo novo nasce com am.

SELIC por dia (selicVersion: 2)

Com selicVersion: 2, as datas da SELIC valem ao pé da letra e o mês em que ela entra (ou sai) é contado proporcional aos dias, em vez de inteiro. Uma SELIC a partir de 15/03/2022 faz março render 17 dos 31 dias — a mesma conta pró-rata que a correção por índice já usa (linear sobre a taxa do mês, dias reais no divisor; não existe série diária de SELIC, a do Bacen é mensal).

Duas condições, e as duas precisam valer:

  1. selicVersion: 2;
  2. o pró-rata da correção ligado — calc_perc_prorata: "1", ou calc_tj_prorata: "1" quando o indexador é uma tabela judicial. É o mesmo interruptor da correção, de propósito: SELIC e índice contando por critérios diferentes no mesmo cálculo seria incoerente.

Só a ponta inicial é prorateada. É o que o Cálculo Judicial faz na SELIC, e a paridade entre os dois produtos é verificada por arnês — pedir a mesma coisa nos dois tem de dar o mesmo número.

⚠️ Cálculo salvo antes desta opção não tem o carimbo e continua contando por mês, ao centavo, mesmo com o pró-rata ligado. Chamada por API é sempre nova: informe selicVersion: 2 para contar o dia. Cálculo criado pela tela já nasce com ele.

A resposta traz selic no resumo e em cada item sempre que houver SELIC, por qualquer dos dois caminhos.


4. Nomes de campo (importante)

Os campos enviados em fields (PATCH) usam chaves achatadas — as mesmas que o GET /v1/describe/{type} lista. Regras por família de tipo:

Sempre confira a lista oficial em GET /v1/describe/{type} — ela traz campo, tipo, required, descricao e valores (enums).

O que é conferido antes de gravar

O gateway valida o lote antes de tocar no cálculo — se qualquer campo estiver errado, nada é gravado:

Isso existe porque o motor não recusa entrada malformada: ele calcula e devolve R$ 0,00 com HTTP 200. Um laudo zerado com cara de laudo pronto é pior que um erro.


Índices: slug e id

Todo índice do catálogo tem id; a maioria também tem slug (ipca_e, inpc, selic…), e os dois resolvem em GET /v1/indices/{slug|id}. Quando dois índices compartilham a mesma série de dados, o mais antigo mantém o slug histórico e o outro recebe um nome próprio — hoje selic (id 23) e selic_mais1 (id 31, "Selic + 1% (RFB)"), indice_tj_tabela_uniforme_geral (id 58, TJ/MA) e tjmt (id 69). Nunca há dois índices com o mesmo slug.

Tabelas judiciais: GET /v1/tables

Tabela judicial não é índice, e desde 26/08/2026 tem rota própria. GET /v1/tables devolve id, slug (o urlamigavel do site) e name. Esse id é o calcId real da tabela — o mesmo espaço de id das tabelas que você cria em POST /v1/calc {"type":"tabelasJudiciais"} —, e é ele que vai em indexador de um calculosJudiciais.

Se você integrou antes desta data: as tabelas vinham dentro de GET /v1/indices, como as entradas sem slug, e com o id somado de 1000. Aquele número nunca funcionou em indexador: o motor procurava outra tabela (ou nenhuma) e o cálculo voltava com o valor nominal, coeficiente 1 e resumoTab.indexador: 0 — com HTTP 200 e sem erro. Hoje esse id é recusado com 400 INDEX_LEGACY_ID informando o número correto, e o campo legacyCatalogId de /v1/tables permite reconhecê-lo. Subtraia 1000 do id antigo, ou simplesmente relista em /v1/tables.

Nem todo índice serve a todo tipo de cálculo: o selic_mais1, por exemplo, é usado dentro de uma tabela judicial (vl: [{ m, i: 31 }]) e não é aceito pelo motor de atualização monetária, que responde 502 ENGINE_NO_RESULT.

Baixar a série de um índice

GET /v1/indices/{slug|id}/series (escopo index:read) devolve o histórico mês a mês.

curl -s "$BASE/v1/indices/ipca_e/series?from=2024-01&to=2024-03" -H "Authorization: Bearer $KEY"
{
  "index": { "id": 45, "slug": "ipca_e", "name": "IPCA-E (IBGE)", "kind": "index", "type": "perc" },
  "basis": { "valueMeaning": "monthly_percent", "factorField": "accumulated",
             "formula": "corrigido = valor * accumulated[ate] / accumulated[de]",
             "variants": { "accumulatedPositive": "expurga os meses de indice negativo; NAO e o padrao da debit" } },
  "sampling": "first_day_of_month",
  "from": "2024-01", "to": "2024-03", "count": 3, "truncated": false,
  "series": [ { "month": "2024-01", "date": "2024-01-01", "value": 0.31,
                "accumulated": 5.79, "accumulatedPositive": 5.98, "published": true } ]
}

factorField mudou em 26/08/2026: era accumulatedPositive, virou accumulated. A série declarava uma coluna e o POST /v1/correct do mesmo servidor calculava por outra — ele cria um atualizacaoMonetaria com o padrão calc_perc_indice_neg = "1", que lê accumulated. As duas só coincidem em índice sem mês negativo, e o IPCA tem 22, o IGP-M tem 69. Quem seguia a fórmula publicada não batia com o /v1/correct nem com o laudo. Os dois campos continuam vindo na série: use accumulatedPositive só se você quiser mesmo expurgar os meses negativos.

Na SELIC, factorField era accumulated e contradizia a própria fórmula (que é soma, não razão): agora é value. A coluna accumulated da SELIC é NULL em 359 das 481 linhas e vinha como 0 — hoje vem como null.

param vale para o que faz
from / to ambos recorta o período (AAAA-MM)
format=csv ambos devolve text/csv como anexo, em vez de JSON
sampling=daily índice comum dólar, euro, Ufesp e Ufir têm série DIÁRIA. O padrão (monthly) devolve só a linha do dia 1º — que é a que o cálculo usa. daily devolve a série real
asOf tabela judicial data de referência da correção (AAAA-MM-DD)

Leia o basis antes de usar os números. Ele diz qual campo é o fator e como aplicá-lo: num índice perc quem corrige é o accumulatedPositive, não o value (que é a variação do mês). Multiplicar value dá resultado errado.

value: null com published: false é mês ainda não publicado, não deflação — a linha continua na série porque o accumulated dela é legítimo.

Tabela judicial: a série é calculada, não guardada

Tabela judicial (as entradas com kind: "judicial_table", id a partir de 1000) não tem série armazenada: o banco guarda quais índices valem em cada faixa, e o coeficiente é calculado na hora. Duas consequências:

curl -s "$BASE/v1/indices/1007/series?asOf=2026-08-01" -H "Authorization: Bearer $KEY"

Vem também segments, os marcos da tabela — qual índice vale em cada faixa, e a descrição dos percentuais fixados por lei ("Fixado em 42,72%").

A série não traz juros. Correção e juros são contas ortogonais; para juros use POST /v1/correct.

No MCP, debit_get_index_series devolve no máximo 240 meses (uma série cheia passa de 900 pontos e não cabe bem num contexto de agente). Para a série inteira, ou para planilha, use o REST com format=csv.

5. Tipos de cálculo

type Descrição Campos obrigatórios típicos
atualizacaoMonetaria Correção monetária (Lei 14.905/2024) indexador, dia_atualiza, lista
calculosJudiciais Liquidação de sentença indexador, dataAtual, valorList
cartaoPonto Horas extras a partir do ponto jornada, horario
pensaoAlimenticia Débito de pensão + atualização fixacao.tipo, dia_atualiza, lista
saldoDevedor Revisão de financiamento valor, juros, juros_periodo, parcelas, dia
tabelasFinanciamento PRICE / SAC / SACRE valor, juros, parcelas, sistema, carencia
tabelasJudiciais Monta a tabela de correção usada pelos cálculos (não atualiza valores) vl
prevDifNRecebidas Diferenças previdenciárias especieBeneficio, dataInicioBeneficio, indexador, valorList

Os campos completos (todos, com enums e descrições) de cada tipo estão em GET /v1/describe/{type}. Esta tabela mostra só os mínimos para um cálculo válido.

Fora da API: o cálculo trabalhista (CLT), a RMI previdenciária e a contagem de tempo de contribuição continuam no site, mas não são expostos por esta API nem pelo MCP. Pedi-los devolve 400 INVALID_TYPE.

Exportação por tipo

Tipo HTML PDF DOCX Excel O que o demonstrativo traz
atualizacaoMonetaria parcelas, correção, juros, multa e honorários
calculosJudiciais parâmetros, critérios, composição da tabela, parcelas, sucumbências, imputação, resumo e o anexo mês a mês
pensaoAlimenticia competências, pagamentos, imputação e art. 528
tabelasJudiciais a série mensal da tabela e as faixas de juros
cartaoPonto espelho de ponto por mês, marcações, horas e extras por adicional
saldoDevedor parâmetros do contrato e evolução do saldo mês a mês
tabelasFinanciamento planilha de amortização com totais e encargos
prevDifNRecebidas diferenças por competência, critérios de correção e totalização

Janela do plano gratuito (retroativo de 2 anos)

O run executa o cálculo na conta do usuário, com o plano que ela tem. Conta sem assinatura ativa calcula os últimos 2 anos; lançamento com data anterior entra no total pelo valor nominal — sem correção monetária, juros, multa nem SELIC. É a mesma régua que o site sempre anunciou em /precos, e vale para atualizacaoMonetaria e calculosJudiciais.

Como reconhecer. Em calculosJudiciais o resultado ganha um bloco (ausente quando nada foi bloqueado, e sempre ausente para assinante):

{
  "bloqueio_assinatura": {
    "aplicado": true,
    "anos": 2,
    "dia_minimo": "12/09/2024",
    "dia_minimo_int": 20240912,
    "mensagem": "Correção monetária e juros anteriores a esta data são exclusivos dos assinantes; o valor entrou no total pelo nominal.",
    "linhas": [ { "dia": "15/03/2019", "valor": 10000, "desc": "" } ]
  }
}

Cada linha atingida traz bloqueada_assinatura: true e mensagem_bloqueio; no demonstrativo (HTML/PDF) a data sai com asterisco e o texto dos critérios explica o que faltou. O anexo mês a mês (anexarTabela) começa na mesma data.

⚠️ Pagamento nunca é bloqueado. Valor negativo é dedução, e derrubá-lo para o nominal aumentaria o débito contra o devedor. Parcela pode ficar sem correção; dedução, não.

A API antiga /api/v2/calculosJudiciais é cobrada pela apikey e não aplica esta janela.

Multa do art. 774, parágrafo único, do CPC/2015

Ato atentatório à dignidade da justiça: até 20% do valor atualizado do débito em execução, revertida em proveito do exequente. Não é automática — o percentual é fixado pelo juiz, e por isso o campo existe mas nasce desligado.

Disponível nos dois tipos, com os nomes de cada um:

atualização monetária cálculo judicial
liga calc_multa_774 calcularMulta774
configura multa_774 multa774

modo: "p" aplica o percentual sobre a base escolhida; modo: "v" lança o valor em reais que a decisão arbitrou, pelo nominal. A base tem oito componentes (base_valor_atualizado, base_juros_moratorios, base_juros_compensatorios, base_multa, base_selic, base_custas, base_sucumbencias, base_honorarios), todos marcados por padrão — o mesmo padrão da multa do art. 523, o que faz a base ser igual ao subtotal. base_multa523 e base_honorarios523 ficam desmarcados: é sanção sobre sanção e exige escolha explícita. Só na atualização monetária existe base_deducoes (padrão ligado), que abate pagamentos da base.

O objeto pode ser parcial — as chaves ausentes vêm do padrão. Percentual acima de 20% é calculado, e o laudo registra a ressalva; a API só recusa zero, negativo e acima de 100%.

Imputação de pagamentos (arts. 352 a 355 do Código Civil)

Um valor negativo na lista de parcelas é um pagamento. Sem nenhuma opção, ele abate principal e juros ao mesmo tempo, proporcionalmente — que é o comportamento de sempre e continua sendo o padrão.

Ligando imputacao.ativa, o pagamento passa a seguir a ordem legal: primeiro os juros vencidos, só o excedente no capital (art. 354), entre as dívidas líquidas e vencidas (art. 355). A consequência prática é que o total fica acima do abatimento direto — quitar juros antes reduz menos o capital, e o capital que sobra continua rendendo juros.

Disponível nos dois tipos, com os mesmos nomes:

campo padrão o que faz
imputacao.ativa false liga a imputação dos arts. 352-355
imputacao.art354 true juros vencidos antes do capital
imputacao.ordem vencimento vencimento (mais antiga) ou onerosidade (art. 355, in fine)
imputacao.permitir_vincendas false ⚠️ imputar em vincenda é compensação, não imputação

Na atualização monetária, a metodologia sai do campo que já existia: calc_abater_valores false faz os juros pararem de correr sobre a parte quitada na data do pagamento; true apura todos os juros até a data de atualização e só então imputa. Não há chave nova para isso.

O excedente que não encontra parcela vencida não abate nada: sai em imputacao_credito_nao_imputado (atualização monetária) ou imputacao.credito_nao_imputado (cálculo judicial), informado à parte. Excedente em parcela vincenda seria compensação, vedada.

Em trecho corrigido pela SELIC não há juros destacados — a SELIC já os embute — e ali o art. 354 fica sem objeto: o pagamento abate o saldo único. O laudo declara isso.

O resultado traz, além dos totais de sempre, quanto foi para juros, quanto para capital, quais parcelas foram quitadas e uma linha por par pagamento × parcela. O laudo (HTML, PDF e DOCX) ganha a seção "Imputação dos pagamentos"; o cálculo judicial ganha as colunas "Imput. em juros" e "Imput. no capital" no demonstrativo.

Consulte, não decore. GET /v1/describe/{type} traz o campo exportFormats com os formatos daquele tipo, gerado do mesmo objeto que decide a rota — é a fonte que não tem como divergir. A tabela acima é cortesia.

O PDF é desenhado (pdfkit): é o mesmo documento que o site entrega. Os três tipos sem PDF são os que ainda não têm renderizador — não é limitação da API.

O DOCX é OOXML de verdade (não HTML com a extensão trocada): o Word abre sem o aviso de conteúdo incompatível, e as tabelas continuam sendo tabelas editáveis. É o mesmo laudo do HTML e do PDF — mesma redação, mesmos números, mesma ordem; só muda o meio. Existe em atualizacaoMonetaria e em calculosJudiciais.

Pedir um formato indisponível devolve 400 EXPORT_UNAVAILABLE com a lista do que existe para aquele tipo.

Pelo MCP, debit_export_calculation devolve o HTML inline e o arquivo de pdf/docx/excel — um bloco resource com o conteúdo em base64, pronto para o cliente salvar. Não é link: os bytes chegam no mesmo turno, sem segunda ida à rede e sem precisar da chave. Acima de 6 MB (MCP_EXPORT_BLOB_MAX_BYTES) o arquivo não é embutido e aí sim volta a URL do REST, com o tamanho medido.

Destaques de valores


6. Extração de documentos (IA)

Tamanho. O teto é de 25 MB por arquivo, aplicado sobre o arquivo já decodificado. Em fileBase64 o corpo cresce ~33%, então o parser aceita até ~34 MB de JSON — mandar o arquivo como multipart (campo file) evita esse acréscimo. Acima de ~20 MB a própria IA costuma recusar por tamanho, e a resposta vem como 413 FILE_TOO_LARGE dizendo exatamente isso. Documento ilegível ou fora de formato volta como 502 EXTRACT_FAILED com o motivo. Corpos muito acima do teto (dezenas de MB) podem ser recusados já pelo proxy, com um 413 sem o envelope JSON — a semântica é a mesma.

POST /v1/extract (escopo extract:run) lê CNIS, sentença, cartão de ponto ou doc trabalhista:

curl -s -X POST "$BASE/v1/extract" -H "Authorization: Bearer $KEY" \
  -F 'docType=cnis' -F '[email protected]'
# ou JSON: { "docType":"cnis", "fileBase64":"...", "filename":"cnis.pdf" }

docType: cnis | sentenca | cartaoPonto | trabalhista. O resultado pode alimentar sc (rmi), valorList, etc.

Retenção (LGPD). O arquivo enviado é apagado do servidor 48 horas depois do upload, junto com os dados que a IA extraiu dele. A resposta desta chamada é a única cópia do resultado — guarde-a, ou aplique-a a um cálculo, na mesma sessão. Não existe rota para rebaixar o documento depois. A exceção é o arquivo fiscal de ponto (AFD/AEJ) importado por /importarPontoFiscal, que não passa por IA e é guardado por 30 dias porque o passo de aplicação o relê.


7. MCP (agentes de IA)

A raiz do host (GET /) é pública e serve de índice: abre uma página com o endereço do MCP, a base REST e os links de documentação; para cliente de API (Accept: application/json) devolve o mesmo em JSON. GET /mcp continua respondendo 405 — o transporte é POST —, mas explica o que fazer em vez de só recusar.

Endpoint POST /mcp (Streamable HTTP, JSON-RPC 2.0), mesma autenticação. As tools espelham o REST: debit_list_indices, debit_list_judicial_tables, debit_get_index_series, debit_describe_calculation_type, debit_correct_value, debit_create_calculation, debit_set_calculation_fields, debit_run_calculation, debit_get_calculation, debit_list_my_calculations, debit_delete_calculation, debit_export_calculation, debit_extract_document.

Conectores remotos (Claude/ChatGPT) descobrem o OAuth via /.well-known/oauth-authorization-server e abrem o "Login com Debit".


8. Erros

Formato padrão:

{ "error": { "code": "FORBIDDEN_SCOPE", "message": "Escopo necessário: calc:write", "hint": "…", "docUrl": "…" } }
Código HTTP Quando acontece / o que fazer
BAD_REQUEST 400 Falta um campo do corpo (ex.: index, updateTo, items em /v1/correct).
INVALID_API_KEY · INVALID_CREDENTIALS 401 Chave inexistente/revogada, ou login e senha que não conferem.
NOT_ALLOWED 401 Credencial válida, conta fora da liberação (fase de testes). Fale com o suporte.
FORBIDDEN_SCOPE 403 A chave não tem o escopo da operação. Crie outra em POST /v1/keys com o escopo que falta.
HUMAN_ONLY 403 Gerenciar chaves exige login por JWT — uma chave não cria outra chave.
FORBIDDEN 403 O cálculo é de outra conta.
FORBIDDEN (do motor) 403 O motor recusou o acesso ao cálculo. Em geral um hash enviado que não é o daquele cálculo — se estiver mandando hash, tente sem.
INVALID_TYPE 400 Tipo de cálculo que não existe (a mensagem lista os aceitos).
TYPE_NOT_DESCRIBED 404 Não há schema para esse tipo (idem, a hint traz a lista).
BAD_ID 400 calcId não numérico ou ≤ 0.
CALC_NOT_FOUND 404 O cálculo não existe.
ENGINE_REJECTED 409 O motor recusou rodar o cálculo sem dizer o porquê ({sucesso:0}). Antes isso chegava como 200 com corpo vazio de números.
INDEX_NOT_FOUND 400/404 Índice inexistente em /v1/correct ou /v1/indices/{slug}.
INDEX_NOT_TABLE 400 O campo recebeu um índice onde se espera uma tabela judicial (ou o contrário, em atualizacaoMonetaria). Um índice ali não corrige nada — a mensagem sugere tabelas válidas.
INDEX_LEGACY_ID 400 Id de tabela no formato antigo do catálogo (calcId + 1000), de antes de GET /v1/tables existir. A mensagem traz o id correto.
TABLE_NOT_FOUND 400/404 A tabela judicial não existe — nem no catálogo, nem entre as suas. Sem ela o motor devolveria o valor nominal. Liste em GET /v1/tables.
TABLE_NO_CORRECTION 422 O run de um calculosJudiciais cuja tabela não corrigiu nenhum mês (coeficiente 1, sem juros, sem SELIC). Em geral o indexador aponta para uma tabela apagada. Regrave o campo com um id de GET /v1/tables.
NO_FIELDS 400 PATCH sem fields, ou com fields vazio. Os campos vão dentro de fields: {"fields": {"dia_atualiza": "25/08/2026"}}. A resposta lista as chaves que chegaram na raiz do corpo.
CONFLICTING_FIELDS 400 indexador e tabelaJudicial juntos no mesmo atualizacaoMonetaria — os dois gravam o mesmo campo do motor.
INVALID_FIELD_FORMAT · INVALID_FIELD_VALUE 400 Campo com formato ou valor fora do schema — inclusive dentro de arrays: uma data 01/13/2019 em lista[0].dia é recusada apontando o item. A mensagem diz o esperado; o lote inteiro é recusado, sem gravar nada.
FIELD_REJECTED 409 O motor recusou o valor do campo.
ENGINE_NO_RESULT 422 O motor respondeu vazio — em geral um parâmetro que aquele tipo não suporta (um índice do catálogo que não se aplica ali). Antes isso voltava como 200 sem número nenhum.
ENGINE_ERROR 422 O motor falhou ao gravar/rodar/abrir, e a mensagem traz o erro dele. Antes isso virava espera até o timeout; hoje volta na hora.
LEGACY_CALC_READ_ONLY 409 O cálculo é de uma geração anterior da calculadora de atualização (coluna tipo = ATUA/A2). Na migração ficou guardado o demonstrativo em PDF, não os dados de entrada — não há o que rodar nem alterar. Baixe com export?format=pdf.
LEGACY_PDF_MISSING 410 Cálculo de geração anterior cujo PDF arquivado não está no servidor. Não há como reconstruí-lo.
CALC_DATA_GONE 410 O cálculo está cadastrado na conta, mas o arquivo com os dados não existe mais no servidor. Aparece na listagem (a linha da tabela ficou), e não há o que rodar nem exportar. Cálculos antigos podem ter o arquivo expurgado.
EXPORT_UNAVAILABLE 400 Formato indisponível para o tipo (a mensagem lista os disponíveis).
BAD_DOCTYPE · NO_FILE 400 Extração: docType fora da lista, ou arquivo ausente/vazio.
FILE_TOO_LARGE 413 Arquivo acima de 25 MB, corpo acima do que o parser aceita, upload cortado no limite, ou documento recusado pela IA por tamanho — a mensagem diz qual dos casos.
EXTRACT_FAILED 502 A IA recusou o documento (ilegível, formato não suportado). A mensagem traz o motivo que o motor devolveu.
BAD_JSON 400 O corpo não é um JSON válido.

Por que erro de cálculo é 4xx e não 502. Os hosts públicos ficam atrás do Cloudflare, que substitui o corpo de toda resposta 5xx pela página de erro dele. Um 502 com uma mensagem precisa dentro chegava ao cliente como error code: 502 e um link para a documentação do Cloudflare — a explicação era descartada no caminho. Por isso, desde 26/08/2026, quando o motor responde dizendo que não consegue (ENGINE_ERROR, ENGINE_NO_RESULT, ENGINE_REJECTED, TABLE_NO_CORRECTION) a resposta é 4xx, que atravessa intacta. O 502 fica para o que ele realmente descreve: motor que não respondeu. | KEY_NOT_FOUND | 404 | Chave inexistente em DELETE /v1/keys/{id}. | | 429 | 429 | Limite de uso — ver "Limites" abaixo. Pode chegar sem o envelope JSON acima quando quem barra é a borda (nginx), e não o gateway. | | INTERNAL | 500 | Erro não previsto. O requestId da resposta identifica a chamada no suporte. |

Todo erro traz docUrl. Erros de campo (INVALID_FIELD_*) dizem o formato esperado — é o bastante para a chamada seguinte já sair certa, sem tentativa e erro.

Limites (o que causa 429)

Não há cota por plano nem por endpoint. O que existe é um teto interno por usuário, na ponte que executa o cálculo sob a sua conta: 60 operações por minuto e 300 por hora. Ele existe para conter script em laço, não para racionar uso legítimo.

O que consome do teto: POST /v1/calc, PATCH, run, GET /v1/calc/{type}/{id} e exportuma unidade cada. PATCH conta 1 por chamada, não por campo, então preencher um cálculo inteiro num único PATCH com 20 campos custa o mesmo que preencher um campo só. Leitura de catálogo e de série (/v1/indices, /v1/tables, /v1/describe) não consome.

Na prática: agrupe os campos num PATCH só, e guarde o calcId em vez de recriar o cálculo. Ao tomar 429, respeite o retryAfter da resposta (em segundos) — repetir na hora só empurra a janela.