← Kapta360

Documentação · v1

API de integração da Kapta360

O relatório completo de análise de crédito de pessoa física, direto no seu sistema: em JSON tipado e no mesmo PDF do painel, com o mesmo saldo de créditos. Servidor a servidor, com webhooks e um ambiente de testes com dados fictícios.

Visão geral

Cada consulta gera o relatório completo do CPF: dados cadastrais e de contato, endereços, participações societárias, restrições financeiras, protestos, informações do Sistema de Informações de Crédito (SCR) e a classificação de risco calculada pela Kapta360. Só o relatório completo é entregue e cobrado: se alguma parte não puder ser obtida, a consulta falha e o crédito volta.

  • Endereço de produção: https://api.kapta360.com/v1 · homologação: https://api-homolog.kapta360.com/v1.
  • JSON em UTF-8, datas em ISO 8601 (UTC), valores em reais como número decimal.
  • Servidor a servidor: nunca use o token em navegador, aplicativo ou repositório de código. A API não aceita chamadas de navegador (sem CORS).
  • 1 crédito = 1 relatório completo, o mesmo saldo do painel. Os relatórios de produção também aparecem no histórico do seu painel.

Primeiros passos

  1. Peça a liberação da API ao suporte da Kapta360 (a API vem desligada em toda conta).
  2. No painel, abra Integração, leia e aceite os Termos de Uso da API.
  3. Crie um token de testes (k360_test_…): dados fictícios, sem cobrança.
  4. Confira o token com GET /v1/conta e faça a primeira consulta com um CPF da seção Ambiente de testes.
  5. Configure o webhook, trate os erros e só então crie o token de produção (k360_live_…).
Consulta completa
# 1) Cria a consulta (com o token de testes, use os CPFs da seção "Ambiente de testes")
curl -X POST https://api.kapta360.com/v1/consultas \
  -H "Authorization: Bearer $KAPTA360_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-12345" \
  -H "Prefer: wait=30" \
  -d '{"cpf":"00000000191","finalidade":"ANALISE_DE_RISCO_DE_CREDITO","autorizacaoTitular":true,"referenciaExterna":"pedido-12345"}'

# 2) Acompanha até CONCLUIDA ou FALHOU (espera até 30 s por chamada)
curl https://api.kapta360.com/v1/consultas/$CONSULTA_ID \
  -H "Authorization: Bearer $KAPTA360_TOKEN" \
  -H "Prefer: wait=30"

# 3) Baixa o PDF
curl -o relatorio.pdf https://api.kapta360.com/v1/consultas/$CONSULTA_ID/pdf \
  -H "Authorization: Bearer $KAPTA360_TOKEN"

Autenticação

Envie o token no cabeçalho Authorization: Bearer k360_…. O prefixo define o ambiente: k360_live_ (produção, cobra créditos) ou k360_test_ (testes). Um token só enxerga as consultas do próprio ambiente.

  • Escopos: consultas:criar, consultas:ler (inclui o PDF) e saldo:ler (saldo e extrato). GET /v1/conta aceita qualquer escopo.
  • Opcional: lista de IPs ou faixas permitidas por token, e validade.
  • O segredo aparece uma única vez, na criação. Em caso de suspeita de vazamento, revogue na área Integração: vale na hora.
  • Respostas: 401 para token ausente, inválido, revogado ou expirado; 403 para token válido sem permissão (IP, escopo, conta bloqueada, API desligada, termos pendentes).

Ciclo da consulta e prazos

POST /v1/consultas responde 202 com a consulta em NA_FILA e o cabeçalho Location. Ela passa a PROCESSANDO e termina em CONCLUIDA (relatório e PDF) ou FALHOU (crédito devolvido). O tempo típico é de 30 a 90 segundos.

  • Para saber quando terminou: receba o webhook, ou consulte GET /v1/consultas/{id} com Prefer: wait=N (até 60 s de espera por chamada) — o mesmo cabeçalho vale no POST.
  • Prazos máximos: 1 h até a coleta começar (na fila ou aguardando capacidade) e 20 min de coleta a partir daí. Depois disso a consulta falha (FILA_EXPIRADA ou PRAZO_ESGOTADO) e o crédito volta.
  • Se o PDF não ficar pronto em 10 min, a consulta conclui com pdf.disponivel = false e o PDF chega depois (evento consulta.pdf_disponivel).
  • Cobrança: o crédito é reservado na criação (RESERVADO), confirmado na conclusão (COBRADO) e devolvido se a consulta falhar (DEVOLVIDO). No ambiente de testes: SEM_COBRANCA.
