Zodiac Tech CRM Docs

How WhatsApp messages are delivered

The status ladder from queued to read, the 24-hour reply window, template vs free-form messages, and how Meta's webhooks update every message.

everyone Medium ⏱ 10 min Updated 2026-10-05

How WhatsApp messages are delivered

Every WhatsApp message in the CRM carries a status: it was queued, it is sending, Meta accepted it, the phone received it, the person read it — or it failed, with a reason. This page traces one message from the inbox (or an automation) to the customer's phone and back, and explains the rule that trips everyone up: the 24-hour reply window.

The status ladder in plain language

Status Meaning
Queued The CRM has accepted the message and it is waiting to be sent
Sending The provider call is in flight
Sent Meta accepted the message for delivery
Delivered The recipient's phone received it
Read The recipient opened the chat (when Meta reports it)
Failed Something went wrong — the reason is stored on the message

Statuses only ever move forward. A late "delivered" webhook cannot overwrite a "read"; nothing downgrades. Failed is terminal.

stateDiagram-v2
    [*] --> QUEUED
    QUEUED --> SENDING
    SENDING --> SENT
    SENT --> DELIVERED
    DELIVERED --> READ
    SENDING --> FAILED
    SENT --> FAILED
    DELIVERED --> FAILED
    FAILED --> [*]

The 24-hour reply window

WhatsApp is strict about who may start a conversation:

  • Inside the window (within 24 hours of the customer's last incoming message): you can send free-form messages — anything your team types in the inbox.
  • Outside the window: you can only send an approved message template (marketing/utility/authentication). Attempting free-form outside the window fails with Meta error 131047 ("re-engagement message").

The CRM tells you which mode you are in:

  • The WhatsApp inbox flags conversations where the window has closed (the composer switches to templates).
  • Automations that send WhatsApp messages describe the template they use; the send path prefers a template descriptor when present.
  • The connection's approved templates are managed under Message templates.

"Failed: 131047" is not a bug If a free-form message failed with 131047, the customer has not written to you in 24 hours. Send an approved template instead — the message failed by WhatsApp's design, not by fault of the CRM.

The technical trace

  1. Queued. Something wants to send: a reply in WhatsApp inbox, an automation's send_whatsapp action (with its guard chain), or a scheduled sweep (appointment + follow-up reminders run every 5 minutes). A message row is created with status queued.
  2. Sending. App\Services\Messaging\ChannelMessageSender builds the payload and calls the WhatsApp Cloud API at https://graph.facebook.com/{version}/{phone_number_id}/messages via App\Services\WhatsApp\MetaCloudWhatsAppProvider. If a template descriptor exists it wins over free-form text.
  3. Sent. Meta's API accepts the message and returns its WhatsApp message id. The message moves to sent.
  4. Status webhooks. Meta posts delivery callbacks for sent, delivered, read, failed to the messaging webhook, received by MessagingWebhookController. Signature is verified (X-Hub-Signature-256).
  5. Monotonic application. App\Services\WhatsApp\WhatsAppMessageService::applyStatus() advances the status using its STATUS_RANK ladder — queued(0) → sending(1) → sent(2) → delivered(3) → read(4); failed(5) is terminal. A status never regresses.
  6. Timeline + UI. Delivered/read/failed events also write activities, so the conversation's timeline in the inbox shows exactly when the customer saw the message — and the failure reason when one exists.
  7. Opt-out. Incoming STOP replies mark the lead as opted out (leads.whatsapp_opted_out_*); future sends to that lead are skipped by the guard chain.

Local install note With QUEUE_CONNECTION=sync (the XAMPP default) the send happens inline in the request — you see "sending → sent" immediately, but there is no retry queue. Production uses the database queue plus a worker; a stopped worker shows up as messages sitting in queued.

Troubleshooting notes

Symptom Likely cause Fix
Messages stuck in Queued Queue worker not running (production) Start/restart the worker; see Cron & queue workers
Message Failed with 131047 Free-form sent outside the 24-hour window Use an approved template
Message Failed with a token/permission error Connection token expired or revoked Reconnect WhatsApp in Connect WhatsApp
Template send fails Template not approved, not synced, or parameter mismatch Sync Templates on the WhatsApp settings page; check the template definition
Status stuck on Sent for days Recipient device offline — normal WhatsApp behaviour Delivered arrives when the phone next connects; "sent" is still valid
No status updates at all Status webhook not subscribed or signature failing Check the WhatsApp webhook configuration and app secret

Was this page helpful?