Catálogo de eventos

O contrato v2 entrega um evento tipado por fato, com envelope próprio e um type por acontecimento. Esta página lista os tipos, o formato do envelope e o corpo de cada um. O contrato v1 tem página própria.

1. Duas versões de contrato

Cada webhook registrado tem uma contract_version, e ela decide o corpo que você recebe.

VersãoCorpo entreguePara quem
v2Envelope tipado, um type por fatoPadrão para integrações novas
v1{event, data} com data.status, o corpo legado (página própria)Quem já integra e ainda não migrou
🚧

Onde o v2 já roda

Em sandbox os produtores já publicam v2: um webhook v2 recebe o envelope nativo, com data.loan.id preenchido e os headers X-IORQ-Event-Id, X-IORQ-Event-Type e X-IORQ-Delivery-Id em toda entrega. Em produção os produtores ainda publicam no formato v1, e a IORQ remonta o envelope v2 a partir dessa mensagem na hora da entrega. Um webhook registrado hoje em produção com contract_version = v2 recebe esse envelope remontado, sem os headers X-IORQ-Event-Id, X-IORQ-Event-Type e X-IORQ-Delivery-Id. A remontagem perde o que o corpo v1 não carrega: em produção, hoje, data.loan.id chega nulo em todo evento, o id do envelope muda a cada entrega, occurred_at é o instante em que o evento entrou na IORQ para entrega, e um status v1 sem equivalente v2 não seria entregue (só o completed legado está nesse caso, e ele já não é emitido). A lista completa, com o que fazer em cada caso, está na seção 7.

Pode registrar v2 em produção? Pode, e é o que uma integração nova deve fazer: registre contract_version = v2 direto e programe contra as ressalvas da seção 7, que somem sozinhas quando cada produtor virar. O que você testou em sandbox foi o envelope nativo; o resumo das diferenças está em O que muda entre sandbox e produção. "Combinar a data de virada" é para quem já consome v1 e vai migrar um consumidor em produção: ver Migrar um webhook v1 para o v2.

A versão é escolhida no registro do webhook e não muda sozinha. Um integrador que já recebe o v1 continua recebendo exatamente o mesmo corpo, sem data de corte nesta página. O contrato v1 está preservado na íntegra em Contrato v1 (legado).

Os dois contratos descrevem os mesmos fatos. A tabela da seção 3 traz, em cada linha, o data.status que aquele fato tem no v1.

2. Envelope v2

Todo evento chega com a mesma estrutura de topo. O que muda entre os tipos é o conteúdo de data.

CampoTipoO que é
idstringIdentificador do envelope, prefixo evt_. Não é chave de deduplicação (ver abaixo)
typestringO que aconteceu, no formato recurso.fato. É o discriminador de data
schema_versionstringVersão do esquema do envelope. Hoje 2026-10-01
occurred_atstringInstante em que a IORQ gerou o evento, ISO-8601 em UTC. Não é o instante do envio. No envelope remontado do v1, o caso de produção hoje, é o instante em que o evento entrou na IORQ para entrega, não o instante do fato (ver seção 7)
fundobjectid do fundo e, quando disponível, name
subjectobjectSobre o que é o fato: type e id
idempotency_keystringChave do fato, derivada do tipo e do sujeito. Igual em qualquer reentrega do mesmo fato
dataobjectOs dados do fato, com a forma que o type determina

Dois campos pedem uma nota:

  • id é estável entre as tentativas quando o produtor publica em v2. Enquanto o fato é publicado em v1 e convertido na entrega, o caso de produção hoje (ver o aviso da seção 1), ele é gerado a cada entrega. Deduplique pela idempotency_key, nunca por ele.
  • subject.type prevê loan, installment, prepayment, acquisition e credit_vehicle. Os fatos de hoje usam loan, installment e credit_vehicle, este no webhook.test, em que subject.id é o fund_id (o acquisition.expired, reservado e sem produtor, também o usaria). Trate um tipo desconhecido como um fato que você ainda não processa, não como erro.

Exemplo de uma operação aprovada:

{
  "id": "evt_01K4Q8ZJ0000000000000000AA",
  "type": "loan.approved",
  "schema_version": "2026-10-01",
  "occurred_at": "2026-09-02T18:33:20.123456Z",
  "fund": {
    "id": "374485cf-f6df-467c-b91d-9f14082c6f36",
    "name": "FIDC EXEMPLO"
  },
  "subject": {
    "type": "loan",
    "id": "OP-001"
  },
  "idempotency_key": "loan.approved:loan:OP-001",
  "data": {
    "loan": {
      "originator_code": "OP-001",
      "id": null,
      "kind": "ccb",
      "banker_code": null
    },
    "installment": null,
    "charge": null,
    "errors": null,
    "reason_text": null
  }
}

O corpo entregue é o envelope, sem nenhum campo injetado: fund e occurred_at já fazem parte dele. É a diferença para o v1, que recebe iorq_fund_id e event_timestamp acrescentados no topo na hora da entrega.

2.1 O que já vem preenchido, e o que ainda não

Alguns campos existem no esquema mas hoje nunca variam. Vale saber antes de escrever o parser:

CampoComo vem hoje
data.loan.originator_codeSempre presente. É o identificador da operação no seu sistema
data.loan.idO identificador da operação na IORQ, e a chave de GET /loan/{id_loan}, quando o produtor publica em v2. Em produção, hoje, chega nulo em todo evento (ver o aviso abaixo)
data.charge.kindSempre boleto. O esquema também prevê pix, que nenhum fato usa hoje
subject.typeloan, installment e, só no webhook.test, credit_vehicle. prepayment e acquisition não são emitidos por nenhum fato de hoje
🚧

data.loan.id ainda chega nulo em produção

O identificador só existe no envelope que o produtor publica em v2. Em sandbox os produtores já publicam v2 e o campo vem preenchido; os de produção ainda publicam v1 (ver seção 7). Até a virada de cada produtor, todo evento entregue em produção traz data.loan.id: null. Concilie pelo data.loan.originator_code. Para consultar a operação, use a listagem GET /loan/ com originator_proposal_code e confira o valor exato no resultado: o filtro casa por trecho e sem diferenciar maiúsculas, e devolve também as operações cujo código contém o seu.

Com o produtor em v2, data.loan.id vem preenchido, salvo quando a IORQ não chegou a resolver a operação que o evento nomeia:

  • Na cessão (loan.invalid): fund_not_found, banker_not_found, fund_not_supported e o unexpected_error_processing_operation que o acompanha; unexpected_error_processing_operation quando a avaliação falha antes de gravar a operação; e a recepção de arquivo de lastro ou a atualização de dados da operação quando a operação não foi encontrada.
  • No código duplicado, de propósito: em duplicated_originator_proposal_code a operação gravada é a outra, aquela com que o seu código colidiu, e o evento fala da recusada.
  • Fora da cessão, quando a operação ou a parcela não foi encontrada: installment.settlement_invalid com installment_not_found, repurchase.invalid com loan_not_found ou com erro antes de resolver a operação, e prepayment.invalid sem a operação do pedido resolvida.

Nesses casos concilie pelo originator_code.

Uma convenção herdada do v1 que costuma surpreender: installment.number igual a 99 não é a parcela 99. É o boleto do lote de antecipação, que não tem parcela por trás. Nesse caso installment.id (e o subject.id, já que o sujeito é a parcela) carrega o identificador da antecipação, não o de uma parcela. Se você casa installment.id com a sua tabela de parcelas, trate o 99 como um caso à parte.

2.2 O que pode mudar sem quebrar você

O schema_version é o contrato de compatibilidade. Enquanto ele não mudar, estas ampliações podem chegar sem aviso:

  • Tipos de evento novos, para quem assina * ou recurso.*.
  • Campos novos no envelope ou dentro de data.
  • Valores novos em campos abertos, como um novo code em errors[] ou um novo kind de operação.
  • Campos hoje sempre nulos passando a vir preenchidos.

Um consumidor preparado ignora campo que não conhece e ignora type que não conhece, em vez de recusar a entrega. Para o que quebra um consumidor assim, como remover um campo, mudar o tipo de um dado ou trocar o significado dele, existe o schema_version: é por isso que ele está no envelope, e é o campo que você lê para saber que o contrato mudou. Leia-o e recuse o que não reconhece, em vez de assumir que ele é sempre 2026-10-01. Uma virada dessas seria combinada com você antes, como está sendo a do v1 para o v2.