GET /v1/consultas/{id} — concluída
{
  "id": "8b6f1d2e-3c4a-4f5b-9a8c-7d6e5f4a3b2c",
  "status": "CONCLUIDA",
  "fase": null,
  "ambiente": "live",
  "cpfMascarado": "***.456.789-**",
  "finalidade": "ANALISE_DE_RISCO_DE_CREDITO",
  "referenciaExterna": "pedido-12345",
  "criadoEm": "2026-10-19T14:02:11.000Z",
  "concluidaEm": "2026-10-19T14:03:05.000Z",
  "cobranca": {
    "situacao": "COBRADO",
    "creditos": 1,
    "reaproveitada": false,
    "saldoRestante": null
  },
  "numeroRelatorio": "2026-000123",
  "validade": {
    "expiraEm": "2027-01-17T14:03:05.000Z",
    "expirado": false
  },
  "erro": null,
  "pdf": {
    "disponivel": true,
    "url": "https://api.kapta360.com/v1/consultas/8b6f1d2e-3c4a-4f5b-9a8c-7d6e5f4a3b2c/pdf"
  },
  "relatorio": { "versao": "1", "metadados": { … }, "titular": { … }, "classificacao": { … }, "restricoes": { … }, "scr": { … }, "avisos": [] }
}

Idempotência e repetição segura

Todo POST /v1/consultas exige Idempotency-Key (8 a 64 caracteres entre letras, números e . _ : -), única por pedido e válida por 7 dias. Repetir com a mesma chave devolve a mesma consulta, com Idempotent-Replayed: true, sem cobrar de novo. A mesma chave com outro corpo é recusada (IDEMPOTENCY_KEY_REUTILIZADA).

Só é resposta da API um 2xx em JSON ou um erro no envelope { "erro": { … } }. Respostas 502, 503, 504 ou 52x sem esse envelope vêm da borda e são transitórias: repita com espera crescente e a mesma Idempotency-Key. Nunca use o CPF na chave nem em referenciaExterna: o CPF do titular em qualquer formato, ou qualquer sequência de exatamente 11 dígitos que forme um CPF válido, é recusado (REFERENCIA_COM_DADO_PESSOAL).

Já existe consulta em andamento para o mesmo CPF (com outra chave)? A resposta é 409 CONSULTA_EM_ANDAMENTO com o id dela em erro.detalhes.consultaId, sem cobrança.

Reaproveitamento

Envie reaproveitarDias (1 a 7) para receber, sem cobrar, um relatório completo do mesmo CPF gerado pela API para a sua conta nesse período: a resposta é 200 imediato, com o mesmo número de relatório e o mesmo PDF, e cobranca.reaproveitada = true. Sem relatório recente, segue como uma consulta nova (cobrada).

Endpoints

GET/v1/conta

Conta, token em uso, limites e termos

Qualquer escopo. Útil para conferir a configuração da integração.

  • 200 Dados da conta.
  • 401 Token ausente, inválido, revogado ou expirado. Códigos possíveis: NAO_AUTENTICADO, TOKEN_REVOGADO, TOKEN_EXPIRADO.
  • 403 Token válido sem permissão para esta operação. Códigos possíveis: IP_NAO_PERMITIDO, CONTA_BLOQUEADA, API_DESABILITADA, TERMOS_PENDENTES, AMBIENTE_INDISPONIVEL, ESCOPO_INSUFICIENTE.
  • 429 Limite excedido. Códigos possíveis: LIMITE_EXCEDIDO, MUITAS_FALHAS_DE_AUTENTICACAO.
  • 503 Indisponível no momento; repita após o Retry-After. Códigos possíveis: API_INDISPONIVEL, SERVICO_INDISPONIVEL.

POST/v1/consultas

Cria uma consulta (relatório completo)

Escopo consultas:criar. Debita 1 crédito na criação (produção); se o relatório não ficar completo, o crédito volta. Repita com a MESMA Idempotency-Key em caso de falha de rede: a resposta é a mesma consulta (Idempotent-Replayed: true).

