Pular para o conteúdo

Margem consignável por API

Consulte margem, reserve e averbe — com autorização do titular em cada operação
Comece pelo sandbox. A chave de teste não alcança dado de ninguém e responde com servidores fictícios determinísticos. O mesmo CPF devolve sempre a mesma margem, o que permite escrever teste automatizado sem depender de estado.

Primeira chamada em menos de cinco minutos

  1. Crie uma conta de consignatária e peça credenciamento no município.
  2. No console, gere uma chave de sandbox.
  3. Rode o comando abaixo com um dos CPFs de teste.
  4. Quando estiver funcionando, gere a chave de produção. Nada mais muda.
export COCLE_API_KEY=cocle_test_...

curl -X POST https://api.cocle.com.br/v1/margens/consultas \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cpf":"11144477735","finalidade":"teste de integração"}'

Servidores de teste

Cada um exercita um caminho que a sua integração precisa tratar — e que, em produção, você descobriria com um cliente real na linha.

Servidores do ambiente de teste
CPFSituaçãoMargemO que exercita
11144477735ativoR$ 1.250,00Caminho feliz: margem ampla, averbação aprovada.
22255588846ativoR$ 42,75Margem quase esgotada — exercita recusa por margem insuficiente.
33366699957ativoMargem bloqueada pelo titular — responde como se não existisse.
44477700083afastadoServidor afastado: inelegível, e a parcela não é descontada no retorno.
55588811194ativoR$ 780,50Dois vínculos no mesmo município — a resposta traz margem por vínculo.

Use 99988877714 para exercitar o 404.

Endpoints

Esta seção é gerada do mesmo contrato que gera o documento OpenAPI 3.1 e o cliente TypeScript, e uma verificação compara os caminhos declarados com os que a API de fato registrou. Documentação desatualizada aqui reprova o build.

POST /v1/margens/consultas

Consultar a margem disponível de um CPF

Responde 404 tanto para CPF que não existe quanto para titular que bloqueou a margem ou se opôs ao uso dos dados. A indistinção é deliberada: um erro distinguível permitiria montar a lista de quem recusou oferta.

curl -X POST https://api.cocle.com.br/v1/margens/consultas \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cpf": "11144477735",
    "finalidade": "oferta de crédito consignado"
  }'

Resposta 200 — Vínculos encontrados, com a margem de cada um.

{
  "consulta_id": "cns_01J8Z",
  "cpf_mascarado": "***.444.777-**",
  "competencia_referencia": "2026-09",
  "vinculos": [
    {
      "vinculo_id": "vnc_01J8Z",
      "municipio": {
        "codigo_ibge": "4204608",
        "nome": "Município de Exemplo"
      },
      "orgao": "Secretaria Municipal de Educação",
      "regime": "ESTATUTARIO",
      "situacao": "ATIVO",
      "elegivel": true,
      "margens": [
        {
          "bucket": "EMPRESTIMO",
          "disponivelEfetivo": 1250
        }
      ]
    }
  ],
  "billing": {
    "evento": "CONSULTA_MARGEM",
    "faturavel": true,
    "valor": 0.45
  }
}

POST /v1/reservas

aceita Idempotency-Key

Reservar margem por tempo determinado

Segura a margem enquanto a proposta é analisada. Envie sempre Idempotency-Key: sem ela, uma retentativa de rede vira duas reservas e consome margem em dobro do mesmo servidor.

curl -X POST https://api.cocle.com.br/v1/reservas \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "vinculo_id": "vnc_...",
    "consignataria_id": "...",
    "bucket": "EMPRESTIMO",
    "valor_parcela": 250.00
  }'

Resposta 201 — Reserva criada.

{
  "reserva_id": "rsv_01J8Z",
  "margem_restante": 1000,
  "status": "VIGENTE"
}

Erros próprios deste endpoint: MARGEM_INSUFICIENTE, RESERVAS_SIMULTANEAS_EXCEDIDAS

POST /v1/autorizacoes

Pedir a autorização do titular

Passo obrigatório entre reservar e averbar. As condições informadas aqui são as que o servidor vê no portal dele, e são conferidas por hash na averbação — divergência de taxa, prazo ou valor faz a averbação ser recusada.