3. Tipos de evento

A tabela é o índice; cada type leva ao seu verbete em Tipos de evento em detalhe, que explica quando o fato acontece, o que vem antes e depois e o que fazer ao receber.

typeQuando disparadataEquivalente v1 (data.status)
loan.approvedGestor aprova o lote de aquisição e a operação segue para a administradoraloanapproved
loan.rejectedOperação recusada pelo gestor, por você em POST /loan/reject, ou em covenant na renegociaçãoloan; motivo em errors[] só na reprovação em covenant (E026)rejected
loan.invalidA operação enviada na cessão reprovou na validação e ficou em invalid. Só a cessão; as outras chamadas têm o seu .invalidloan, e o motivo em errors[] (operacional) ou reason_text (elegibilidade)invalid
acquisition.completedAdministradora confirma a aquisição, e a operação passa a activeloanacquisition_completed
installment.settledParcela liquidada, primeiro tempo, quando a IORQ já registrou o pagamentoloan, installment, charge quando houve boletosettled_installment
installment.settlement_completedBaixa confirmada pela administradora, segundo tempo, e a parcela vai a settledloan, e a installment que a IORQ envia hoje neste fatosettlement_completed
installment.due_date_changedNova data de vencimento em vigor no boleto da parcelaloan, installment com due_datedue_date_changed
installment.settlement_invalidA liquidação notificada em POST /installment/settle não foi aplicada; a parcela segue como estava, salvo no erro de conciliação, que a marca settlement_conciliation_errorloan, installment com o code enviado, e o motivo em errors[]invalid_settlement
prepayment.receivedLiquidação antecipada aceitaloanreceived_prepayment
prepayment.invalidO pedido de antecipação não foi aceito; nenhuma parcela mudouloan da primeira operação, installment.number com o code do lote, e o motivo em errors[]invalid_prepayment
charge.issuedBolecode emitido para a parcela ou para o lote de antecipaçãoloan, installment, chargeissued_bolecode
charge.cancelledBolecode cancelado, por exemplo quando a parcela é substituída por recompra ou renegociaçãoloan, installment, chargecanceled_bolecode
repurchase.approvedGestor aprova a recompra e ela segue para a administradoraloanapproved
repurchase.rejectedGestor recusa a recompra, ou a aprova deixando de fora uma operação que ele já tinha recusadoloanrejected
repurchase.completedAdministradora confirma a recompra, e a operação vai a repurchasedloanrepurchase_completed
repurchase.invalidO pedido de recompra não foi aceito; a operação segue como estavaloan e o motivo em errors[]invalid_repurchase
renegotiation.approvedRenegociação aprovada, com a nova operação na carteiraloanapproved_renegotiation
renegotiation.rejectedAo aprovar um lote de renegociação, a operação não chegou a aprovada e não é inválidaloanrejected_renegotiation
renegotiation.invalidFalha na renegociaçãoloan, e o motivo em errors[] ou reason_textinvalid_renegotiation
renegotiation.completedAdministradora confirma a renegociação, a nova operação vai a active e a antiga a renegotiatedloanrenegotiation_completed
webhook.testVocê pediu um evento de teste em POST /api/webhook/{webhook_id}/testvazio; o sujeito é o fundotest

Nenhum tipo novo é inventado na entrega: o v2 nomeia os mesmos fatos que o v1 já enviava. Os headers de cada entrega (assinatura, timestamp, identificação) estão descritos uma única vez em Configuração e segurança.

Esta tabela lista o que a IORQ emite hoje. O contrato reserva outros tipos que nenhum produtor emite no momento, e tipos novos podem aparecer sem aviso para quem assina * ou recurso.*. Ignore com segurança um type que você não conhece, em vez de recusar a entrega: um parser que só aceita esta lista quebra na primeira ampliação.

📘

A baixa confirmada muda de grão entre as versões

No v1 esse status chega como um evento por operação: o produtor manda o corpo do loan uma vez por parcela, mas as cópias são idênticas e a IORQ as reconhece como o mesmo evento. Se a primeira entrega foi aceita, as cópias seguintes não viram novas entregas; se ela falhou, a cópia seguinte é entregue no lugar dela. No v2 é um evento por parcela: o subject é a parcela e a idempotency_key leva o número e o vencimento, então duas parcelas da mesma operação chegam como dois fatos.

