Pular para o conteúdo principal

WebhookDelivery

Um WebhookDelivery é uma tentativa de POST de payload de webhook para sua URL alvo. O dispatcher cria uma linha por tentativa; retries produzem linhas adicionais ligadas ao mesmo system_event_id.

Propósito​

  • Prover um histórico auditável de toda tentativa (status, payload, response, duração).
  • Permitir replay de entregas falhas sem reemitir o evento subjacente.
  • Alimentar a UI de webhook deliveries em /manage.

Schema​

CampoTipoDescrição
idUUIDID interno.
webhook_idUUIDWebhook dono.
system_event_idUUIDO evento sendo entregue.
attemptintContador de retry 1-indexed.
statusenumpending, success, failed.
request_headersobjectHeaders de saída (auth values redigidos).
request_bodyobjectPayload JSON enviado.
response_statusint | nullStatus HTTP recebido.
response_headersobject | nullHeaders do alvo (capped em 4 KiB).
response_bodystring | nullBody do alvo (capped em 16 KiB).
errorstring | nullErro de transporte (timeout, DNS, TLS).
duration_msint | nullWall time da chamada HTTP.
delivered_atdatetime | nullQuando a chamada terminou.
next_retry_atdatetime | nullQuando o próximo retry está agendado (se houver).
created_at / updated_atdatetime

Status​

StatusSignificado
pendingEnfileirada ou em vôo; delivered_at não setado.
success2xx recebido dentro do timeout.
failedNão-2xx, timeout ou erro de transporte. Pode retry — confira next_retry_at.

Exemplo​

{
"id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"webhook_id": "f6a7b8c9-0d1e-4f2a-3b4c-5d6e7f8a9b0c",
"system_event_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"attempt": 2,
"status": "success",
"request_headers": {
"X-Kobana-Event": "email.db.created",
"X-Kobana-Timestamp": "1747490400",
"X-Kobana-Signature": "sha256=…",
"Authorization": "Bearer [REDACTED]"
},
"request_body": { "event": "email.db.created", "data": {} },
"response_status": 200,
"response_headers": { "content-type": "application/json" },
"response_body": "{\"ok\":true}",
"error": null,
"duration_ms": 312,
"delivered_at": "2026-05-17T12:05:14.218Z",
"next_retry_at": null,
"created_at": "2026-05-17T12:05:13.906Z",
"updated_at": "2026-05-17T12:05:14.218Z"
}

Leitura​

MétodoPath
GET/webhooks/{id}/deliveries?status=failed&attempt[gte]=2
GET/webhooks/{id}/deliveries/{deliveryId}

Filtros: status, attempt[gte|lt], created_at[gte|lt].

Replay​

POST /webhooks/{id}/deliveries/{deliveryId}/replay enfileira uma redelivery imediata — ignora qualquer backoff pendente mas é registrado como uma nova linha (attempt não é incrementado; uma nova WebhookDelivery é appended com attempt=1 e uma referência metadata.replay_of).

Quando qualquer entrega de um evento a um webhook tem sucesso — replay incluído — as retentativas ainda agendadas para esse evento não são enviadas, e o next_retry_at das linhas failed anteriores é limpo. Um replay bem-sucedido, portanto, encerra a cadeia de backoff da entrega original.

A requisição do replay também carrega seu próprio X-Idempotency-Key, um valor novo e aleatório em vez do id do evento que toda tentativa automática usa (veja Headers). Retentativas automáticas continuam deduplicadas contra a tentativa original de propósito; um replay é um pedido deliberado de agir sobre o evento de novo, então não deve ser engolido pela mesma guarda.