curl -X POST https://api.cocle.com.br/v1/autorizacoes \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reserva_id": "...",
    "valor_financiado": 12000.00,
    "valor_parcela": 250.00,
    "qtd_parcelas": 60,
    "taxa_am": 1.65,
    "cet_aa": 21.70
  }'

Resposta 201 — Pedido registrado. O titular decide no Portal do Servidor.

{
  "autorizacao_id": "atz_01J8Z",
  "status": "PENDENTE"
}

Erros próprios deste endpoint: RESERVA_EXPIRADA, AUTORIZACAO_JA_EXISTE

POST /v1/averbacoes

aceita Idempotency-Key

Efetivar a averbação

Só passa com autorização APROVADA pelo titular e com as condições idênticas às autorizadas. A resposta traz a memória de cálculo do success fee, para conferir a cobrança no ato e não trinta dias depois.

curl -X POST https://api.cocle.com.br/v1/averbacoes \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "reserva_id": "...",
    "numero_contrato": "CT-2026-0001",
    "valor_financiado": 12000.00,
    "valor_parcela": 250.00,
    "qtd_parcelas": 60,
    "taxa_am": 1.65,
    "cet_aa": 21.70
  }'

Resposta 201 — Contrato averbado. O desconto entra na próxima folha.

{
  "contrato_id": "ctr_01J8Z",
  "status": "AVERBADA",
  "parcelas": 60,
  "billing": {
    "evento": "AVERBACAO_EFETIVADA",
    "base_calculo": "VALOR_FINANCIADO",
    "valor_base": 12000,
    "aliquota": 0.01,
    "valor": 120
  }
}

Erros próprios deste endpoint: AUTORIZACAO_AUSENTE, CONDICOES_DIVERGEM_DA_AUTORIZACAO, RESERVA_EXPIRADA

POST /v1/folha/remessas

Abrir a remessa de uma competência

Primeiro passo do envio da folha. A remessa é área de espera: os lotes se acumulam nela e nada vira contracheque até o fechamento. Reabrir com a mesma referência devolve a remessa que já existe em vez de criar outra — repetir a abertura depois de um timeout é o primeiro comportamento de qualquer integrador, e duas remessas com metade dos lotes cada é exatamente a meia folha que o desenho evita.

curl -X POST https://api.cocle.com.br/v1/folha/remessas \
  -H "Authorization: Bearer $COCLE_ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "competencia": "2026-03", "referencia": "folha-mensal-001" }'

Resposta 201 — Remessa aberta, pronta para receber lotes.

{
  "remessa_id": "rmf_01J8Z",
  "status": "ABERTA",
  "competencia": "2026-03",
  "max_linhas_por_lote": 1000,
  "max_lotes": 200
}

Erros próprios deste endpoint: COMPETENCIA_INVALIDA, REMESSA_JA_FECHADA

POST /v1/folha/remessas/:id/lotes

Enviar um lote de contracheques

O lote é numerado por você, a partir de 1. Reenviar o mesmo número com o mesmo conteúdo é aceito em silêncio e não soma duas vezes — é a retentativa normal de quem integra. O mesmo número com conteúdo diferente é recusado, porque aceitar calado deixaria a divergência para o fechamento, longe de quem poderia corrigi-la. Linha com erro de formato é isolada e devolvida em `problemas`; o restante do lote entra. Uma matrícula com CPF digitado errado no cadastro não pode segurar a folha dos outros 3.999.

curl -X POST https://api.cocle.com.br/v1/folha/remessas/$REMESSA/lotes \
  -H "Authorization: Bearer $COCLE_ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lote": 1, "vinculos": [ ... ] }'

Resposta 202 — Lote recebido na área de espera. Nada virou contracheque ainda.

{
  "aceitas": 1,
  "recusadas": 0,
  "problemas": [],
  "remessa": {
    "vinculos": 1,
    "lotes_recebidos": 1,
    "total_proventos": 4200,
    "total_descontos": 462
  }
}

Erros próprios deste endpoint: LOTE_DIVERGENTE, LOTE_GRANDE, LOTE_VAZIO, LOTE_INVALIDO, REMESSA_JA_FECHADA