O corpo v1 segue exatamente como era, sem os campos de parcela, para quem continua em v1.

4. Exemplos

O envelope é o mesmo em todos os tipos, e o que muda é a forma de data. Os exemplos abaixo cobrem as formas que existem, e a tabela da seção anterior diz qual delas cada tipo carrega. Todos são gerados com as mesmas classes que os serviços da IORQ usam para publicar, então o que você lê aqui é byte a byte a forma que o serviço monta.

Bolecode emitido, com a parcela como sujeito:

{
  "id": "evt_01K4Q8ZJ0000000000000000AJ",
  "type": "charge.issued",
  "schema_version": "2026-10-01",
  "occurred_at": "2026-09-02T18:33:20.123456Z",
  "fund": {
    "id": "374485cf-f6df-467c-b91d-9f14082c6f36",
    "name": "FIDC EXEMPLO"
  },
  "subject": {
    "type": "installment",
    "id": "c37d893a-a92e-4f1e-af80-2c7d6779d2fc"
  },
  "idempotency_key": "charge.issued:installment:c37d893a-a92e-4f1e-af80-2c7d6779d2fc:5:2026-10-20",
  "data": {
    "loan": {
      "originator_code": "OP-001",
      "id": null,
      "kind": "ccb",
      "banker_code": "BP-001"
    },
    "installment": {
      "id": "c37d893a-a92e-4f1e-af80-2c7d6779d2fc",
      "number": "5",
      "due_date": "2026-10-20"
    },
    "charge": {
      "kind": "boleto",
      "nosso_numero": "22625",
      "banker_response": null
    },
    "errors": null,
    "reason_text": null
  }
}

Operação reprovada na elegibilidade da cessão. Os critérios chegam no mapa {"E0xx": {...}} serializado em reason_text, com errors nulo e sem banker_code; os motivos operacionais da cessão chegam em errors[] (ver Códigos de erro):

{
  "id": "evt_01K4Q8ZJ0000000000000000AC",
  "type": "loan.invalid",
  "schema_version": "2026-10-01",
  "occurred_at": "2026-09-02T18:33:20.123456Z",
  "fund": {
    "id": "374485cf-f6df-467c-b91d-9f14082c6f36",
    "name": "FIDC EXEMPLO"
  },
  "subject": {
    "type": "loan",
    "id": "OP-001"
  },
  "idempotency_key": "loan.invalid:loan:OP-001",
  "data": {
    "loan": {
      "originator_code": "OP-001",
      "id": "0d3a2f7e-5c1b-4c8e-9f4a-2b7d6e1a9c33",
      "kind": "ccb",
      "banker_code": null
    },
    "installment": null,
    "charge": null,
    "errors": null,
    "reason_text": "{\"E006\": {\"title\": \"Prazo Acima do Limite\", \"description\": \"Prazo superior ao limite (36 meses)\"}}"
  }
}

O data.loan.id preenchido é o de um produtor que já publica em v2. Em produção, hoje, ele chega nulo (ver seção 2.1).

Baixa confirmada pela administradora, um evento por parcela:

{
  "id": "evt_01K4Q8ZJ0000000000000000AF",
  "type": "installment.settlement_completed",
  "schema_version": "2026-10-01",
  "occurred_at": "2026-09-02T18:33:20.123456Z",
  "fund": {
    "id": "374485cf-f6df-467c-b91d-9f14082c6f36",
    "name": "FIDC EXEMPLO"
  },
  "subject": {
    "type": "installment",
    "id": "c37d893a-a92e-4f1e-af80-2c7d6779d2fc"
  },
  "idempotency_key": "installment.settlement_completed:installment:c37d893a-a92e-4f1e-af80-2c7d6779d2fc:5:2026-10-20",
  "data": {
    "loan": {
      "originator_code": "OP-001",
      "id": null,
      "kind": "ccb",
      "banker_code": null
    },
    "installment": {
      "id": "c37d893a-a92e-4f1e-af80-2c7d6779d2fc",
      "number": "5",
      "due_date": "2026-10-20"
    },
    "charge": null,
    "errors": null,
    "reason_text": null
  }
}