ParâmetroOndeObrigatórioDescrição
Idempotency-Keycabeçalhosim8 a 64 caracteres [A-Za-z0-9._:-], única por pedido (validade de 7 dias). Nunca o CPF.
Prefercabeçalhonãowait=N (0 a 60): espera até N segundos pelo status final antes de responder.
  • 200 Concluída/falhou dentro da espera, ou reaproveitada.
  • 202 Aceita e na fila; acompanhe pelo Location (ou pelo webhook).
  • 400 Pedido malformado. Códigos possíveis: IDEMPOTENCY_KEY_OBRIGATORIA, CORPO_INVALIDO.
  • 401 Token ausente, inválido, revogado ou expirado. Códigos possíveis: NAO_AUTENTICADO, TOKEN_REVOGADO, TOKEN_EXPIRADO.
  • 402 Sem créditos. Códigos possíveis: SALDO_INSUFICIENTE.
  • 403 Token válido sem permissão para esta operação. Códigos possíveis: IP_NAO_PERMITIDO, CONTA_BLOQUEADA, API_DESABILITADA, TERMOS_PENDENTES, AMBIENTE_INDISPONIVEL, ESCOPO_INSUFICIENTE.
  • 409 Já existe consulta em andamento para este CPF (detalhes.consultaId). Códigos possíveis: CONSULTA_EM_ANDAMENTO.
  • 422 Dados recusados. Códigos possíveis: DADOS_INVALIDOS, CPF_INVALIDO, CPF_FORA_DO_AMBIENTE_DE_TESTES, FINALIDADE_INVALIDA, AUTORIZACAO_DO_TITULAR_OBRIGATORIA, REFERENCIA_COM_DADO_PESSOAL, IDEMPOTENCY_KEY_REUTILIZADA.
  • 429 Limite excedido. Códigos possíveis: LIMITE_EXCEDIDO, MUITAS_FALHAS_DE_AUTENTICACAO.
  • 503 Indisponível ou sem capacidade; repita após o Retry-After. Códigos possíveis: API_INDISPONIVEL, SERVICO_INDISPONIVEL, CAPACIDADE_ESGOTADA.

GET/v1/consultas

Lista as consultas do ambiente do token

Escopo consultas:ler. Mais recentes primeiro, com cursor. Sem o relatório (use GET /consultas/{id}).

ParâmetroOndeObrigatórioDescrição
statusquerynão—
cobrancaquerynão—
tokenquerynãoParte pública do token.
referenciaExternaquerynão—
dequerynão—
atequerynão—
limitequerynão—
cursorquerynão—
  • 200 Página de consultas.
  • 401 Token ausente, inválido, revogado ou expirado. Códigos possíveis: NAO_AUTENTICADO, TOKEN_REVOGADO, TOKEN_EXPIRADO.
  • 403 Token válido sem permissão para esta operação. Códigos possíveis: IP_NAO_PERMITIDO, CONTA_BLOQUEADA, API_DESABILITADA, TERMOS_PENDENTES, AMBIENTE_INDISPONIVEL, ESCOPO_INSUFICIENTE.
  • 422 Filtro inválido. Códigos possíveis: DADOS_INVALIDOS, REFERENCIA_COM_DADO_PESSOAL.
  • 429 Limite excedido. Códigos possíveis: LIMITE_EXCEDIDO, MUITAS_FALHAS_DE_AUTENTICACAO.
  • 503 Indisponível no momento; repita após o Retry-After. Códigos possíveis: API_INDISPONIVEL, SERVICO_INDISPONIVEL.

GET/v1/consultas/{id}

Status e, quando concluída, o relatório v1

Escopo consultas:ler. Consulta de outro cliente ou de outro ambiente: 404.

ParâmetroOndeObrigatórioDescrição
idcaminhosim—
Prefercabeçalhonãowait=N (0 a 60): espera até N segundos pelo status final antes de responder.
  • 200 A consulta.
  • 401 Token ausente, inválido, revogado ou expirado. Códigos possíveis: NAO_AUTENTICADO, TOKEN_REVOGADO, TOKEN_EXPIRADO.
  • 403 Token válido sem permissão para esta operação. Códigos possíveis: IP_NAO_PERMITIDO, CONTA_BLOQUEADA, API_DESABILITADA, TERMOS_PENDENTES, AMBIENTE_INDISPONIVEL, ESCOPO_INSUFICIENTE.
  • 404 Consulta inexistente para este token. Códigos possíveis: NAO_ENCONTRADO.
  • 429 Limite excedido. Códigos possíveis: LIMITE_EXCEDIDO, MUITAS_FALHAS_DE_AUTENTICACAO.
  • 503 Indisponível no momento; repita após o Retry-After. Códigos possíveis: API_INDISPONIVEL, SERVICO_INDISPONIVEL.

