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 + route | Permission | Request | Response (data) |
|---|---|---|---|
POST GetDataTable | NotificationDeadLetters.ViewAll | DtParameters | DtResult<NotificationDeadLetterDto> (unwrapped) |
GET GetDetails/{id} | NotificationDeadLetters.View | — | NotificationDeadLetterDetailsDto |
PUT Requeue | NotificationDeadLetters.Requeue | DeleteDto[] | Guid |
PUT Cancel | NotificationDeadLetters.Cancel | DeleteDto[] | Guid |
DELETE Delete?restore= | NotificationDeadLetters.Delete | DeleteDto[] | 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.