Webhook Dispatcher — Domain Model
Status: populated
Owner: Platform Engineering
Last updated: 2026-04-18
Companion: SERVICE_OVERVIEW · DATA_MODEL · EVENT_SCHEMAS
1. Core Aggregates
WebhookConfig (root)
Represents a customer-registered webhook endpoint. Owns the signing secret and URL.
| Field | Type | Notes |
|---|
webhookId | WebhookId (UUIDv4) | Primary key |
accountId | AccountId (UUIDv4) | Owner account; isolation boundary |
url | WebhookUrl (VO) | HTTPS URL; validated at registration |
secret | WebhookSecret (VO) | HMAC-SHA256 signing key; stored encrypted |
description | string | null | Human-readable label |
events | WebhookEventType[] | Event filter (e.g. ['DLR_DELIVERED', 'DLR_FAILED']) |
isActive | boolean | Soft-disable without deletion |
createdAt | Instant | Registration timestamp |
updatedAt | Instant | Last modification |
DeliveryAttempt (root)
Represents one delivery attempt for a single webhook.dispatch event to a single WebhookConfig. Accumulates per-attempt results.
| Field | Type | Notes |
|---|
attemptId | AttemptId (UUIDv4) | Primary key |
deliveryId | DeliveryId (UUIDv4) | Groups all attempts for the same event+webhook |
webhookId | WebhookId | Target webhook |
accountId | AccountId | Owner account |
eventId | string | Source webhook.dispatch.eventId (dedup key) |
attemptNumber | 1..5 | Current attempt |
status | DeliveryStatus | See §3 |
httpStatusCode | number | null | Response status code |
responseBodyPreview | string | null | First 512 chars of response |
errorMessage | string | null | Network error or timeout message |
scheduledAt | Instant | When this attempt was/is due |
attemptedAt | Instant | null | When HTTP request was made |
nextRetryAt | Instant | null | Null for final attempt or SUCCESS |
2. Value Objects
| VO | Invariant |
|---|
WebhookId | UUIDv4 branded |
DeliveryId | UUIDv4 branded; stable across all retries of same event |
WebhookUrl | HTTPS only; valid URL; max 2048 chars |
WebhookSecret | Non-empty; 16–128 chars; stored AES-256-GCM encrypted |
WebhookEventType | Enum: DLR_DELIVERED, DLR_FAILED, DLR_UNDELIVERED, DLR_EXPIRED, DLR_REJECTED, DLR_UNKNOWN |
3. DeliveryStatus Enum
| Status | Meaning |
|---|
PENDING | Queued for first attempt or retry |
IN_FLIGHT | HTTP request in progress |
SUCCESS | 2xx response received |
FAILED_RETRY | Non-2xx or timeout; retry scheduled |
DEAD_LETTER | Max attempts exhausted; no further retries |
4. Retry Schedule
| Attempt | Delay after previous failure |
|---|
| 1 | Immediate (from event receipt) |
| 2 | 30 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| — (after attempt 5 fails) | 8 hours — dead-letter after this |
Total retry window: ≈ 10.5 hours from first attempt.
5. Domain Events (internal)
| Event | Trigger |
|---|
WebhookDelivered | 2xx response on any attempt |
WebhookRetryScheduled | Non-2xx or timeout; attempt < 5 |
WebhookDeadLettered | Attempt 5 fails |
WebhookConfigCreated | New webhook registered |
WebhookConfigDeactivated | isActive set to false |
6. Invariants
deliveryId is stable: all retry attempts for the same (eventId, webhookId) pair share the same deliveryId.
DeliveryStatus transitions are monotone: no transition from SUCCESS or DEAD_LETTER.
WebhookUrl must use HTTPS scheme; plain HTTP rejected at registration.
- A
WebhookConfig with isActive = false receives no dispatch attempts.
- Maximum 10
WebhookConfig records per accountId.