Skip to main content
Version: Latest

6.1 WhatsAppWebhookController

Route: api/v{version:apiVersion}/notifications · Class attributes: [AllowAnonymous] · 2 endpoints

The only controller in this reference that does not require a Shumoul auth token — Meta calls it directly. Security is provided by webhook signature validation instead (see §8 Webhooks and §11 Security).


GET webhook

Purpose: Meta's one-time webhook URL verification handshake, run when a developer configures or re-verifies the webhook URL in the Meta App Dashboard. Authorization: none ([AllowAnonymous]) · Permission: none

Request (query string):

GET /api/v1/notifications/webhook?hub.mode=subscribe&hub.verify_token=ShumoulWhatsAppWebhook2026&hub.challenge=1158201444

Response (success): 200 OK, plain-text body = the hub.challenge value, echoed verbatim. Response (failure): 403 Forbidden if hub.verify_token does not match the configured VerifyToken.

Error cases:

  • Missing/incorrect hub.verify_token403, no body.
  • hub.mode other than subscribe → treated as failure per Meta's own contract.

Backend service: none — compares directly against WhatsAppCloudApiOptions.VerifyToken. Calls Meta API: No. Angular/Flutter usage: Not applicable — this endpoint is called only by Meta's servers during webhook configuration, never by a Shumoul client application.


POST webhook

Purpose: receives every inbound WhatsApp event from Meta — new messages and delivery/read status updates. Authorization: none ([AllowAnonymous]) · Permission: none

Request: raw JSON body (Meta's webhook payload shape — see §8 Webhooks) plus the X-Hub-Signature-256 header. Example (inbound text message):

{
"object": "whatsapp_business_account",
"entry": [{
"id": "2091544401399393",
"changes": [{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": { "display_phone_number": "966501234567", "phone_number_id": "1088505757690326" },
"contacts": [{ "profile": { "name": "Ahmed Al-Otaibi" }, "wa_id": "966501112222" }],
"messages": [{
"from": "966501112222",
"id": "wamid.HBgLOTY2NTAxMTEyMjIyFQIAEhggMTIzNA==",
"timestamp": "1751500000",
"type": "text",
"text": { "body": "Hello, I have a question about my order" }
}]
}
}]
}]
}

Response: always 200 OK for a structurally valid, signature-valid request — regardless of whether any individual message/status item fails to process. 403 Forbidden only on signature validation failure.

Error cases:

  • Missing or invalid X-Hub-Signature-256403 (when SignatureValidationEnabled is true).
  • Malformed JSON → parser returns an empty WhatsAppWebhookParseResult, controller still returns 200 (Meta must not receive a retry-triggering non-2xx for a payload it will only ever send once, malformed or not).
  • A message referencing an unknown/deleted conversation phone number → a new WhatsAppConversation is created; processing does not fail.

Backend service: IWhatsAppSignatureValidator.ValidateIWhatsAppWebhookParser.ParseAsyncIWhatsAppInboxService.ProcessWebhookMessageAsync / ProcessWebhookStatusAsyncINotificationDeliveryReceiptService.UpsertAsync. Calls Meta API: No (this endpoint only receives; it never calls out to Meta). Angular/Flutter usage: Not applicable — this endpoint is called only by Meta's servers. Frontend clients observe the results of webhook processing via SignalR and the Inbox REST endpoints, not this endpoint directly.