GET/v1/consultas/{id}/pdf

PDF do relatório (o mesmo da impressão do painel)

Escopo consultas:ler. Disponível quando pdf.disponivel = true na consulta. A ETag é o SHA-256 do arquivo: envie If-None-Match para receber 304 sem baixar de novo. Consulta que falhou: 404.

ParâmetroOndeObrigatórioDescrição
idcaminhosim—
If-None-Matchcabeçalhonão—
  • 200 O PDF (A4).
  • 304 Não mudou desde a ETag enviada.
  • 401 Token ausente, inválido, revogado ou expirado. Códigos possíveis: NAO_AUTENTICADO, TOKEN_REVOGADO, TOKEN_EXPIRADO.
  • 403 Token válido sem permissão para esta operação. Códigos possíveis: IP_NAO_PERMITIDO, CONTA_BLOQUEADA, API_DESABILITADA, TERMOS_PENDENTES, AMBIENTE_INDISPONIVEL, ESCOPO_INSUFICIENTE.
  • 404 Consulta inexistente para este token, ou que falhou. Códigos possíveis: NAO_ENCONTRADO.
  • 409 Ainda em processamento ou com o PDF sendo gerado. Códigos possíveis: PDF_EM_GERACAO.
  • 410 Passou o prazo de retenção do resultado. Códigos possíveis: RESULTADO_EXPIRADO.
  • 429 Limite excedido. Códigos possíveis: LIMITE_EXCEDIDO, MUITAS_FALHAS_DE_AUTENTICACAO.
  • 503 Indisponível no momento; repita após o Retry-After. Códigos possíveis: API_INDISPONIVEL, SERVICO_INDISPONIVEL.

GET/v1/extrato

Consumos e estornos de créditos das consultas da API

Escopo saldo:ler. No ambiente de testes a lista é vazia (amostra: true).

ParâmetroOndeObrigatórioDescrição
limitequerynão—
cursorquerynão—
  • 200 Lançamentos.
  • 401 Token ausente, inválido, revogado ou expirado. Códigos possíveis: NAO_AUTENTICADO, TOKEN_REVOGADO, TOKEN_EXPIRADO.
  • 403 Token válido sem permissão para esta operação. Códigos possíveis: IP_NAO_PERMITIDO, CONTA_BLOQUEADA, API_DESABILITADA, TERMOS_PENDENTES, AMBIENTE_INDISPONIVEL, ESCOPO_INSUFICIENTE.
  • 429 Limite excedido. Códigos possíveis: LIMITE_EXCEDIDO, MUITAS_FALHAS_DE_AUTENTICACAO.
  • 503 Indisponível no momento; repita após o Retry-After. Códigos possíveis: API_INDISPONIVEL, SERVICO_INDISPONIVEL.

GET/v1/saldo

Créditos disponíveis

Escopo saldo:ler. No ambiente de testes o saldo é fictício (amostra: true).

  • 200 Saldo de créditos.
  • 401 Token ausente, inválido, revogado ou expirado. Códigos possíveis: NAO_AUTENTICADO, TOKEN_REVOGADO, TOKEN_EXPIRADO.
  • 403 Token válido sem permissão para esta operação. Códigos possíveis: IP_NAO_PERMITIDO, CONTA_BLOQUEADA, API_DESABILITADA, TERMOS_PENDENTES, AMBIENTE_INDISPONIVEL, ESCOPO_INSUFICIENTE.
  • 429 Limite excedido. Códigos possíveis: LIMITE_EXCEDIDO, MUITAS_FALHAS_DE_AUTENTICACAO.
  • 503 Indisponível no momento; repita após o Retry-After. Códigos possíveis: API_INDISPONIVEL, SERVICO_INDISPONIVEL.

