On this page
- Before you start
- Steps
- What you will see
- The full status list
- Why does "Sent" on the funnel include recipients that have since moved to Delivered or Read?
- Why does a status never move backward?
- Settings and options
- Why does "holdout" exist as its own status rather than just "not sent"?
- Troubleshooting
- Related reading

Every recipient of a VGraple CRM broadcast carries one of eight explicit statuses (pending, sent, held, delivered, read, failed, suppressed, cancelled) or skipped_quota, forming a real state machine rather than a single flat "sent" count. This article defines each one precisely and shows how they roll up into the funnel tabs on a campaign's detail page.
Recipient statuses
- 1Open a campaign's detail page
- 2Read the filter tabs above the recipient
- 3Click a tab to see exactly who
- 4Check the Status column for an individual
- 5Hover or check the Error column for
Before you start
- No setup is needed; every broadcast recipient carries one of these statuses automatically.
- Open any completed or in-progress campaign's detail page to see the statuses in the recipient table and the filter tabs above it.
- Understand that a status is a property of one recipient's one attempt at this specific campaign, not the contact's overall history with your business.
Steps
- Open a campaign's detail page. From Broadcasts, click into any campaign to see its funnel and recipient table.

Read the filter tabs above the recipient table. They are: All, Pending, Sent, Held by Meta, Delivered, Read, Replied, Clicked, Suppressed, Failed, Cancelled, Over daily limit, Holdout. Each tab, except All, Replied and Clicked, shows a count in parentheses.
Click a tab to see exactly who is in that state. The recipient table filters instantly and stays searchable by name or phone within that filter.
Check the Status column for an individual recipient. The recipient table's Status column shows the current literal status, colored (green for read, blue for delivered, amber for suppressed, held or skipped, red for failed, gray for pending or cancelled), with a retry count shown if the recipient has been retried.
Hover or check the Error column for a failed or suppressed recipient. It shows the human-readable reason (and, on export, the Meta error code where one exists) for exactly why that recipient did not receive the message.
What you will see
The stat tiles at the top of the detail page (Recipients, Sent, Delivered, Read, Replied, Failed, and Held or Suppressed when either is nonzero) summarize the same underlying statuses the filter tabs use. A fully successful small campaign shows most recipients moving quickly from Sent through Delivered to Read; a campaign with a significant Suppressed count is usually working correctly, not failing, since suppression means the platform is protecting your number rather than something going wrong.
The full status list
| Status | What it means | Counts as sent? | Billed? | Ever retried automatically? |
|---|---|---|---|---|
| pending | Queued, not yet attempted | No | No | Not applicable, it has not run yet |
| sent | Meta accepted the message | Yes | Yes | N/A, this is a success state |
| held | Meta accepted it but is withholding delivery, usually during template quality review | Yes (rolls into Sent) | Yes | N/A, resolves on its own through delivery webhooks |
| delivered | Reached the recipient's device | Yes | Yes | N/A |
| read | Opened by the recipient | Yes | Yes | N/A |
| failed | Meta rejected the send, or a delivery error arrived later | No | No | Yes, via "Retry failed," up to 3 attempts |
| suppressed | VGraple CRM's own pre-flight check refused to send | No | No | No, never, by design |
| cancelled | The broadcast was stopped before this recipient's turn | No | No | Yes, via "Retry failed" |
| skipped_quota | The daily messaging limit ran out and auto-continue was off | No | No | Yes, via "Retry failed," once headroom returns |
| holdout | Deliberately withheld as a control group for measuring the campaign's effect | No | No | No, this is intentional and permanent for this campaign |
Why does "Sent" on the funnel include recipients that have since moved to Delivered or Read?
The Sent tab and the sent_count on a campaign mean "accepted by WhatsApp and beyond," not the literal current database value, because delivered and read are strictly later stages of an accepted message. An earlier, simpler implementation of this kind of feature commonly matches "sent" to only the literal status value, which meant the Sent tab hid every recipient whose status had since been moved to delivered or read by an incoming webhook, and a "not yet read" cohort silently excluded delivered recipients that had genuinely not been read yet. VGraple CRM's status logic treats sent, held, delivered and read as one connected group (all "accepted"), so every count and every tab agrees on what accepted actually means, and a fully delivered campaign's Sent tab correctly shows the full number rather than a shrinking one as webhooks arrive.
Why does a status never move backward?
Treating recipient status as a strict, forward-only state machine (pending to sent to delivered to read, or pending straight to suppressed, cancelled, failed or skipped_quota, with a retry starting a fresh attempt back at pending) means the funnel on a campaign's detail page can never contradict itself. A recipient who was briefly marked failed due to a transient network error and then successfully retried does not silently overwrite its own history; the retry count increments and the row genuinely restarts its journey from pending, so the campaign's record of "this recipient needed 2 attempts before delivering" stays intact and visible rather than being erased by the second attempt's eventual success. This same discipline is what makes the exported CSV, the recipient table, and the funnel's stat tiles all agree with each other at any moment, since all three read from the identical underlying status rather than three different derived views that could drift out of sync.
Settings and options
| Concept | What it does | Where to see it |
|---|---|---|
| Filter tabs | Narrow the recipient table to one status group | Top of the recipient table on the campaign detail page |
| Search | Find a specific recipient by name or phone within the current filter | Search box next to the filter tabs |
| Retry count | How many times a recipient has been re-attempted | Shown next to Status for a retried recipient |
| Error reason | Human-readable explanation for a failed or suppressed recipient | Error column in the recipient table |
Why does "holdout" exist as its own status rather than just "not sent"?
A holdout recipient is deliberately, intentionally never sent to, set aside specifically so a campaign's real effect can be measured against people who received nothing at all, a control group in the statistical sense. Lumping holdout recipients into a generic "not sent" or "skipped" bucket alongside suppressed and cancelled rows would make it impossible to tell, later, whether a recipient was left out because something went wrong or because they were always meant to be part of the comparison. Keeping holdout as its own distinct status means a campaign's revenue attribution and delivery figures can be compared cleanly against the holdout group's own (necessarily zero-send) outcome, answering "did this campaign actually cause anything" rather than just "did this campaign reach people."
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| A recipient shows "sent" but you expected "delivered" | The delivery webhook has not arrived yet, which can take a few seconds to minutes | Wait and refresh; the recipient's row updates automatically once the webhook arrives |
| Large number of recipients show "held" | New or recently edited templates go through Meta's quality assessment period, during which accepted messages are paced | Expected and temporary; held messages still resolve to delivered and read through the normal webhooks |
| A recipient shows "suppressed" that you expected to receive the message | The pre-flight check found an opt-out, a block, a frequency-cap window, or no valid WhatsApp number for that contact | Check the contact's own profile for opt-in and blocked status; see why a recipient was suppressed |
| "Failed" recipients did not get retried automatically | Retries are manual, triggered by the "Retry failed" action on the campaign | Use Retry failed on the campaign detail page; up to 3 attempts per recipient |
| Some recipients show skipped_quota after a large campaign | The daily portfolio messaging limit was reached and auto-continue was off for that campaign | Use Retry failed once daily headroom returns to attempt them |
Related reading
See why a recipient was suppressed for the full list of suppression causes, and retry failed vs retarget for how to act on any of these statuses after a campaign finishes.