Skip to main content
Version: 1.2

9.7 Notification Dead Letters

Controller: NotificationDeadLettersController · Route: api/v1/NotificationDeadLetters (plural — renamed from the singular NotificationDeadLetterController used in early phase reports) · GroupName: notifications · 5 endpoints · The terminal store for notifications whose retries were exhausted or that failed permanently. See Chapter 12.

Verb + routePermissionRequestResponse (data)
POST GetDataTableNotificationDeadLetters.ViewAllDtParametersDtResult<NotificationDeadLetterDto> (unwrapped)
GET GetDetails/{id}NotificationDeadLetters.ViewNotificationDeadLetterDetailsDto
PUT RequeueNotificationDeadLetters.RequeueDeleteDto[]Guid
PUT CancelNotificationDeadLetters.CancelDeleteDto[]Guid
DELETE Delete?restore=NotificationDeadLetters.DeleteDeleteDto[]Guid

GET GetDetails/{id} — response shape

{
"succeeded": true,
"message": null,
"data": {
"id": "7d8e9f00-0000-0000-0000-000000000001",
"notificationId": "8b1e2f40-0000-0000-0000-000000000001",
"channel": 2,
"recipient": "966549000191",
"eventKey": "SubscriptionExpiryReminder",
"reasonCode": "SMS_GATEWAY_REJECTED",
"reason": "SMS gateway rejected the message after 3 retry attempts.",
"exceptionDetails": "FourJawaly API returned HTTP 422: invalid recipient format",
"retryCount": 3,
"lastAttemptOn": "2026-07-04T08:40:00Z",
"payloadSnapshot": "{\"eventKey\":\"SubscriptionExpiryReminder\",\"recipientUserId\":\"...\",\"data\":{...}}",
"correlationId": "corr-4b5c6d7e",
"status": 0,
"requeuedOn": null,
"cancelledOn": null,
"createdBy": "System",
"createdOn": "2026-07-04T08:40:05Z"
}
}

status (NotificationDeadLetterStatus): Pending=0, Requeued=1, Cancelled=2. payloadSnapshot preserves the exact DispatchNotificationRequest JSON that failed, so a requeue can reconstruct the original dispatch without asking the caller to resend it.

PUT Requeue — resurrect a dead letter as a fresh retry attempt

Request:

[ { "id": "7d8e9f00-0000-0000-0000-000000000001" } ]

Response:

{ "succeeded": true, "message": "Dead letter entry requeued", "data": "7d8e9f00-0000-0000-0000-000000000001" }

Business notes: sets Status = Requeued, RequeuedOn = now, and creates a new NotificationRetryQueue row from PayloadSnapshot with a reset AttemptNumber — it does not mutate the original dead-letter row's history away, keeping a permanent audit trail of "this failed, an operator chose to retry it, here's what happened on the new attempt."

PUT Cancel

Sets Status = Cancelled, CancelledOn = now. A cancelled dead letter is retained (not deleted) and will never be requeued automatically.

DELETE Delete

Soft-deletes (or restores with ?restore=true) the dead-letter record itself — distinct from Cancel, which changes business status but keeps the record visible in the default grid.

Retention policy

Dead letters older than DeadLetterRetentionDays (default 90, via NotificationRetentionSettings) are cleaned up automatically by the notification-deadletter-cleanup-pipeline Hangfire job (daily, 02:00 UTC) — Pending-status entries are never auto-deleted regardless of age, only Requeued/Cancelled ones. See Chapter 12.

Angular: an operator dashboard, typically grouped by ReasonCode/EventKey to spot systemic provider issues (e.g. a spike of SMS_GATEWAY_REJECTED suggests a gateway account problem, not isolated bad numbers). Related APIs: Retry Queue, Analytics.