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ão | Corpo entregue | Para quem |
|---|---|---|
v2 | Envelope tipado, um type por fato | Padrã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á rodaEm sandbox os produtores já publicam v2: um webhook v2 recebe o envelope nativo, com
data.loan.idpreenchido e os headersX-IORQ-Event-Id,X-IORQ-Event-TypeeX-IORQ-Delivery-Idem 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 comcontract_version = v2recebe esse envelope remontado, sem os headersX-IORQ-Event-Id,X-IORQ-Event-TypeeX-IORQ-Delivery-Id. A remontagem perde o que o corpo v1 não carrega: em produção, hoje,data.loan.idchega nulo em todo evento, oiddo 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ó ocompletedlegado 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 = v2direto 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.
| Campo | Tipo | O que é |
|---|---|---|
id | string | Identificador do envelope, prefixo evt_. Não é chave de deduplicação (ver abaixo) |
type | string | O que aconteceu, no formato recurso.fato. É o discriminador de data |
schema_version | string | Versão do esquema do envelope. Hoje 2026-10-01 |
occurred_at | string | Instante 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) |
fund | object | id do fundo e, quando disponível, name |
subject | object | Sobre o que é o fato: type e id |
idempotency_key | string | Chave do fato, derivada do tipo e do sujeito. Igual em qualquer reentrega do mesmo fato |
data | object | Os 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 pelaidempotency_key, nunca por ele.subject.typeprevêloan,installment,prepayment,acquisitionecredit_vehicle. Os fatos de hoje usamloan,installmentecredit_vehicle, este nowebhook.test, em quesubject.idé ofund_id(oacquisition.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:
| Campo | Como vem hoje |
|---|---|
data.loan.originator_code | Sempre presente. É o identificador da operação no seu sistema |
data.loan.id | O 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.kind | Sempre boleto. O esquema também prevê pix, que nenhum fato usa hoje |
subject.type | loan, installment e, só no webhook.test, credit_vehicle. prepayment e acquisition não são emitidos por nenhum fato de hoje |
data.loan.idainda chega nulo em produçãoO 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 pelodata.loan.originator_code. Para consultar a operação, use a listagemGET /loan/comoriginator_proposal_codee 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_supportede ounexpected_error_processing_operationque o acompanha;unexpected_error_processing_operationquando 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_codea 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_invalidcominstallment_not_found,repurchase.invalidcomloan_not_foundou com erro antes de resolver a operação, eprepayment.invalidsem 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
*ourecurso.*. - Campos novos no envelope ou dentro de
data. - Valores novos em campos abertos, como um novo
codeemerrors[]ou um novokindde 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.
type | Quando dispara | data | Equivalente v1 (data.status) |
|---|---|---|---|
loan.approved | Gestor aprova o lote de aquisição e a operação segue para a administradora | loan | approved |
loan.rejected | Operação recusada pelo gestor, por você em POST /loan/reject, ou em covenant na renegociação | loan; motivo em errors[] só na reprovação em covenant (E026) | rejected |
loan.invalid | A 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 .invalid | loan, e o motivo em errors[] (operacional) ou reason_text (elegibilidade) | invalid |
acquisition.completed | Administradora confirma a aquisição, e a operação passa a active | loan | acquisition_completed |
installment.settled | Parcela liquidada, primeiro tempo, quando a IORQ já registrou o pagamento | loan, installment, charge quando houve boleto | settled_installment |
installment.settlement_completed | Baixa confirmada pela administradora, segundo tempo, e a parcela vai a settled | loan, e a installment que a IORQ envia hoje neste fato | settlement_completed |
installment.due_date_changed | Nova data de vencimento em vigor no boleto da parcela | loan, installment com due_date | due_date_changed |
installment.settlement_invalid | A 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_error | loan, installment com o code enviado, e o motivo em errors[] | invalid_settlement |
prepayment.received | Liquidação antecipada aceita | loan | received_prepayment |
prepayment.invalid | O pedido de antecipação não foi aceito; nenhuma parcela mudou | loan da primeira operação, installment.number com o code do lote, e o motivo em errors[] | invalid_prepayment |
charge.issued | Bolecode emitido para a parcela ou para o lote de antecipação | loan, installment, charge | issued_bolecode |
charge.cancelled | Bolecode cancelado, por exemplo quando a parcela é substituída por recompra ou renegociação | loan, installment, charge | canceled_bolecode |
repurchase.approved | Gestor aprova a recompra e ela segue para a administradora | loan | approved |
repurchase.rejected | Gestor recusa a recompra, ou a aprova deixando de fora uma operação que ele já tinha recusado | loan | rejected |
repurchase.completed | Administradora confirma a recompra, e a operação vai a repurchased | loan | repurchase_completed |
repurchase.invalid | O pedido de recompra não foi aceito; a operação segue como estava | loan e o motivo em errors[] | invalid_repurchase |
renegotiation.approved | Renegociação aprovada, com a nova operação na carteira | loan | approved_renegotiation |
renegotiation.rejected | Ao aprovar um lote de renegociação, a operação não chegou a aprovada e não é inválida | loan | rejected_renegotiation |
renegotiation.invalid | Falha na renegociação | loan, e o motivo em errors[] ou reason_text | invalid_renegotiation |
renegotiation.completed | Administradora confirma a renegociação, a nova operação vai a active e a antiga a renegotiated | loan | renegotiation_completed |
webhook.test | Você pediu um evento de teste em POST /api/webhook/{webhook_id}/test | vazio; o sujeito é o fundo | test |
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õesNo 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 aidempotency_keyleva 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
errors[] e reason_textQuando há um motivo, ele vem em um dos dois campos, nunca nos dois:
errors[]nas falhas operacionais, com umcodedo catálogo de códigos de erro,titleedescriptioncom o detalhe da ocorrência:installment.settlement_invalid,prepayment.invalid,repurchase.invalide os motivos operacionais deloan.invaliderenegotiation.invalid. UmE0xxsó chega emerrors[]em dois casos: orenegotiation.invaliddo lote de renegociação e oE026deloan.rejected. Programe contra ocode; adescriptioné texto.reason_textem fatos de sucesso que carregam uma confirmação em texto (installment settled successfully), e nas falhas por critérioE0xxemitidas fora do lote: a elegibilidade na entrada da cessão (loan.invalid) e da renegociação, a expiraçãoE023e o documento reprovadoE024(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ão | Recebe |
|---|---|
* | Todos os tipos, presentes e futuros. É o valor padrão |
recurso.* | Todos os tipos daquele recurso, por exemplo installment.* |
tipo.exato | Só 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.idchega nulo em todo evento. O corpo v1 nunca levou o identificador da operação na IORQ. Concilie pelooriginator_code; para consultar, use a listagem pororiginator_proposal_codee 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,
approvederejectedsão os mesmos literais para a cessão e para a recompra; a IORQ marca a recompra na mensagem interna, e a remontagem entregarepurchase.approvederepurchase.rejected, com a chaverepurchase.approved:loan:<originator_code>(ourepurchase.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 comoloan.approvedouloan.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
iddo envelope não é estável. Ele é gerado no momento da entrega, então a mesma tentativa repetida traz umiddiferente. Use aidempotency_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-TypeeX-IORQ-Delivery-Idacompanham a virada do v2 em produção. Owebhook-id, com o mesmo valor doX-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 aerrors[]quando reconhece o texto de um código do catálogo. O caso comum éunexpected_error_processing_operationna recepção de arquivo de lastro, que chega com o texto da exceção emreason_text. Ver Códigos de erro. - Um status v1 sem equivalente v2 não é entregue ao webhook v2. É um só: o
completedlegado, 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
Updated 11 days ago