Corpo do POST: cpf (com ou sem pontuação), finalidade, autorizacaoTitular: true, e opcionais referenciaExterna e reaproveitarDias. Esquemas completos no OpenAPI.

Relatório v1 (dicionário)

Quando a consulta conclui, relatorio traz os grupos versao, metadados, titular, contatos, enderecos, participacoesSocietarias, indicadores, classificacao, restricoes, scr, avisos. Campos sem informação vêm como null (nunca omitidos); listas vazias, como []. Ausência de registros é resultado válido e vem com aviso em avisos[] (ex.: SCR_SEM_RELACIONAMENTO). Ignore campos novos que aparecerem: eles podem ser acrescentados sem aviso.

CampoTipoDescrição
versaotexto
metadadosobjeto
metadados.numerotextoNúmero do relatório (AMOSTRA-… no ambiente de testes).
metadados.geradoEmdata-hora
metadados.ambientelive | test
metadados.finalidadeANALISE_DE_RISCO_DE_CREDITO | CONCESSAO_OU_EXTENSAO_DE_CREDITO | VENDA_A_PRAZO_OU_TRANSACAO_COM_RISCO_FINANCEIRO
metadados.amostrabooleanotrue no ambiente de testes: dados fictícios.
titularobjeto
titular.nometexto | null
titular.cpftexto
titular.dataNascimentodata (AAAA-MM-DD) | null
titular.idadeinteiro | null
titular.situacaoCadastraltexto | null
titular.nomeMaetexto | null
titular.sexotexto | null
titular.estadoCiviltexto | null
titular.classeSocialtexto | null
titular.obitobooleano | null
contatosobjeto
contatos.telefoneslista
contatos.emailslista
enderecoslista
enderecos[].tipotexto | nullNatureza do endereço quando informada (ex.: Residencial, Trabalho).
enderecos[].logradourotexto | null
enderecos[].numerotexto | null
enderecos[].complementotexto | null
enderecos[].bairrotexto | null
enderecos[].cidadetexto | null
enderecos[].uftexto | null
enderecos[].ceptexto | null
participacoesSocietariaslista
participacoesSocietarias[].cnpjtexto | null
participacoesSocietarias[].razaoSocialtexto | null
participacoesSocietarias[].qualificacaotexto | null
participacoesSocietarias[].participacaoPercentualnúmero | null
participacoesSocietarias[].dataEntradadata (AAAA-MM-DD) | null
indicadoresobjeto
indicadores.scorenúmero | nullScore de crédito (0 a 1000).
indicadores.probabilidadeInadimplenciaPercentualnúmero | null
indicadores.rendaPresumidaFaixatexto | null
indicadores.pontualidadePagamentosPercentualnúmero | null
classificacaoobjeto
classificacao.statusOK | INDISPONIVEL
classificacao.motivoCodigoSCORE_NAO_CALCULADO | PD_NAO_CALCULADA | BASE_NAO_APURADA | null
classificacao.letraAAA | AA | A | BBB | BB | B | CCC | CC | C | D | E | null
classificacao.grupotexto | null
classificacao.leituratexto | null
classificacao.conclusaotexto | null
classificacao.resumotexto
classificacao.letraBasetexto | null
classificacao.ajusteslista
classificacao.ajustes[].indicadortexto
classificacao.ajustes[].efeito-1 | 0 | 1
classificacao.ajustes[].motivotexto
classificacao.somaAjustesinteiro
classificacao.versaoMotortexto
classificacao.alertatexto | null
restricoesobjeto
restricoes.semRestricoesbooleano
restricoes.quadrolista
restricoes.quadro[].itemtexto
restricoes.quadro[].situacaoNADA_CONSTA | CONSTA | NAO_APURADO
restricoes.quadro[].descricaotexto
restricoes.ocorrenciaslista
restricoes.ocorrencias[].tipotexto
restricoes.ocorrencias[].origemtexto
restricoes.ocorrencias[].datadata (AAAA-MM-DD) | null
restricoes.ocorrencias[].valornúmero | null
restricoes.ocorrencias[].detalhetexto | null
scrobjeto
scr.situacaoCOM_RELACIONAMENTO | SEM_RELACIONAMENTO
scr.dataBasemês (AAAA-MM) | nullMês de referência (AAAA-MM).
scr.inicioRelacionamentodata (AAAA-MM-DD) | null
scr.quantidadeInstituicoesinteiro | null
scr.quantidadeOperacoesinteiro | null
scr.scoretexto | null
scr.carteiraobjeto
scr.carteira.aVencernúmero | null
scr.carteira.vencidonúmero | null
scr.carteira.prejuizonúmero | null
scr.carteira.limitenúmero | null
scr.modalidadeslista
scr.modalidades[].titulotexto
scr.modalidades[].linhaslista
avisoslista
avisos[].codigotexto
avisos[].mensagemtexto