POST /v1/folha/remessas/:id/fechamento

Fechar a remessa e aplicar a folha

Avalia a remessa inteira contra a competência anterior e, se passar, aplica numa transação só. Antes de aplicar há um freio de mudança em massa. Queda de mais de 10% nos vínculos, de mais de 15% nos proventos, crescimento de mais de 50% ou mais de 5% das linhas recusadas devolvem `status: BLOQUEADA` e a folha **não** é aplicada — a remessa fica esperando uma pessoa do município liberar, com justificativa. Não é conservadorismo: uma exportação quebrada que traz 40 servidores em vez de 4.000 zera a margem de quase toda a prefeitura numa chamada HTTP. Fechar de novo uma remessa já aplicada devolve o mesmo resultado sem reaplicar.

curl -X POST https://api.cocle.com.br/v1/folha/remessas/$REMESSA/fechamento \
  -H "Authorization: Bearer $COCLE_ERP_KEY"

Resposta 200 — Remessa avaliada. Veja `status`: APLICADA ou BLOQUEADA.

{
  "status": "APLICADA",
  "avaliacao": {
    "vinculos_agora": 612,
    "vinculos_antes": 610,
    "competencia_comparada": "2026-02",
    "variacao_vinculos": 0.0033,
    "variacao_proventos": 0.0089,
    "proporcao_problemas": 0,
    "alertas": []
  }
}

Erros próprios deste endpoint: REMESSA_VAZIA, REMESSA_JA_FECHADA

GET /v1/folha/remessas/:id

Consultar a situação da remessa

Traz status, totais, a lista de problemas linha a linha e a avaliação do freio. É o endereço para descobrir por que a folha não entrou às duas da manhã, sem abrir chamado.

curl https://api.cocle.com.br/v1/folha/remessas/$REMESSA \
  -H "Authorization: Bearer $COCLE_ERP_KEY"

Resposta 200 — Situação da remessa.

{
  "remessa_id": "rmf_01J8Z",
  "status": "APLICADA",
  "competencia": "2026-03",
  "referencia": "folha-mensal-001",
  "vinculos": 612,
  "itens": 4284,
  "lotes_recebidos": 1,
  "total_proventos": 2741320.55,
  "total_descontos": 611004.12,
  "problemas": [],
  "avaliacao": null,
  "motivo": null
}

Erros

Códigos de erro
HTTPCódigoQuando acontece
401UNAUTHENTICATEDChave ausente, inválida ou revogada.
403FORBIDDENA chave não alcança este município ou esta operação.
404NOT_FOUNDCPF sem vínculo, margem bloqueada pelo titular, ou oposição registrada. Não distinga os casos: a resposta é a mesma de propósito.
409MARGEM_INSUFICIENTEA parcela pedida não cabe na margem disponível.
409AUTORIZACAO_AUSENTENão há autorização aprovada pelo titular para esta reserva.
409CONDICOES_DIVERGEM_DA_AUTORIZACAOValor, prazo, taxa ou CET diferem do que o titular autorizou.
409RESERVA_EXPIRADAA reserva venceu. Consulte a margem de novo.
422VALIDATION_ERRORCampo ausente ou inválido. O campo vem em `erro.campo`.
403CREDENCIAL_SUSPENSAA credencial foi suspensa por padrão compatível com varredura de CPF. Gerar outra chave não contorna — a reativação é decisão humana.
429RATE_LIMITEDAcima do limite por minuto da chave. A resposta traz em quantos segundos tentar de novo.

Webhooks

Registre um destino no console. Cada entrega vai assinada com HMAC-SHA256 no cabeçalho Cocle-Signature, no formato t=<epoch>,v1=<hex> — e o instante entra no que é assinado, para que uma entrega capturada não valha para sempre.

