Idempotência e reentrega

1. Por que ocorre duplicação

Três cenários produzem duplicação legítima:

  • Timeout do seu lado: a IORQ envia o evento, você processa, mas sua resposta 200 demora > 10s para chegar. A IORQ assume falha e reenvia. Seu sistema processou duas vezes.
  • Erro 5xx com efeito colateral: seu endpoint persistiu o evento mas explodiu antes de responder. A IORQ reenvia, sua persistência grava de novo.
  • Reenvio manual: você pediu o redelivery via POST /webhook/event/{event_id}/redeliver para reconciliar uma divergência.

2. Como deduplicar

Cada evento tem um event_id único (campo do body e header X-IORQ-Event-Id). Use-o como chave de deduplicação:

def handle_webhook(event):
    if processed_events.exists(event["event_id"]):
        return 200  # já processado, ack e ignora
    with database.transaction():
        process_event(event)
        processed_events.insert(event["event_id"])
    return 200

Pontos de atenção:

  • Tabela de IDs processados com índice único em event_id — viola constraint = duplicação detectada.
  • Mesmo transaction-scope entre processar o evento e inserir o ID — sem isso, dá race.
  • Retenção mínima de 30 dias — a IORQ pode reenviar dentro dessa janela.

3. Política de retentativa da IORQ

Quando a IORQ não recebe 2xx em até 10s, ela reenvia com backoff exponencial:

TentativaQuando
1Imediato (evento original)
2+ 30 segundos
3+ 2 minutos
4+ 10 minutos
5+ 1 hora
6+ 4 horas
7+ 12 horas
8+ 24 horas

Após 8 tentativas (~42 horas de janela total), o evento entra em delivery_failed e fica disponível para retentativa manual via GET /webhook/{id}/deliveries.

🚧

Não dependa apenas dos webhooks

Se seu endpoint fica fora por mais de 42h, eventos podem ser marcados como falha permanente. Implemente reconciliação periódica via GET /loan/{originator_proposal_code} como salva-vidas.

4. Garantia de ordem

A IORQ garante ordem por originator_proposal_code: você não recebe loan_approved antes de loan_received para a mesma operação. Mas não há ordem global — eventos de operações diferentes podem chegar em qualquer ordem.

Se seu sistema precisa processar em ordem temporal (ex: aging diário), use o campo timestamp do evento, não a hora de recebimento.

5. Reentregas manuais

Você pode forçar a reentrega de um evento específico:

curl -X POST 'https://hs-api.iorq.com.br/webhook/event/evt_9c4a8b2d1e5f/redeliver' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Ou listar entregas falhas em um intervalo:

curl 'https://hs-api.iorq.com.br/webhook/wh_2A8f1b3c4d5e/deliveries?status=failed&since=2026-05-12T00:00:00Z' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Útil em casos de outage seu ou para reconciliar após mudança de schema interno.

6. Idempotência em requests da API

O mesmo princípio vale para chamadas de você para a IORQ. Todo endpoint mutador aceita uma chave:

EndpointChave
POST /loanoriginator_proposal_code
POST /installment/settleidempotency_key
POST /installment/prepaymentidempotency_key
POST /loan/repurchaseidempotency_key
PATCH /loan/renegotiateoriginal_proposal_code

Reenviar com a mesma chave e payload idêntico retorna o estado atual sem duplicar. Reenviar com a mesma chave e payload conflitante retorna 409.

7. Próximos passos