PDF

GET /v1/consultas/{id}/pdf devolve o mesmo relatório da impressão do painel (A4), com o nome kapta360-<numero>.pdf. Use quando pdf.disponivel = true. Ainda em geração: 409 PDF_EM_GERACAO com Retry-After. A ETag é o SHA-256 do arquivo: com If-None-Match, a resposta é 304. No ambiente de testes o PDF leva a marca d'água "AMOSTRA — DADOS FICTÍCIOS".

Webhooks

Configure na área Integração um endereço https:// por ambiente e os eventos: consulta.concluida, consulta.falhou, consulta.pdf_disponivel e saldo.baixo (só produção, a partir do limite de créditos que você escolher). O botão de teste envia webhook.teste. O corpo não traz o relatório: busque-o pelo link.

POST no seu endereço — consulta.concluida
{
  "id": "c0a8012e-7f3d-4b1a-9e2c-5d4f3a2b1c0d",
  "evento": "consulta.concluida",
  "criadoEm": "2026-10-19T14:03:06.000Z",
  "ambiente": "live",
  "dados": {
    "consulta": {
      "id": "8b6f1d2e-3c4a-4f5b-9a8c-7d6e5f4a3b2c",
      "status": "CONCLUIDA",
      "numeroRelatorio": "2026-000123",
      "cpfMascarado": "***.456.789-**",
      "referenciaExterna": "pedido-12345",
      "cobranca": "COBRADO",
      "reaproveitada": false,
      "erro": null,
      "pdf": {
        "disponivel": true
      },
      "links": {
        "consulta": "https://api.kapta360.com/v1/consultas/8b6f1d2e-3c4a-4f5b-9a8c-7d6e5f4a3b2c",
        "pdf": "https://api.kapta360.com/v1/consultas/8b6f1d2e-3c4a-4f5b-9a8c-7d6e5f4a3b2c/pdf"
      }
    }
  }
}
  • Cabeçalhos: Kapta360-Assinatura, Kapta360-Evento, Kapta360-Entrega (id desta entrega) e Kapta360-Tentativa.
  • Responda 2xx em até 10 s. Redirecionamentos não são seguidos. O endereço precisa ser um nome de domínio público (IPs internos são recusados).
  • Entrega pelo menos uma vez: o mesmo evento pode chegar mais de uma vez — deduplique pelo id do corpo (o reenvio manual mantém o mesmo id).
  • Sem 2xx, repetimos (até 11 tentativas, com até 10% de variação nos intervalos):
TentativaQuandoTempo desde a primeira
1na hora—
230 s depois da anterior30 s
32 min depois da anterior2,5 min
410 min depois da anterior12,5 min
530 min depois da anterior42,5 min
61 h depois da anterior1 h 43 min
72 h depois da anterior3 h 43 min
83 h depois da anterior6 h 43 min
94 h depois da anterior10 h 43 min
105 h depois da anterior15 h 43 min
117 h depois da anterior22 h 43 min
  • Com 50 falhas seguidas e nenhum sucesso em 72 h, o webhook é desativado; corrija, teste e reative na área Integração (eventos perdidos podem ser reenviados de lá).
  • Conta bloqueada ou API desligada: as entregas ficam suspensas e voltam se a conta for liberada em até 72 h.

Confira a assinatura antes de usar o corpo. Kapta360-Assinatura: t=<unix>,v1=<hex>, onde v1 é o HMAC-SHA256 do texto "<t>.<corpo cru>" com o segredo do webhook (k360_whsec_…, visível na área Integração). Recuse t com mais de 300 s de diferença do seu relógio. Pode haver mais de um v1: aceite se algum conferir.

Verificação da assinatura
const crypto = require("node:crypto");

