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.averba.link/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.
33366699957ativo—Margem bloqueada pelo titular — responde como se não existisse.
44477700083afastado—Servidor 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. TODA consulta de produção é tarifada — com ou sem resultado: o preço é pelo processamento da busca, e o 404 gratuito seria varredura de CPF de graça. A suspensão automática por enumeração continua valendo por cima. Cada vínculo traz a MATRÍCULA para a conferência de identidade na autocontratação (pergunte-a ao titular e compare); todo acesso fica registrado e visível ao titular no portal dele.

curl -X POST https://api.averba.link/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",
      "matricula": "MAT004821",
      "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,
          "taxa_max_am": 1.8,
          "prazo_max_meses": 96,
          "valor_maximo_financiavel_estimado": 56917.45
        }
      ]
    }
  ],
  "billing": {
    "evento": "CONSULTA_MARGEM",
    "faturavel": true,
    "valor": 0.45
  }
}

POST /v1/titulares/dados

Dados cadastrais do titular na autocontratação

Para o atendimento da autocontratação: com o CPF digitado pelo próprio titular e a MATRÍCULA confirmada com ele (campo matricula da consulta de margem), devolve o cadastro — nome, nome social, data de nascimento, e-mail e telefone quando a folha os informa — e o vínculo confirmado. O par CPF+matrícula precisa bater no MESMO vínculo: é a prova de que o titular está presente e pediu o contrato (LGPD art. 7º, V). Combinação errada, oposição do titular ou município não credenciado respondem o MESMO 404 genérico — e a chamada é TARIFADA como consulta, com ou sem resultado: adivinhar matrícula custa dinheiro e alimenta a proteção de enumeração. Todo acesso fica registrado e visível ao titular no portal dele. Dado bancário não está disponível: a folha importada não traz banco de pagamento.

curl -X POST https://api.averba.link/v1/titulares/dados \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cpf": "11144477735",
    "matricula": "MAT004821",
    "finalidade": "atendimento de autocontratação"
  }'

Resposta 200 — Par CPF+matrícula confirmado: o cadastro do titular e o vínculo.

{
  "consulta_id": "cns_01J8Z",
  "consultado_em": "2026-08-26T12:00:00.000Z",
  "titular": {
    "nome": "Marina Ferreira",
    "nome_social": null,
    "data_nascimento": "1984-03-14",
    "cpf_mascarado": "***.444.777-**",
    "email": "marina.ferreira@exemplo.dev",
    "telefone": "48 91234-5678"
  },
  "vinculo": {
    "vinculo_id": "vnc_01J8Z",
    "matricula": "MAT004821",
    "municipio": {
      "codigo_ibge": "3138203",
      "nome": "Município de Lavras"
    },
    "orgao": "Secretaria Municipal de Educação",
    "cargo": "Professor de Ensino Fundamental",
    "regime": "ESTATUTARIO",
    "situacao": "ATIVO"
  },
  "billing": {
    "evento": "CONSULTA_MARGEM",
    "faturavel": true,
    "valor": 0.45
  }
}

POST /v1/titulares/contratos

Seus contratos em aberto com o titular, na autocontratação

A SUA carteira com o titular, entregue a pedido dele no atendimento (refinanciamento, renegociação): cada contrato vivo DA SUA INSTITUIÇÃO no vínculo confirmado — parcela, prazo, parcelas pagas e restantes, valor já descontado e SALDO EM ABERTO — com os totais. Contrato de outra instituição NÃO aparece aqui, nunca: o endividamento total do titular sai AGREGADO na consulta de margem (comprometido por bucket), sem expor credor nem condições de ninguém. Par confirmado sem contrato seu devolve contratos: []. Mesmas guardas dos dados cadastrais: o par CPF+matrícula precisa bater no MESMO vínculo, combinação errada e oposição respondem o MESMO 404 genérico, a chamada é TARIFADA como consulta com ou sem resultado, e todo acesso fica registrado e visível ao titular. O saldo é o previsto das parcelas não canceladas menos o já descontado — o que a folha ainda vai descontar, não o saldo de quitação (juros de liquidação antecipada são conta sua).

curl -X POST https://api.averba.link/v1/titulares/contratos \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cpf": "11144477735",
    "matricula": "MAT004821",
    "finalidade": "refinanciamento a pedido do titular"
  }'