Vencimento alterado, o único tipo em que a installment.due_date é obrigatória, com o corpo v1 equivalente logo abaixo para comparação:

{
  "id": "evt_01K4Q8ZJ0000000000000000AG",
  "type": "installment.due_date_changed",
  "schema_version": "2026-10-01",
  "occurred_at": "2026-09-02T18:33:20.123456Z",
  "fund": {
    "id": "374485cf-f6df-467c-b91d-9f14082c6f36",
    "name": "FIDC EXEMPLO"
  },
  "subject": {
    "type": "installment",
    "id": "c37d893a-a92e-4f1e-af80-2c7d6779d2fc"
  },
  "idempotency_key": "installment.due_date_changed:installment:c37d893a-a92e-4f1e-af80-2c7d6779d2fc:5:2026-10-20",
  "data": {
    "loan": {
      "originator_code": "OP-001",
      "id": null,
      "kind": "ccb",
      "banker_code": "BP-001"
    },
    "installment": {
      "id": "c37d893a-a92e-4f1e-af80-2c7d6779d2fc",
      "number": "5",
      "due_date": "2026-10-20"
    },
    "charge": null,
    "errors": null,
    "reason_text": null
  }
}

O mesmo fato, entregue a um webhook que continua em v1:

{
  "event": "update",
  "data": {
    "entity_id": "OP-001",
    "status": "due_date_changed",
    "installment_code": "5",
    "banker_proposal_code": "BP-001",
    "iorq_installment_id": "c37d893a-a92e-4f1e-af80-2c7d6779d2fc",
    "entity_type": "ccb",
    "due_date": "2026-10-20"
  },
  "iorq_fund_id": "374485cf-f6df-467c-b91d-9f14082c6f36",
  "event_timestamp": "2026-09-02T18:33:20.123456+00:00"
}

Repare no que a conversão faz: entity_id é o originator_code, installment_code é o number, iorq_installment_id é o id da parcela, entity_type é o kind da operação, e o status é o equivalente v1 do type. O iorq_fund_id e o event_timestamp do topo são acrescentados na entrega, e no v2 o mesmo dado já mora em fund e occurred_at.

5. Motivos: errors[] e reason_text

Quando há um motivo, ele vem em um dos dois campos, nunca nos dois:

  • errors[] nas falhas operacionais, com um code do catálogo de códigos de erro, title e description com o detalhe da ocorrência: installment.settlement_invalid, prepayment.invalid, repurchase.invalid e os motivos operacionais de loan.invalid e renegotiation.invalid. Um E0xx só chega em errors[] em dois casos: o renegotiation.invalid do lote de renegociação e o E026 de loan.rejected. Programe contra o code; a description é texto.
  • reason_text em fatos de sucesso que carregam uma confirmação em texto (installment settled successfully), e nas falhas por critério E0xx emitidas fora do lote: a elegibilidade na entrada da cessão (loan.invalid) e da renegociação, a expiração E023 e o documento reprovado E024 (renegotiation.invalid), sempre como o mapa {"E0xx": {...}} serializado em string. Ver Códigos de erro.

No envelope remontado a partir do v1, o caso de produção hoje, um motivo em texto que a IORQ não reconhece como código do catálogo também chega em reason_text (ver seção 7).

Dois detalhes que economizam um retrabalho:

  • Não é exclusivo de recusa. Fatos de sucesso também podem trazer reason_text, como a liquidação de parcela, que carrega uma confirmação em texto. Leia o campo como "o que a IORQ disse sobre este fato", não como "deu errado".
  • errors[] pode vir vazio. Uma lista vazia significa "sem motivo a declarar", e é o que o v1 entregava como "reason": {}. Não trate lista vazia como ausência de campo.

No v1 os dois viram o mesmo campo data.reason: string para um código do catálogo operacional e para todo reason_text, objeto para os E0xx que o v2 leva em errors[]. Ver Códigos de erro.

6. Assinatura por tipo

Um webhook declara em event_types o que quer receber. Três formas de padrão:

PadrãoRecebe
*Todos os tipos, presentes e futuros. É o valor padrão
recurso.*Todos os tipos daquele recurso, por exemplo installment.*
tipo.exatoSó aquele tipo, por exemplo installment.settled