/** true se o corpo veio da Kapta360. "corpo" é o texto CRU recebido, antes de qualquer JSON.parse. */
function webhookValido(corpo, cabecalho, segredo, toleranciaSeg = 300) {
  const partes = String(cabecalho ?? "").split(",").map((p) => p.trim());
  const t = Number(partes.find((p) => p.startsWith("t="))?.slice(2));
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > toleranciaSeg) return false;
  const esperado = Buffer.from(crypto.createHmac("sha256", segredo).update(`${t}.${corpo}`).digest("hex"));
  return partes
    .filter((p) => p.startsWith("v1="))
    .map((p) => Buffer.from(p.slice(3)))
    .some((r) => r.length === esperado.length && crypto.timingSafeEqual(r, esperado));
}
No seu endpoint
// Express: o corpo precisa chegar CRU para a assinatura conferir.
app.post("/webhooks/kapta360", express.raw({ type: "application/json" }), (req, res) => {
  const corpo = req.body.toString("utf8");
  if (!webhookValido(corpo, req.get("Kapta360-Assinatura"), process.env.KAPTA360_WEBHOOK_SEGREDO)) {
    return res.sendStatus(400);
  }
  const evento = JSON.parse(corpo);
  // Deduplique por evento.id (pode chegar mais de uma vez), enfileire o trabalho e responda logo.
  res.sendStatus(204);
});

Erros

Todo erro vem no envelope abaixo, com um codigo estável (use-o no seu código, não a mensagem) e o requestId — informe-o ao suporte. O mesmo id volta no cabeçalho X-Request-Id.

Envelope de erro
{
  "erro": {
    "codigo": "SALDO_INSUFICIENTE",
    "mensagem": "Créditos insuficientes. Compre créditos no painel.",
    "requestId": "req_2a216d2d7a1d41529024492ec4ae4113"
  }
}
CódigoHTTPMensagem
CORPO_INVALIDO400Corpo da requisição inválido (JSON esperado).
IDEMPOTENCY_KEY_OBRIGATORIA400Envie o cabeçalho Idempotency-Key (8 a 64 caracteres).
NAO_AUTENTICADO401Token ausente ou inválido. Envie Authorization: Bearer <token>.
TOKEN_EXPIRADO401Este token expirou. Crie outro na área Integração do painel.
TOKEN_REVOGADO401Este token foi revogado. Crie outro na área Integração do painel.
SALDO_INSUFICIENTE402Créditos insuficientes. Compre créditos no painel.
AMBIENTE_INDISPONIVEL403O ambiente de testes não está liberado para esta conta.
API_DESABILITADA403A API não está liberada para esta conta. Fale com o suporte.
CONTA_BLOQUEADA403A conta está bloqueada. Fale com o suporte.
ESCOPO_INSUFICIENTE403Este token não tem o escopo necessário para esta operação.
IP_NAO_PERMITIDO403O IP de origem não está na lista de IPs permitidos deste token.
TERMOS_PENDENTES403Aceite os Termos de Uso da API na área Integração do painel.
NAO_ENCONTRADO404Recurso não encontrado.
METODO_NAO_PERMITIDO405Método não permitido neste endereço.
CONSULTA_EM_ANDAMENTO409Já existe uma consulta em andamento para este CPF.
PDF_EM_GERACAO409O PDF ainda está sendo gerado. Tente de novo em instantes.
RESULTADO_EXPIRADO410O resultado desta consulta não está mais disponível (prazo de retenção).
CORPO_GRANDE_DEMAIS413Corpo da requisição grande demais.
AUTORIZACAO_DO_TITULAR_OBRIGATORIA422Declare que tem a autorização do titular (autorizacaoTitular: true).
CPF_FORA_DO_AMBIENTE_DE_TESTES422No ambiente de testes, use um dos CPFs de exemplo da documentação.
CPF_INVALIDO422CPF inválido.
DADOS_INVALIDOS422Dados inválidos.
FINALIDADE_INVALIDA422Informe uma finalidade permitida (veja a documentação).
IDEMPOTENCY_KEY_REUTILIZADA422Esta Idempotency-Key já foi usada com outro corpo.
REFERENCIA_COM_DADO_PESSOAL422A referência externa e a Idempotency-Key não podem conter CPF.
LIMITE_EXCEDIDO429Limite de requisições excedido. Aguarde o tempo indicado em Retry-After.
MUITAS_FALHAS_DE_AUTENTICACAO429Muitas tentativas com credencial inválida. Aguarde e tente de novo.
ERRO_INTERNO500Erro interno. Tente de novo; se persistir, informe o requestId ao suporte.
API_INDISPONIVEL503A API está temporariamente indisponível.
CAPACIDADE_ESGOTADA503Capacidade de processamento esgotada no momento. Tente de novo após o Retry-After.
SERVICO_INDISPONIVEL503Serviço temporariamente indisponível. Tente de novo após o Retry-After.