Tópicos de webhook
TópicoQuando dispara
consentimento.revogadoO titular se opôs ao uso dos dados, revogou o consentimento ou pediu eliminação. Interrompa o tratamento: novas consultas respondem 404.
consentimento.restauradoO titular retirou a oposição. As consultas voltam a responder.
margem.bloqueadaO titular bloqueou a própria margem. Nenhuma reserva ou averbação nova é aceita para o vínculo.
margem.desbloqueada
averbacao.efetivadaUm contrato seu foi averbado e entra na próxima folha.
contrato.liquidadoA última parcela foi descontada e a margem correspondente voltou.
Trate consentimento.revogado antes de qualquer outro. Ele chega em menos de 60 segundos da decisão do titular, e continuar tratando o dado depois disso é infração sua — a consulta seguinte responderia 404 de qualquer forma, mas a sua esteira já teria ligado para alguém que pediu para ser deixado em paz.
import { createHmac, timingSafeEqual } from "node:crypto";

// O corpo precisa ser o texto cru. Reserializar o JSON depois de parseá-lo
// muda espaços e ordem, e a assinatura deixa de bater por um motivo que
// não tem nada a ver com autenticidade.
export function conferir(segredo: string, corpo: string, cabecalho: string) {
  const partes = new Map(cabecalho.split(",").map((p) => {
    const i = p.indexOf("=");
    return [p.slice(0, i), p.slice(i + 1)] as const;
  }));

  const t = Number(partes.get("t"));
  const recebida = partes.get("v1") ?? "";
  const chave = Buffer.from(segredo.replace("whsec_", ""), "base64url");
  const esperada = createHmac("sha256", chave).update(t + "." + corpo).digest("hex");

  const a = Buffer.from(esperada), b = Buffer.from(recebida);
  if (a.length !== b.length || !timingSafeEqual(a, b)) return false;

  // Rejeite o que for velho demais: sem isto, uma entrega capturada pode ser
  // reenviada meses depois e o seu servidor não tem como distinguir.
  return Math.abs(Date.now() / 1000 - t) < 300;
}

Quatro coisas que quebram integração

  • Não enviar Idempotency-Key em escrita. Uma retentativa de rede vira contrato duplicado — e desconto em duplicidade no contracheque de uma pessoa.
  • Deduplicar webhook pelo conteúdo em vez do Cocle-Event-Id. A entrega é at-least-once. É a causa nº 1 de bug em integração aqui.
  • Ignorar consentimento.revogado. Continuar tratando o dado depois da revogação é infração sua, não nossa.
  • Varrer CPFs. Acima de 70% de consultas sem resultado, com pelo menos 50 chamadas na janela, a credencial é suspensa automaticamente — e gerar outra não contorna. O limiar é publicado de propósito: é folgado para base fria legítima, e a regra que você conhece é a que consegue respeitar. Consulte quem procurou você.

Cliente em TypeScript

O SDK trata por padrão as três coisas que a integração erra com mais frequência: manda Idempotency-Key em toda escrita, só repete em erro transitório — nunca num 409, que é decisão de negócio — e devolve o erro como objeto com codigo, para o seu if não depender do texto da mensagem.

npm install @cocle/sdk@2026.8.0
import { Cocle, ErroCocle } from "@cocle/sdk";

const cocle = new Cocle({ chave: process.env.COCLE_API_KEY! });

try {
  const r = await cocle.consultarMargem({
    cpf: "11144477735",
    finalidade: "oferta de crédito consignado",
  });

  const vinculo = r.vinculos.find((v) => v.elegivel);
  if (!vinculo) return;

  const reserva = await cocle.criarReserva({
    vinculoId: vinculo.vinculo_id,
    consignatariaId: process.env.COCLE_CONSIGNATARIA_ID!,
    bucket: "EMPRESTIMO",
    valorParcela: 250,
  });

  // O titular precisa aprovar antes de averbar. Nenhum desconto é lançado
  // sem isso, e as condições daqui são conferidas por hash na averbação.
  await cocle.pedirAutorizacao({
    reservaId: reserva.reserva_id,
    valorFinanciado: 12000, valorParcela: 250, qtdParcelas: 60,
    taxaAm: 1.65, cetAa: 21.7,
  });
} catch (e) {
  if (e instanceof ErroCocle && e.codigo === "MARGEM_INSUFICIENTE") {
    // Ofereça um valor de parcela menor.
  } else if (e instanceof ErroCocle) {
    console.error(e.codigo, e.requestId);
  }
}

Status da plataforma · Entrar no console