Os recursos aceitos em recurso.* são loan, acquisition, installment, prepayment, charge, repurchase, renegotiation e webhook (o evento de teste). Um padrão fora dessas três formas é recusado no registro, e uma lista vazia também.

⚠️

O filtro vale por como o fato foi publicado, não pela versão do seu webhook

event_types é aplicado aos fatos publicados em v2, e vale para qualquer webhook do fundo, inclusive um webhook v1. Enquanto um produtor ainda publica em v1, os fatos dele não passam por esse filtro e chegam a todos os webhooks ativos do fundo, mesmo aos que declararam uma lista restrita. Na prática: assine o que quer receber, mas não conte com o filtro para deixar de receber o resto antes da virada do produtor.

Como declarar o filtro no registro: Configurando webhooks.

7. O que muda durante a transição

Em produção, hoje, os produtores publicam em v1, e um webhook v2 recebe o envelope remontado a partir dessa mensagem. Nada desta seção vale em sandbox, onde os produtores já publicam v2 e o envelope chega nativo (ver O que muda entre sandbox e produção). A remontagem só tem o que o corpo v1 carrega, e isso tem consequências, todas temporárias, que valem até a virada de cada produtor:

  • data.loan.id chega nulo em todo evento. O corpo v1 nunca levou o identificador da operação na IORQ. Concilie pelo originator_code; para consultar, use a listagem por originator_proposal_code e confira o valor exato no resultado (ver seção 2.1).
  • A decisão de recompra chega com o tipo próprio, salvo a registrada antes de 28/09/2026. No v1, approved e rejected são os mesmos literais para a cessão e para a recompra; a IORQ marca a recompra na mensagem interna, e a remontagem entrega repurchase.approved e repurchase.rejected, com a chave repurchase.approved:loan:<originator_code> (ou repurchase.rejected:loan:<originator_code>). A marca só existe nas mensagens publicadas a partir dessa data: uma decisão de recompra registrada antes de 28/09/2026, se reentregue ou reenviada por replay, ainda chega como loan.approved ou loan.rejected, com a chave da cessão (loan.approved:loan:<originator_code>), a mesma da decisão de cessão daquela operação. Uma integração que começou depois dessa data nunca vê o caso; quem pediu recompra antes dela e for fazer replay desse período trata a colisão consultando a operação. Esta é a única descrição da ressalva; as outras páginas apontam para cá.
  • O id do envelope não é estável. Ele é gerado no momento da entrega, então a mesma tentativa repetida traz um id diferente. Use a idempotency_key, que é derivada do fato e é a mesma em qualquer reentrega do mesmo fato. Ver Idempotência e reentrega.
  • occurred_at é o instante em que o evento entrou na IORQ para entrega, não o instante do fato. Ele não muda entre as tentativas nem num replay, mas vem depois do fato. Não o use para datar o fato; use o estado da operação.
  • Os headers de identificação não são enviados. X-IORQ-Event-Id, X-IORQ-Event-Type e X-IORQ-Delivery-Id acompanham a virada do v2 em produção. O webhook-id, com o mesmo valor do X-IORQ-Event-Id, já chega em toda entrega (ver Configuração e segurança).
  • Um motivo em texto que a IORQ não reconhece chega em reason_text. O v1 leva o motivo operacional como texto, e a remontagem só o devolve a errors[] quando reconhece o texto de um código do catálogo. O caso comum é unexpected_error_processing_operation na recepção de arquivo de lastro, que chega com o texto da exceção em reason_text. Ver Códigos de erro.
  • Um status v1 sem equivalente v2 não é entregue ao webhook v2. É um só: o completed legado, que a administradora emitia para confirmar quatro operações distintas sem dizer qual, e que está fora do catálogo desde que cada fluxo ganhou o seu *_completed (ver Contrato v1 (legado)). Nenhum produtor o emite mais. Se voltasse, a IORQ seguraria a mensagem para tratamento manual em vez de inventar um tipo, e ele chegaria só a webhooks v1; é uma das razões para a migração ser feita com os dois webhooks em paralelo antes do corte.

8. Próximos passos


Did this page help you?