Contrato v1 (legado)

O corpo {event, data} que webhooks com contract_version = v1 recebem, com os valores de data.status e os dois campos que a IORQ acrescenta na entrega. Preservado na íntegra; integrações novas devem usar o v2.

Webhooks com contract_version = v1 seguem recebendo este corpo, sem mudança e sem data de corte nesta página. Integrações novas devem registrar contract_version = v2 e ler o Catálogo de eventos; a tabela de tipos de lá traz, em cada linha, o data.status equivalente desta página.

1. Envelope

Hoje a IORQ envia um único message_type: LOAN_UPDATE. O corpo do POST para a sua URL é o objeto payload acrescido de iorq_fund_id e event_timestamp no topo, com esta forma:

{
  "event": "update",
  "data": {
    "entity_id": "<originator_proposal_code>",
    "status": "<status — ver abaixo>",
    "reason": "<motivo, quando aplicável>",
    "nosso_numero": "<quando aplicável>",
    "installment_code": "<quando aplicável>",
    "banker_proposal_code": "<quando aplicável>",
    "entity_type": "ccb",
    "due_date": "<quando aplicável — nova data de vencimento (ISO)>"
  },
  "iorq_fund_id": "374485cf-f6df-467c-b91d-9f14082c6f36",
  "event_timestamp": "2026-09-02T18:33:20.123456+00:00"
}

O discriminador do que aconteceu é data.status. Os demais campos de data variam conforme o status e só aparecem quando há valor: são omitidos, não enviados como null. banker_proposal_code, por exemplo, só pode vir nos status ligados às suas chamadas e à cobrança (falhas de chamada, liquidação, antecipação, bolecode e alteração de vencimento); as decisões do gestor (approved, rejected e as de renegociação) e as confirmações da administradora (*_completed) nunca o trazem. Não há um evento por transição de estado — tudo chega como LOAN_UPDATE.

Um exemplo real de operação aprovada:

{
  "event": "update",
  "data": {
    "entity_id": "OP-001",
    "status": "approved",
    "entity_type": "ccb"
  },
  "iorq_fund_id": "374485cf-f6df-467c-b91d-9f14082c6f36",
  "event_timestamp": "2026-09-02T18:33:20.123456+00:00"
}

2. Campos que a IORQ acrescenta na entrega

Dois campos no topo do corpo não vêm do payload do evento: a IORQ os injeta no momento da entrega. Ambos entram antes da assinatura, então o HMAC cobre o corpo já com eles. A validação não muda: calcule o digest sobre os bytes exatos recebidos (ver Configuração e segurança).

Consumidores que ignoram campos desconhecidos não precisam de nenhuma mudança. Se o seu parser rejeita campos extras (additionalProperties: false ou equivalente), libere os dois.

iorq_fund_id

O UUID do fundo a que o evento pertence, o mesmo informado no registro do webhook (header iorq-fund-id). Quem integra mais de um fundo pode rotear pelo próprio corpo, sem depender de uma URL distinta por fundo. O valor é o do evento registrado na IORQ: se o seu webhook está registrado para o fundo A, todo corpo entregue nele traz o iorq_fund_id do fundo A.

event_timestamp

O instante em que a IORQ gerou o evento, ISO-8601 em UTC com offset (2026-09-02T18:33:20.123456+00:00).

  • Não é o instante do envio. É o do evento, e por isso é estável: todas as tentativas de entrega do mesmo evento chegam com o mesmo event_timestamp. Para saber quando aquele POST específico saiu, use o header X-IORQ-Timestamp, que muda a cada tentativa.
  • Numa reentrega manual (replay), o valor é o do evento original, não o do momento do replay. Um replay pedido três dias depois não se apresenta como fato novo, e é assim que você distingue uma notificação nova de uma reconciliação tardia.
  • Chega em toda entrega, inclusive nos webhooks sem signing_secret, que não recebem os headers de assinatura.
  • Se você recebe mais de uma entrega do mesmo fato por ter mais de um webhook registrado no fundo, todas trazem o mesmo event_timestamp.
📘

Sobre o nome, sem o prefixo iorq_

Diferente do iorq_fund_id, este campo não carrega o prefixo. Ele é acrescentado pela IORQ na entrega, como o outro. Se o payload que a sua originação envia já contém um campo event_timestamp, o valor da IORQ substitui o seu no corpo entregue. Nenhum produtor IORQ de LOAN_UPDATE emite esse campo hoje, mas vale conferir do seu lado antes do go-live.

3. Valores de data.status

⚠️

Breaking change: completed foi substituído por um valor por fluxo

A IORQ costumava enviar o literal "completed" em data.status para confirmar, pela administradora, a conclusão de quatro operações distintas, sem nenhum campo no payload que dissesse qual delas havia ocorrido. Isso mudou: cada fluxo agora envia seu próprio valor (settlement_completed, acquisition_completed, repurchase_completed, renegotiation_completed, listados abaixo). Se o seu integrador filtra por data.status == "completed", atualize-o: esse valor não é mais enviado.

📘

invalid deixou de ser a falha de toda chamada, e você escolhe quando adotar

O literal "invalid" era também o valor de falha da liquidação, da antecipação e da recompra, e o payload não dizia de qual chamada a falha vinha: só a presença de installment_code e o texto de data.reason permitiam inferir. Cada uma tem agora o seu valor: invalid_settlement, invalid_prepayment e invalid_repurchase, listados nas seções abaixo.

O que o seu webhook recebe depende do campo v1_failure_status dele. Webhook registrado antes do campo existir está em shared_invalid e continua recebendo invalid nas três falhas, sem mudança. Quando o seu integrador estiver pronto para os três valores, troque com PATCH /api/webhook/{webhook_id} e {"v1_failure_status": "per_context"}; vale a partir da próxima entrega, e volta do mesmo jeito. Webhook novo nasce em per_context. Ver Configurando webhooks.