Motivos de falha de uma consulta (erro.codigo quando status = FALHOU; o crédito é sempre devolvido):

CódigoSignificado
BASES_INDISPONIVEISNão foi possível obter o relatório completo. Nenhum crédito foi cobrado.
PRAZO_ESGOTADOA consulta não terminou dentro do prazo. Nenhum crédito foi cobrado; tente de novo mais tarde.
FILA_EXPIRADAA consulta ficou tempo demais na fila. Nenhum crédito foi cobrado; tente de novo.
CONTA_BLOQUEADAA conta foi bloqueada antes do processamento. Nenhum crédito foi cobrado.

Limites

LimitePadrão
Criações de consulta por minuto10
Leituras por minuto (GET)120
Consultas em andamento ao mesmo tempo10
Tokens ativos5

Os limites são por conta e por ambiente e podem ser ajustados pelo suporte; os seus aparecem em GET /v1/conta. Acima deles: 429 LIMITE_EXCEDIDO com Retry-After. Em picos de demanda, a criação pode responder 503 CAPACIDADE_ESGOTADA com Retry-After (sem cobrança). Tokens desconhecidos: 20 tentativas em 10 min bloqueiam o IP por 15 min.

Ambiente de testes

Com um token k360_test_, o caminho é o mesmo da produção (fila, processamento, PDF, webhooks), mas com dados fictícios, sem cobrança, sem entrar no histórico do painel e com número de relatório AMOSTRA-…. O saldo e o extrato são fictícios (amostra: true). Só estes CPFs são aceitos; outros recebem 422 CPF_FORA_DO_AMBIENTE_DE_TESTES:

CPFCenário
000.000.001-91Relatório completo, sem restrições.
000.000.002-72Relatório completo, com pendências financeiras e protesto.
000.000.003-53Relatório completo de quem não tem histórico no SCR (vem com aviso).
000.000.004-34Uma base não responde: a consulta falha e o crédito não é cobrado.
000.000.005-15Relatório completo; o PDF fica pronto alguns instantes depois.

LGPD e termos

  • Toda consulta declara a finalidade, uma de: ANALISE_DE_RISCO_DE_CREDITO (análise de risco de crédito); CONCESSAO_OU_EXTENSAO_DE_CREDITO (concessão ou extensão de crédito); VENDA_A_PRAZO_OU_TRANSACAO_COM_RISCO_FINANCEIRO (venda a prazo ou outra transação com risco financeiro) (Lei nº 12.414/2011), e autorizacaoTitular: true, confirmando que você obteve a autorização do titular e guarda a prova.
  • Você e a Kapta360 atuam como controladoras independentes. É vedado revender, compartilhar ou usar os dados para outra finalidade. Leia os Termos de Uso da API e a Política de Privacidade.
  • O titular pode pedir a revisão da classificação de risco, que é automatizada (art. 20 da LGPD), pelo canal indicado na Política de Privacidade.
DadoRetenção
Relatório, CPF e PDF nas consultas da API90 dias após a geração (depois: só número, status e cobrança)
Cópia no histórico do painel (produção)até a sua empresa apagar
Registro de requisições90 dias
Idempotency-Key7 dias
Entregas de webhook90 dias
Trilha de auditoria5 anos

Versionamento

A versão está no caminho (/v1) e no relatório (relatorio.versao = "1"). Na v1 só entram mudanças compatíveis: novos campos, eventos, códigos de erro e endpoints — trate o desconhecido sem quebrar. Mudanças incompatíveis vêm numa nova versão, com aviso de pelo menos 6 meses antes de a v1 ser desligada.

Suporte

Fale com o suporte pelo canal do painel e informe o requestId da resposta (ou o id da consulta). Nunca envie o token nem o segredo do webhook: o suporte não precisa deles.