Resposta 200 — Os SEUS contratos vivos no vínculo confirmado, com totais — [] quando não há.

{
  "consulta_id": "cns_01J8Z",
  "consultado_em": "2026-08-26T12:00:00.000Z",
  "municipio": {
    "codigo_ibge": "3138203",
    "nome": "Município de Lavras"
  },
  "matricula": "MAT004821",
  "contratos": [
    {
      "numero_contrato": "CT-2025-4410",
      "bucket": "EMPRESTIMO",
      "status": "ATIVA",
      "taxa_am": 1.6,
      "valor_financiado": 8000,
      "valor_parcela": 180,
      "qtd_parcelas": 60,
      "parcelas_pagas": 12,
      "parcelas_restantes": 48,
      "valor_pago": 2160,
      "saldo_aberto": 8640,
      "competencia_inicio": "2025-09",
      "competencia_fim": "2030-08",
      "proxima_parcela": "2026-09"
    }
  ],
  "totais": {
    "contratos_abertos": 1,
    "parcelas_mensais": 180,
    "saldo_aberto": 8640
  },
  "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.averba.link/v1/reservas \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "vinculo_id": "vnc_...",
    "bucket": "EMPRESTIMO",
    "valor_parcela": 250.00
  }'

Resposta 201 — Reserva criada.

{
  "reserva_id": "01a0c930-b3d5-7f26-b284-6a1f3d26fa10",
  "margem_restante": 1000,
  "status": "VIGENTE",
  "expira_em": "2026-09-25T12:56:47.309Z"
}

Erros próprios deste endpoint: MARGEM_INSUFICIENTE, RESERVAS_SIMULTANEAS_EXCEDIDAS

GET /v1/reservas/:id

Consultar uma reserva sua

O estado que VALE (já EXPIRADA quando a data passou, mesmo antes da rotina de expiração), a validade e as autorizações que pendem dela. Responde à pergunta da esteira: "tenho um reserva_id; posso pedir a autorização sobre ele ou preciso reservar de novo?" Só a própria consignatária enxerga a sua: reserva de outra responde 404, sem confirmar que existe. Não é tarifada: é gestão da própria esteira.

curl https://api.averba.link/v1/reservas/01a0c930-b3d5-7f26-b284-6a1f3d26fa10 \
  -H "Authorization: Bearer $COCLE_API_KEY"

Resposta 200 — A reserva.

{
  "reserva_id": "01a0c930-b3d5-7f26-b284-6a1f3d26fa10",
  "status": "VIGENTE",
  "bucket": "EMPRESTIMO",
  "valor_parcela": 70,
  "vinculo_id": "vnc_01a0c8f1b2c3d4e5f60718293a",
  "municipio": {
    "codigo_ibge": "3138203",
    "nome": "Município de Lavras"
  },
  "criado_em": "2026-09-22T12:56:47.309Z",
  "expira_em": "2026-09-25T12:56:47.309Z",
  "autorizacoes": []
}

GET /v1/credenciamentos

Suas configurações: municípios credenciados, modalidades e preços

O cardápio da integração, direto da plataforma: em quais municípios a chave está credenciada, com quais modalidades, e a configuração vigente de cada um — percentual de margem, teto de taxa, prazo máximo, validade da autorização. O teto de taxa e o prazo valem POR PRODUTO; modalidade sem teto próprio na regra do município herda o do EMPRESTIMO, e o número informado aqui já é o efetivo — o mesmo que a autorização impõe. Também devolve o preço que a sua instituição paga por uso. Configure a esteira a partir daqui em vez de manter uma tabela própria, que envelhece: quando o município publica regra nova, esta resposta muda junto — e a autorização passa a recusar o que ficou fora do teto novo (só para contratos novos: o já averbado não muda). Chamada NÃO tarifada.

curl https://api.averba.link/v1/credenciamentos \
  -H "Authorization: Bearer $CHAVE"

Resposta 200 — Configurações vigentes para a chave.

