Webhooks
Várias etapas do crédito são assíncronas (consulta de margem, assinatura, averbação, desembolso, repasse). Em vez de ficar consultando o recurso em loop (polling), cadastre URLs e a Socinal avisa você quando cada evento concluir.
Cadastrando um endpoint
Registre a URL e os eventos
curl -X POST https://econsignado-api.socinal.com.br/v1/webhooks \
-H 'Authorization: Bearer SEU_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://seu-sistema.com.br/webhooks/socinal",
"eventos": ["autorizacao.confirmada", "assinatura.concluida", "averbacao.concluida", "desembolso.liquidado"]
}'Guarde o secret
{
"id": "wh_a1b2...",
"url": "https://seu-sistema.com.br/webhooks/socinal",
"eventos": ["autorizacao.confirmada", "assinatura.concluida", "averbacao.concluida", "desembolso.liquidado"],
"secret": "whsec_abc123...",
"ativo": true,
"criado_em": "2026-06-27T12:00:00Z"
}O secret é exibido só uma vez — ele é a chave para validar a assinatura das
entregas.
Formato da entrega
Cada evento chega como um POST na sua URL, com estes headers:
| Header | Descrição |
|---|---|
X-Webhook-Event | Nome do evento (ex.: averbacao.concluida). |
X-Webhook-Delivery | ID único da entrega. Use para idempotência — reentregas de retry repetem o mesmo ID. |
X-Webhook-Signature | sha256=<hmac_hex> — assinatura HMAC do corpo (ver abaixo). |
Em todos os eventos do Backoffice o corpo segue o mesmo envelope: os metadados ficam na raiz e a carga
específica do evento vai em data.
| Campo (raiz) | Tipo | Descrição |
|---|---|---|
evento | string | Nome do evento (igual ao header X-Webhook-Event). |
timestamp | string (ISO 8601) | Momento da emissão. |
parceiro_id | uuid | Parceiro dono do endpoint. |
data | object | Carga específica do evento (ver tabela de eventos). |
O envelope acima é o do Backoffice. A API de Leilão entrega um envelope
parecido mas não idêntico: ela não envia parceiro_id na raiz. Se o seu
receptor é compartilhado entre as duas APIs, não trate parceiro_id como
obrigatório.
{
"evento": "averbacao.concluida",
"timestamp": "2026-07-10T18:30:00.000Z",
"parceiro_id": "5e02a0c7-5846-42c1-a39e-06abec548396",
"data": {
"emprestimo_id": "6f1e...",
"averbacao_id": "9a2c...",
"protocolo_dataprev": "123456789"
}
}Eventos disponíveis
Os campos abaixo são os de data (a raiz é sempre o envelope acima).
| Evento | Dispara quando | Campos em data |
|---|---|---|
autorizacao.confirmada | O trabalhador aceitou a autorização (link web/WhatsApp) | autorizacao_id, cpf_trabalhador, status, origem |
margem.concluida | Uma consulta de margem ao Dataprev terminou com sucesso. Condicional — veja abaixo: não é emitido quando a consulta é reaproveitada do cache | cpf_trabalhador, cnpj_empregador, consulta_margem_id, salario_bruto, margem_disponivel, margem_percentual |
margem.falhou | A consulta de margem falhou | cpf_trabalhador, cnpj_empregador, tipo_erro (upstream/negocial), erro |
emprestimo.emitido | Uma emissão que voltou 202 (status: emitindo) foi concluída — a CCB está em_assinatura e o número definitivo está atribuído | emprestimo_id, numero, valor_liberado |
emprestimo.emissao_falhou | Uma emissão que voltou 202 foi abandonada após esgotar as tentativas — o empréstimo foi cancelado e você pode reemitir | emprestimo_id, motivo |
assinatura.concluida | A assinatura se completou. Com assinatura pela Socinal (D4Sign): o documento foi finalizado (tipo_assinatura: "socinal"). Com assinatura externa: o pacote completo (CCB assinada + selfie + documento frente/verso + evidências) foi recebido e arquivado (tipo_assinatura: "externa") | emprestimo_id, tipo_assinatura |
assinatura.pendente | Só na entrega diferida: o empréstimo ficou pronto para averbar (aguardando_averbacao) com o pacote assinado ainda incompleto | emprestimo_id, numero_emprestimo, ccb_assinada_recebida |
docs_dataprev.pendente | Só na entrega diferida: a averbação abriu a pendência de envio de documentos ao Dataprev (com prazo) | emprestimo_id, numero_emprestimo, prazo |
docs_dataprev.recebido | Os documentos do Dataprev foram recebidos | emprestimo_id, numero_emprestimo |
averbacao.concluida | A averbação foi registrada no Dataprev | emprestimo_id, averbacao_id, protocolo_dataprev |
averbacao.falhou | A averbação falhou | emprestimo_id, averbacao_id, erro |
desembolso.liquidado | O PIX de desembolso foi liquidado | emprestimo_id, numero_emprestimo, valor_liberado, data_liberacao |
desembolso.falhou | O desembolso falhou | emprestimo_id, numero_emprestimo, motivo |
trabalhador.desligado | A sincronização com o Dataprev detectou o término do vínculo de um trabalhador com CCB sua (o empréstimo é encerrado por término de vínculo) | emprestimo_id, numero_emprestimo, cpf_trabalhador, matricula, nome_trabalhador, data_desligamento, motivo_desligamento ({codigo, descricao}), situacao_emprestimo ({codigo, descricao}) |
trabalhador.vinculo_novo | Um trabalhador que teve empréstimo encerrado por término de vínculo foi readmitido — sinaliza a oportunidade de averbar o saldo pendente no novo emprego | cpf_trabalhador, nome_trabalhador, novo_empregador ({cnpj, nome}), data_admissao, margem_disponivel, emprestimo_id, saldo_pendente, averbavel |
repasse.processado | O lote de repasse mensal foi fechado/consolidado | repasse_id, competencia, total_parcelas, numero_ccbs, valor_total, fechado_em |
Matriz: evento × pré-condição × configuração
Nem todo evento do catálogo chega para todo parceiro. Alguns dependem da configuração da sua conta (modalidade de assinatura, entrega diferida de documentos), outros só existem em caminhos específicos do fluxo. Assinar um evento condicional que o seu setup nunca produz faz o seu sistema esperar para sempre — confira nesta matriz antes de construir a lógica de espera.
Legenda de garantia
- Garantido — sempre chega quando a transição de domínio acontece, sem depender de configuração.
- Condicional — depende da configuração da sua conta ou de um caminho específico do fluxo. Se o seu setup não o produz, ele nunca chega.
| Evento | Pré-condição (o que tem que acontecer) | Configuração exigida | Garantia |
|---|---|---|---|
autorizacao.confirmada | O trabalhador aceitou a autorização de consulta | — | Garantido |
margem.concluida | Uma consulta de margem chegou ao Dataprev (sua, ou disparada pela Socinal numa operação interna) | — | Condicional: não é emitido quando a consulta é reaproveitada do cache. A sua chamada é síncrona — não dependa deste evento (ver abaixo) |
margem.falhou | POST /v1/margem/consulta falhou no Dataprev | — | Garantido |
emprestimo.emitido | Uma emissão que voltou 202 foi concluída | — | Condicional: só existe no caminho 202 (status: emitindo) |
emprestimo.emissao_falhou | Uma emissão que voltou 202 foi abandonada | — | Condicional: só existe no caminho 202 |
assinatura.concluida | A assinatura se completou: no D4Sign, o documento foi finalizado; na assinatura externa, o pacote completo ficou arquivado. data.tipo_assinatura distingue (socinal / externa) | — | Garantido nas duas modalidades |
assinatura.pendente | O empréstimo chegou a aguardando_averbacao com o pacote ainda incompleto | Assinatura externa + entrega diferida | Condicional |
docs_dataprev.pendente | A averbação concluiu e abriu a necessidade de entrega dos documentos | Assinatura externa + entrega diferida | Condicional |
docs_dataprev.recebido | O pacote de documentos foi entregue por você | Assinatura externa + entrega diferida | Condicional |
averbacao.concluida | A averbação foi registrada no Dataprev | — | Garantido |
averbacao.falhou | A averbação falhou | — | Garantido |
desembolso.liquidado | O PIX de desembolso foi liquidado | — | Garantido |
desembolso.falhou | O desembolso falhou | — | Garantido |
trabalhador.desligado | A sincronização noturna detectou término de vínculo | — | Garantido na primeira detecção de cada registro |
trabalhador.vinculo_novo | A sincronização noturna detectou readmissão de quem teve CCB encerrada por término de vínculo | — | Garantido na primeira detecção de cada registro |
repasse.processado | O lote de repasse mensal foi fechado | — | Garantido |
repasse.pago | — | — | Reservado, nunca emitido |
autorizacao.expirada | — | — | Reservado, nunca emitido |
As configurações citadas são definidas pela Socinal no seu cadastro, não por API.
Em caso de dúvida sobre qual modalidade a sua conta usa, confirme com o suporte —
e note que GET /v1/emprestimos/{id} sempre permite reconciliar por consulta,
independentemente de evento.
Os eventos marcados como Condicional não devem virar pontos de bloqueio do
seu fluxo sem confirmar que o seu setup os produz. Em especial:
docs_dataprev.pendente não é uma consequência garantida de
POST /v1/emprestimos/{id}/averbacao — no fluxo agrupado (e na assinatura pela
Socinal) ele simplesmente não existe, porque a necessidade de entrega diferida
não existe.
margem.concluida: quando esperar (e quando não)
Este evento não serve para acompanhar a sua própria chamada.
POST /v1/margem/consulta é síncrono: responde 200 já com
valor_margem_disponivel e consulta_margem_id no corpo. Quando você recebe a
resposta, a consulta terminou — não há nada para aguardar por webhook.
O evento é emitido quando uma consulta ao Dataprev acontece, e é útil no caso em que você não é quem disparou: a Socinal também consulta a margem do seu trabalhador em operações internas (montagem de proposta pelo atendimento, renegociação). Aí o webhook é o único aviso que você recebe.
Duas consequências práticas:
- Reaproveitamento de cache não emite o evento. A consulta é DB-first: se
houver uma consulta recente do mesmo par CPF + empregador, ela é reaproveitada,
o Dataprev não é chamado (não há cobrança) e nenhum evento é emitido. Sua
resposta REST vem completa do mesmo jeito. Use
skip_cache=truepara forçar a consulta nova. - Não use este evento como confirmação da sua consulta. A confirmação é a
resposta
200. Se você chamar duas vezes seguidas para o mesmo trabalhador, é esperado receber o evento na primeira e não na segunda.
O data de margem.concluida usa nomes diferentes dos da resposta REST de
POST /v1/margem/consulta. É um contrato público já em uso e não vai ser
renomeado — mapeie assim:
Campo no evento (data) | Campo equivalente na resposta REST |
|---|---|
salario_bruto | valor_total_vencimentos |
margem_disponivel | valor_margem_disponivel |
cnpj_empregador | numero_inscricao_empregador (CNPJ completo, 14 dígitos) |
cpf_trabalhador | cpf |
margem_percentual | — (derivado: valor_margem_disponivel / valor_total_vencimentos, null quando os vencimentos são zero) |
consulta_margem_id | consulta_margem_id |
Assinatura externa: assinatura.concluida vs. assinatura.pendente
assinatura.concluida significa pacote assinado completo arquivado: CCB
assinada + selfie + documento frente/verso + as evidências da assinatura. Ele
nunca é emitido por uma simples transição de status.
- Entrega agrupada — você envia o pacote inteiro em
POST /v1/emprestimos/{id}/documentos:assinatura.concluidasai nesse momento, antes da averbação. - Entrega diferida — averbar não exige documento nenhum. Enquanto o pacote
não fecha, sai
assinatura.pendente(comccb_assinada_recebidaindicando se a CCB já foi adiantada), tanto quando você adianta a CCB emPOST /v1/emprestimos/{id}/documentosquanto quando a averbação parte direto deem_assinatura.assinatura.concluidasó chega no final, quando a última peça do pacote é entregue — em lote (POST /v1/emprestimos/{id}/documentos-dataprev) ou no último envio incremental (POST /v1/emprestimos/{id}/documentos/{tipo}) —, junto dedocs_dataprev.recebido.
Ou seja: no fluxo diferido, receber assinatura.concluida antes da entrega
dos documentos era um bug e não acontece mais. Não use esse evento como gatilho
para averbar — para isso, o sinal é assinatura.pendente (ou simplesmente
chamar a averbação, que não depende de documento no fluxo diferido).
Emissão assíncrona: emprestimo.emitido e emprestimo.emissao_falhou
Esses dois eventos só aparecem quando POST /v1/emprestimos respondeu
202. No caminho
normal a emissão é sincrona (201, já em_assinatura) e nenhum dos dois é
emitido — não construa o fluxo assumindo que eles sempre chegam.
Quando um 202 acontece, exatamente um deles chega para aquele empréstimo:
emprestimo.emitido→ siga o fluxo normal a partir deem_assinatura. Onumerodo evento é o definitivo (atribuído pelo sistema de liquidação); se você guardou o número devolvido antes, substitua-o.emprestimo.emissao_falhou→ o empréstimo foi cancelado; emita novamente, com uma novaIdempotency-Key.
Enquanto nenhum dos dois chegar, GET /v1/emprestimos/{id} mostra status: emitindo e proxima_acao: aguardar_emissao. Casos raros ficam em emitindo
esperando ação da Socinal — se isso passar de algumas horas, acione o suporte com
o emprestimo_id; não reemita por conta própria.
Docs Dataprev: dimensão separada do ciclo de vida da CCB
docs_dataprev.pendente e docs_dataprev.recebido não são eventos do ciclo de
vida da CCB — eles pertencem à segunda dimensão de
estado do empréstimo e só
existem na assinatura externa com entrega diferida.
Consequências práticas ao reagir a eles:
- Receber
docs_dataprev.pendentenão bloqueia nem substitui o desembolso: se o empréstimo estáaverbada, a ação da CCB continua sendodesembolsar(proxima_acao), e a pendência de documentos vive ementrega_docs_dataprev. - Receber
desembolso.liquidadonão encerra a pendência de documentos: a CCB viradesembolsada(proxima_acao: null) e o bloco pode seguiraguardando, com o prazo correndo. Sódocs_dataprev.recebidofecha essa dimensão. - Ao reconciliar por consulta, leia as duas dimensões de
GET /v1/emprestimos/{id}:status/proxima_acao(CCB) eentrega_docs_dataprev(documentos).
Os dois eventos de vínculo têm equivalente por consulta, para reconciliação ou
caso você prefira polling:
GET /v1/vinculos/desligamentos
e GET /v1/vinculos/novos.
São emitidos pela sincronização proativa noturna com o Dataprev — e só na
primeira detecção de cada registro (janelas sobrepostas não reenviam).
Os eventos repasse.pago e autorizacao.expirada estão reservados no catálogo,
mas ainda não são emitidos — não construa integrações que dependam deles.
Validando a assinatura
Cada entrega vem assinada com HMAC-SHA256 no header
X-Webhook-Signature: sha256=<hex>. Calcule o HMAC do corpo cru da
requisição (os bytes exatos, antes de qualquer parse) usando o seu secret e
compare em tempo constante:
import { createHmac, timingSafeEqual } from 'node:crypto';
function assinaturaValida(corpoCru: string, header: string, secret: string) {
const esperado = 'sha256=' + createHmac('sha256', secret).update(corpoCru).digest('hex');
const a = Buffer.from(header ?? '');
const b = Buffer.from(esperado);
return a.length === b.length && timingSafeEqual(a, b);
}Valide a assinatura antes de processar o payload, e responda 2xx
rapidamente (timeout de 30s) — faça o processamento pesado em segundo plano.
Política de retry
- A entrega é assíncrona; cada tentativa é registrada.
- Se você não responder
2xx(ou houver timeout/erro de conexão), a Socinal refaz a entrega: 5 tentativas com backoff fixo — 1min, 5min, 15min, 1h, 6h. - Esgotadas as tentativas, a entrega fica como
erro(sem reentrega automática depois disso). - Reentregas repetem o mesmo
X-Webhook-Delivery→ deduplique por ele.
Webhooks vs. polling
Prefira webhooks para resultados assíncronos. Use as consultas (GET /v1/emprestimos/{id}, GET /v1/emprestimos/{id}/averbacao) como fallback —
por exemplo, para reconciliar caso você tenha perdido uma entrega.