Skip to main content
Version: Latest

5. Runtime Flows

Every flow below is verified against the current, post–Phase 1 source code — no invented steps.

5.1 Send Template Message (Communication Module / Business Document)

5.2 Send OTP

OTP codes are never logged — TenantNotificationLog stores the literal string "Template:{templateName}" as the message body, never the rendered parameter values.

5.3 Send Activation / Welcome Message

Identical shape to §5.2, triggered by TenantSubscriptionActivatedEventSendSubscriptionActivatedWhatsAppHandlerWhatsAppActivationMessageService.SendTenantActivationMessageAsync, using the welcome_activation_ar/welcome_activation_en templates with two body parameters (customer name, organization name).

5.4 Send Invoice (Business Document)

Same code path as §5.1 — an invoice send is CommunicationService.SendDocumentAsync with Channel = WhatsApp and DocumentType = "Invoice". No separate implementation exists; it is documented here only to explicitly confirm coverage of this flow.

5.5 Receive Webhook

The controller always returns 200 OK for a structurally valid, signature-valid request regardless of per-item processing failures — Meta retries on non-2xx, so swallowing per-item errors after acknowledging receipt avoids duplicate-delivery storms.

5.6 Verify Webhook

5.7 Reply From Inbox

Free-text replies (SendReply) are only permitted inside Meta's 24-hour customer-service window (CanSendFreeText on WhatsAppConversationDto); outside that window, only an approved template reply (SendTemplateReply) is allowed — this is a Meta platform rule, not a Shumoul business rule.

5.8 Media Upload / Send / Download

Outbound ("upload"/"send"): this codebase has no separate Meta media-upload endpoint call — outbound media is attached to a template component as a public URL (ImageUrl/DocumentUrl/VideoUrl on WhatsAppTemplateComponentParameterDto). "Upload" and "send" are therefore the same flow as §5.1/§5.7.

Inbound (download):

The Meta CDN URL is never exposed to the frontend — the backend always proxies the bytes through GetMediaInfoAsync/DownloadMediaAsync.

5.9 Delivery Status Update

Two independent consumers of the same webhook status event (see §5.5):

  1. WhatsAppInboxService.ProcessWebhookStatusAsync — updates WhatsAppMessage.MessageStatus for the Inbox UI.
  2. INotificationDeliveryReceiptService.UpsertAsync — feeds the Notification Framework's generic delivery-receipt/retry pipeline via WhatsAppMetaReceiptMapper's status-string mapping.

5.10 Error Handling

  • Meta API errors (non-2xx or an "error" object in the response body): parsed identically by WhatsAppCloudApiClient for every send path, since Phase 1 made it the single client. The (Success, MessageId, ErrorMessage)/(Success, ExternalId, ErrorMessage) tuple shape is consistent across all callers.
  • Notification Framework retry/dead-letter path: NotificationFailureClassifier's Meta-error-code heuristics decide whether a failed dispatch is retryable — unrelated to and untouched by the WhatsApp library itself.
  • Webhook processing errors: always per-item try/catch; the controller acknowledges Meta with 200 OK regardless of individual parse/persist failures (see §5.5).

5.11 Tenant Credentials

Platform-wide credentials (WhatsAppCloudApiOptions) and tenant-scoped credentials (WhatsAppOperationalSettings) are two entirely separate configuration surfaces — see §4 Configuration. Both ultimately feed the same WhatsAppCloudApiClient class, just with different WhatsAppCloudApiOptions instances constructed at the point of use.