{
  "consultado_em": "2026-08-26T12:00:00.000Z",
  "precos": {
    "moeda": "BRL",
    "plano": "Sob demanda",
    "consulta_margem": 0.45,
    "averbacao_percentual": 1,
    "mensalidade": 0,
    "franquia_consultas": 0
  },
  "municipios": [
    {
      "codigo_ibge": "3138203",
      "nome": "Município de Lavras",
      "uf": "MG",
      "situacao": "ATIVO",
      "credenciada_desde": "2026-01-01",
      "validade_autorizacao_horas": 48,
      "regra_versao": 1,
      "modalidades": [
        {
          "modalidade": "EMPRESTIMO",
          "percentual_margem": 30,
          "taxa_max_am": 1.8,
          "prazo_max_meses": 96
        },
        {
          "modalidade": "CARTAO_CREDITO",
          "percentual_margem": 5,
          "taxa_max_am": 1.8,
          "prazo_max_meses": 96
        }
      ]
    }
  ]
}

Erros próprios deste endpoint: CONSIGNATARIA_SUSPENSA

GET /v1/autorizacoes

Listar autorizações da consignatária

A esteira: com status=APROVADA (padrão) devolve as autorizações que o titular aprovou e AINDA NÃO foram averbadas — a lista de ação, com a reserva e as condições prontas para o POST /v1/averbacoes. PENDENTE acompanha o que espera decisão. Só as da própria chave, nos municípios credenciados. Não é tarifada: é gestão da própria esteira.

curl https://api.averba.link/v1/autorizacoes?status=APROVADA \
  -H "Authorization: Bearer $COCLE_API_KEY"

Resposta 200 — Autorizações no estado pedido — APROVADA exclui as já averbadas.

{
  "status_filtrado": "APROVADA",
  "autorizacoes": [
    {
      "autorizacao_id": "aut_01J8Z",
      "reserva_id": "rsv_01J8Z",
      "vinculo_id": "vnc_01J8Z",
      "municipio": {
        "codigo_ibge": "4204608",
        "nome": "Município de Exemplo"
      },
      "status": "APROVADA",
      "condicoes": {
        "bucket": "EMPRESTIMO",
        "valor_financiado": 12000,
        "valor_parcela": 250,
        "qtd_parcelas": 60,
        "taxa_am": 1.65,
        "cet_aa": 21.7
      },
      "decidido_em": "2026-08-15T14:22:03.000Z",
      "expira_em": "2026-08-17T14:22:03.000Z"
    }
  ]
}

POST /v1/autorizacoes

aceita Idempotency-Key

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. O município LIMITA as condições: taxa e prazo acima do teto do PRODUTO na regra de margem (produto sem teto próprio herda o do EMPRESTIMO) são recusados aqui, antes de chegar ao titular. Os limites vigentes saem na consulta de margem, por bucket, e em GET /v1/credenciamentos. Envie Idempotency-Key: sem ela, uma retentativa de rede cria um SEGUNDO pedido idêntico na tela do titular.

curl -X POST https://api.averba.link/v1/autorizacoes \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -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, RESERVA_NAO_VIGENTE, PARCELA_ACIMA_DA_RESERVA, AUTORIZACAO_JA_EXISTE, RETENTATIVA_EM_ANDAMENTO, TAXA_OBRIGATORIA, TAXA_ACIMA_DO_TETO, PRAZO_ACIMA_DO_MAXIMO

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.averba.link/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/averbacoes/:id/estorno

Estornar uma averbação no prazo de arrependimento

O consumidor tem 7 dias para desfazer a contratação (CDC art. 49). O estorno leva o contrato a ESTORNADA, cancela as parcelas projetadas, devolve a margem — a consulta seguinte já a mostra disponível — e lança o estorno do success fee como linha negativa na fatura da competência corrente. Fora do prazo o pedido é recusado; repetir o estorno do mesmo contrato não lança a devolução em dobro.

curl -X POST https://api.averba.link/v1/averbacoes/ctr_01J8Z/estorno \
  -H "Authorization: Bearer $COCLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "motivo": "cliente exerceu o direito de arrependimento" }'

Resposta 200 — Averbação desfeita. A margem volta na consulta seguinte.

{
  "contrato_id": "ctr_01J8Z",
  "numero_contrato": "CT-2026-0001",
  "status": "ESTORNADA",
  "parcelas_canceladas": 60,
  "billing": {
    "evento": "ESTORNO_AVERBACAO",
    "valor": -120
  }
}

Erros próprios deste endpoint: PRAZO_DE_ARREPENDIMENTO_EXPIRADO, TRANSICAO_INVALIDA, SANDBOX_SOMENTE_CONSULTA

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.averba.link/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.averba.link/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.averba.link/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.averba.link/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