Cessão

statusQuando dispara
approvedO gestor do fundo aprovou o lote de aquisição com a operação, e ela segue para a administradora. A elegibilidade aprovada na entrada não gera evento; só a reprovada (invalid)
rejectedOperação recusada, esquemática ou de negócio (ver data.reason)
invalidOperação reprovou na validação e ficou em invalid. Em per_context, só a cessão usa este valor; em shared_invalid, também a falha de liquidação, antecipação e recompra chega assim

Aquisição

statusQuando dispara
acquisition_completedAdministradora confirma a aquisição; a operação passa a active e o lote a acquired

Liquidação

statusQuando dispara
settled_installmentParcela liquidada, 1º tempo: a IORQ registrou o pagamento
received_prepaymentLiquidação antecipada aceita
settlement_completedBaixa confirmada pela administradora, 2º tempo: a parcela vai a settled
invalid_settlementA liquidação notificada não foi aplicada (estado inválido, parcela não encontrada, conciliação); a parcela segue como estava, salvo em settlement_conciliation_error, que a marca nesse estado. Traz installment_code e, quando há boleto, nosso_numero; ver data.reason
invalid_prepaymentO pedido de antecipação não foi aceito; nenhuma parcela mudou. installment_code é o code do lote; ver data.reason

Pós-cessão

statusQuando dispara
approvedRecompra aprovada pelo gestor. É o mesmo valor da aprovação de cessão: no v1 os dois fatos não se distinguem, e no v2 são loan.approved e repurchase.approved. A mesma operação pode receber approved duas vezes, a da cessão e depois a da recompra; ver o aviso abaixo
rejectedRecompra recusada, ou renegociação recusada manualmente pelo gestor antes de qualquer aprovação. Mesma observação: no v2 são loan.rejected e repurchase.rejected
approved_renegotiationRenegociação aprovada, com a nova operação na carteira
rejected_renegotiationAo aprovar um lote de renegociação, a operação não chegou a approved_renegotiation nem é inválida. Distinto do rejected acima, que é a rejeição manual anterior a qualquer aprovação
invalid_renegotiationFalha na renegociação
invalid_repurchaseO pedido de recompra não foi aceito (loan_not_found, invalid_loan_state, value_mismatch nos fundos cuja recompra confere o valor); a operação segue como estava; ver data.reason
repurchase_completedAdministradora confirma a recompra; a operação vai a repurchased
renegotiation_completedAdministradora confirma a renegociação; a nova operação vai a active e a antiga a renegotiated
⚠️

approved pode chegar duas vezes para a mesma operação

A aprovação da recompra chega com o mesmo corpo da aprovação da cessão: status: approved para o mesmo entity_id; o mesmo vale para rejected. O v1 não distingue os dois fatos. Uma deduplicação por entity_id + status descarta a aprovação da recompra como repetida. Trate um approved de uma operação que já está na carteira e para a qual você pediu POST /loan/repurchase como a decisão da recompra e confirme consultando a operação, ou migre para o v2, que entrega repurchase.approved.

Bolecode

statusQuando dispara
issued_bolecodeBoleto emitido para a parcela ou para o lote de antecipação; traz nosso_numero, installment_code e banker_proposal_code
canceled_bolecodeBoleto cancelado, por exemplo quando a parcela é substituída por recompra ou renegociação

Alteração de vencimento

statusQuando dispara
due_date_changedNova data de vencimento em vigor no boleto da parcela, um webhook por parcela, com a data em data.due_date e a parcela em data.installment_code

O due_date_changed só é emitido quando a cobrança da operação é de responsabilidade da IORQ: sem boleto IORQ, a alteração é registrada só na administradora e não gera evento. Ver Alteração de vencimento.

Teste

statusQuando dispara
testVocê pediu um evento de teste em POST /api/webhook/{webhook_id}/test; entity_id é o fund_id e não há mais campos em data

4. data.reason

Em status de recusa ou falha, data.reason traz o motivo, e pode ser uma string ou um objeto:

  • String nos motivos operacionais (fund_not_found, loan_not_found for originator=... banker=None, invalid_installment_state, value_mismatch (...)).
  • String com um JSON dentro nos critérios E0xx emitidos fora do lote: a elegibilidade na entrada da cessão (invalid) e da renegociação (invalid_renegotiation), a expiração da renegociação pré-aprovada (E023) e o documento de renegociação reprovado (E024). O valor é o mapa {"E0xx": {"title", "description"}} serializado: "reason": "{\"E023\": {\"title\": \"Renegociação Expirada\", ...}}". Parseie a string para ler os códigos.
  • Objeto {"E0xx": {"title", "description"}} nos E0xx do lote de renegociação (invalid_renegotiation) e no E026 da reprovação em covenant (rejected).

Os textos são estáveis por compatibilidade, não por contrato: o código por trás de cada string está no catálogo de códigos de erro, e no v2 ele chega estruturado em errors[].code.

5. Sem filtro por tipo de evento

Um webhook v1 recebe todos os LOAN_UPDATE do fundo. O event_types do registro só filtra fatos que o produtor já publica em v2; até a virada, o filtro é feito no seu lado, pelo data.status.

6. Deduplicação

O corpo v1 não traz identificador do evento. Deduplique pelo conteúdo de data (entity_id + status, mais installment_code e due_date quando existirem), prefixado pelo iorq_fund_id, com a exceção do approved/rejected da recompra descrita na seção 3. A receita está em Idempotência e reentrega.

7. Próximos passos


Did this page help you?