Skip to main content
Version: Latest

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.View—NotificationDeadLetterDetailsDto
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.