Skip to main content
Version: 1.2

9.11 Notification Delivery Receipts

Controller: NotificationDeliveryReceiptController · Route: api/v1/NotificationDeliveryReceipt · GroupName: notifications.receipts · 5 endpoints, all read-only. Delivery receipts capture provider-reported, asynchronous delivery status — distinct from the synchronous ChannelDeliveryResult recorded at dispatch time. Today this is populated exclusively by the WhatsApp inbound webhook (Meta delivery-status callbacks); it is not a general outbound webhook-subscription feature — see §9.11.3.

Verb + routePermissionRequestResponse (data)
POST GetDataTableNotificationReceipts.ViewAllDtParametersDtResult<NotificationDeliveryReceiptDto> (unwrapped)
GET GetDetails/{id}NotificationReceipts.ViewNotificationDeliveryReceiptDetailsDto
GET GetByProviderMessageId/{providerMessageId}NotificationReceipts.ViewrouteNotificationDeliveryReceiptDto[]
GET GetSummary?from=&to=NotificationReceipts.Viewquery DateTime?NotificationReceiptSummaryDto
GET GetTimeline?from=&to=NotificationReceipts.Viewquery DateTime?NotificationReceiptTimelineDto[]

PermissionConstants.NotificationReceipts.Export is defined but not referenced by any action here — likely reserved for a future export/download endpoint.

9.11.1 How a receipt gets created — inbound webhook, not this controller

There is no POST/PUT action on this controller — receipts are never created through this API surface. They are written by the existing WhatsAppWebhookController (outside this controller, part of the WhatsApp integration surface), which:

  1. Validates the Meta webhook signature (HMAC-SHA256 against WhatsAppCloudApi.AppSecret).
  2. Runs its pre-existing ProcessWebhookStatusAsync logic (unchanged).
  3. Additionally (added later, try/catch-isolated so a receipt-mapping bug can never break webhook processing) calls INotificationDeliveryReceiptService.UpsertAsync to record/update a NotificationDeliveryReceipt row via WhatsAppMetaReceiptMapper (INotificationReceiptMapper).

GET GetDetails/{id} — response shape

{
"succeeded": true,
"message": null,
"data": {
"id": "5b6c7d80-0000-0000-0000-000000000001",
"notificationId": "8b1e2f40-0000-0000-0000-000000000002",
"deliveryAttemptId": "6c7d8e90-0000-0000-0000-000000000001",
"campaignId": null,
"campaignStepId": null,
"providerName": "MetaWhatsApp",
"providerMessageId": "wamid.HBgLOTY2NTQ5MDAwMTkxFQIAERgSNzc4RUY2QjM3RjM4RjM4RjM4AA==",
"channel": 6,
"recipient": "966549000191",
"eventKey": "PurchaseInvoicePosted",
"receiptStatus": 2,
"providerStatus": "delivered",
"providerErrorCode": null,
"providerErrorMessage": null,
"receivedOn": "2026-07-04T09:13:40Z",
"providerTimestamp": "2026-07-04T09:13:38Z",
"correlationId": "corr-9f1a2b3c",
"createdBy": "System",
"createdOn": "2026-07-04T09:13:40Z"
}
}

receiptStatus (NotificationReceiptStatus): Unknown=0, Sent=1, Delivered=2, Read=3, Opened=4, Clicked=5, Failed=6, Rejected=7, Expired=8, Bounced=9, Unsubscribed=10.

GET GetSummary

{
"succeeded": true,
"message": null,
"data": {
"totalReceipts": 4200,
"totalSent": 4200,
"totalDelivered": 4110,
"totalRead": 3502,
"totalOpened": 0,
"totalClicked": 0,
"totalFailed": 90,
"totalBounced": 0,
"totalRejected": 0,
"deliveryRate": 97.86,
"readRate": 83.38,
"clickRate": 0,
"failureRate": 2.14,
"dateFrom": "2026-06-27T00:00:00Z",
"dateTo": "2026-07-04T23:59:59Z",
"byChannel": [
{ "channel": 6, "totalReceipts": 4200, "totalSent": 4200, "totalDelivered": 4110, "totalRead": 3502, "totalFailed": 90, "deliveryRate": 97.86, "readRate": 83.38 }
]
}
}

9.11.3 Inbound vs outbound — what this is not

This is not an outbound webhook-subscription feature (i.e. Shumoul does not let customers register a URL to be called when a delivery status changes). "Webhook" here means exclusively the inbound Meta → Shumoul provider callback. Email/SMS/Push provider delivery-status webhooks are documented as future extension points in earlier phase reports, but no controller exists for them today — do not represent them as implemented.

Angular: a delivery-receipts drill-down view, typically linked from a specific NotificationDeliveryAttempt or campaign step via GetByProviderMessageId. Related APIs: Analytics, Campaigns, Chapter 7 § WhatsApp.