Skip to main content

Broadcasts REST API

Endpoints for monitoring and managing email broadcasts. Broadcasts are bulk email sends to a show’s subscriber list (or a filtered subset). No premium license is required to use these endpoints.

All endpoints require manage_options and a valid X-WP-Nonce header.


GET /benecaster/v1/broadcasts

Returns a paginated list of broadcast records for a show. Every broadcast sent is listed, including one that mailed nobody because every recipient had opted out, and one whose recipient rows the retention purge has since deleted (it keeps its sent and failed counts).

Auth: manage_options + X-WP-Nonce

Parameters:

Param Type Default Description
show_id int 0 Show to list broadcasts for. 0 lists every show’s broadcasts
page int 1 Page number
per_page int 20 Items per page (max 100)

Response:

{
  "items": [
    {
      "id": 12,
      "show_id": 45,
      "subject": "New episode: Season finale is here",
      "preheader": "Forty minutes on the last day of shooting",
      "audiences": ["subscribers", "followers"],
      "author_id": 1,
      "created_at": "2026-05-17 09:00:00",
      "completed_at": "2026-05-17 09:06:00",
      "recipients": 261,
      "suppressed": 12,
      "skipped": 2,
      "total": 247,
      "sent": 245,
      "failed": 2,
      "pending": 0,
      "body_purged": false,
      "status": "complete_with_failures"
    }
  ],
  "total": 8
}

Newest first. The response is items and total only.

Field Meaning
audiences Who it was addressed to, as the audience keys it was sent with
author_id WordPress user who sent it
recipients Everyone the audiences resolved to, set when the broadcast is sent
suppressed Not mailed because they had opted out
skipped Not mailed because they had no email address, or the email could not be queued
total Recipients handed to the email queue so far
sent / failed / pending Queue progress. pending counts recipients still queued or being sent
completed_at When the last recipient was handed to the queue; null until then
body_purged true once the retention purge has deleted the message body

Recipients are handed to the queue in the background. benecaster_send_broadcast() returns at once, and scheduled tasks then queue the recipients in batches of 100. Until the last batch, the broadcast lists as sending, with recipients already the full audience while total, suppressed and skipped rise batch by batch, and completed_at is null. On a large audience this can take a few minutes, longer on a site whose scheduled tasks run only when someone visits. created_at is when the broadcast was queued, as a UTC Y-m-d H:i:s string with no zone marker; treat it as UTC before parsing, the same as sent_at below.

Status values:

Status Meaning
queued Every recipient handed to the queue; nothing sent or failed yet
sending Still handing recipients to the queue, or some recipients done and others waiting
complete Every recipient sent, none failed. Also a broadcast that mailed nobody, with suppressed saying why
complete_with_failures Every recipient finished; one or more failed

There is no status filter: the status is worked out from each broadcast’s counts.

The Broadcasts admin screen polls this endpoint every 30 seconds while any broadcast has status queued or sending.


GET /benecaster/v1/broadcasts/{id}/recipients

Returns per-recipient delivery status for a broadcast, plus a summary of who was not mailed. Answers 404 only for an id with no broadcast record; a broadcast with no recipient rows (everyone opted out, or the first batch has not run yet) gets an empty list with 200.

Auth: manage_options + X-WP-Nonce

Parameters:

Param Type Default Description
page int 1 Page number
per_page int 50 Items per page (max 200)
status string — Filter by status: pending, sending, sent, failed, or in_progress (pending and sending together). Omit to list every recipient

Response:

{
  "items": [
    {
      "queue_id": 4821,
      "user_id": 112,
      "display_name": "Sarah Chen",
      "email": "sarah@example.com",
      "status": "sent",
      "attempts": 1,
      "last_error": null,
      "sent_at": "2026-05-17 09:03:00",
      "created_at": "2026-05-17 09:00:00"
    },
    {
      "queue_id": 4822,
      "user_id": 113,
      "display_name": "Bob Martinez",
      "email": "bob@example.com",
      "status": "failed",
      "attempts": 3,
      "last_error": "SMTP connection timeout",
      "sent_at": null,
      "created_at": "2026-05-17 09:00:00"
    }
  ],
  "total": 247,
  "summary": {
    "recipients": 261,
    "suppressed": 12,
    "skipped": 2,
    "status": "sent"
  }
}

Work out the page count from total and the per_page you asked for.

summary comes from the broadcast record, not the recipient rows, and ignores the status filter. It is the only place one broadcast’s not-mailed figures are returned, because opted-out and skipped listeners have no rows. Its status is the record’s own: sending while recipients are still being handed to the queue, sent once all have been, or '' if the record could not be read. It is what tells apart the two cases with no rows at all: sent means the broadcast finished having queued nobody, sending means its first batch has not run yet. Rows come oldest first.

sent_at and created_at are UTC, with no zone marker. Both are Y-m-d H:i:s strings; sent_at is null until that recipient has been sent, and created_at is when they were queued. Treat them as UTC before parsing: for example, replace the space with T and append Z. Parsed as they stand, most JavaScript runtimes read them as the viewer’s local time and show the wrong hour.

display_name and email are read from the recipient’s WordPress account, and are empty strings for a recipient whose account has since been deleted.

Recipient status values:

Status Meaning
pending Queued, including a failed recipient put back by a retry — unless they have opted out since, in which case the retry leaves them failed (see below)
sending Being sent now
sent Handed to your mail server
failed All delivery attempts exhausted; last_error says why

POST /benecaster/v1/broadcasts/{id}/retry-all

Puts every failed recipient row in a broadcast back to pending, so it is sent again. Recipients that already succeeded are unaffected.

A recipient who has opted out since their copy failed is not retried. Their row stays failed, last_error reads Not retried: the recipient has opted out (…), and the email log records the email as suppressed. A retry never reaches someone who has asked to stop.

Auth: manage_options + X-WP-Nonce

Response:

{
  "count": 2
}

count is the number of rows put back to pending. It is 0 when there is nothing to retry, including for a broadcast ID with no rows.


POST /benecaster/v1/broadcasts/{id}/recipients/{qid}/retry

Puts one failed recipient row back to pending, so it is sent again. {qid} is the recipient row ID from GET /broadcasts/{id}/recipients.

Auth: manage_options + X-WP-Nonce

Response:

{
  "success": true
}

Errors:

Code HTTP Condition
not_retryable 400 The row was not found, is not failed, or its recipient has opted out since — in that last case it stays failed with Not retried: the recipient has opted out (…) in last_error

Recipe

Recipe: Query Broadcast Delivery Status from an External Tool — use GET /broadcasts and GET /broadcasts/{id}/recipients from an external script or CI pipeline to monitor delivery after a scheduled send.

See Also

Need this built rather than just documented? See our services →