Email Queue REST API
A single endpoint for polling the status of an email job group. Used by the Migration Wizard status panel, the Broadcasts admin screen and Benecaster’s admin sending banner to show real-time delivery progress. An add-on can put its own job in that banner; see Showing Your Job in the Admin Banner.
GET /benecaster/v1/shows/{id}/email-queue-status
Returns the current delivery status for a job group — a set of email queue rows identified by a shared related_id and type.
Auth: manage_options + X-WP-Nonce
Parameters:
| Param | Source | Type | Required | Description |
|---|---|---|---|---|
{id} |
Path | int | Yes | Show ID |
related_id |
Query | int | Yes | ID of the entity driving this job group (e.g. migration import ID or broadcast ID) |
type |
Query | string | Yes | Email type string identifying the job group (e.g. migration_invitation, broadcast) |
Response:
{
"total": 89,
"sent": 71,
"failed": 2,
"pending": 16,
"status": "sending",
"estimated_completion": "2026-05-17T15:42:00"
}
Response fields:
| Field | Type | Description |
|---|---|---|
total |
int | Total rows in this job group |
sent |
int | Rows with status sent |
failed |
int | Rows with status failed |
pending |
int | Rows with status pending or in_progress |
status |
string | queued, sending, complete, or complete_with_failures |
estimated_completion |
string|null | ISO 8601 datetime estimated from current pending count and the emails_per_hour setting; null when pending = 0 |
Status values:
| Status | Meaning |
|---|---|
queued |
Job exists but processing hasn’t started yet |
sending |
At least one row sent; rows still pending |
complete |
All rows terminal (sent or failed); zero failures |
complete_with_failures |
All rows terminal; one or more failed rows |
Errors:
| Code | HTTP | Condition |
|---|---|---|
show_not_found |
404 | Show ID does not exist |
invalid_param |
400 | related_id or type missing |
Polling pattern:
The Broadcasts screen polls this endpoint every 30 seconds while status is queued or sending. Stop polling when status is complete or complete_with_failures. The estimated_completion field gives the UI a target to display — it is recalculated on every poll as the pending count decreases.
Relationship to broadcasts:
For broadcasts specifically, use related_id = the broadcast ID and type = broadcast. The GET /broadcasts/{id}/recipients endpoint gives per-recipient detail; this endpoint gives the aggregate counts needed for a status bar.
Showing Your Job in the Admin Banner
Benecaster’s admin shows a sending banner at the top of every Benecaster screen — “Sending {label}: 47 of 1,203 sent” with a View status → link — for one email job at a time. It follows the job named in the browser’s localStorage under the key benecaster_active_email_job, and polls this endpoint for it. It re-reads the key whenever the podcaster moves to another Benecaster screen, and when another tab writes it, so the banner appears as soon as your job starts.
The banner does not discover jobs from the server. No key, no banner: an add-on that queues email and wants the banner writes the key itself.
localStorage.setItem( 'benecaster_active_email_job', JSON.stringify( {
show_id: showId, // the show the queue rows belong to
related_id: importId, // the queue rows' related_id
type: 'migration_welcome', // the rows' exact email_type: the banner polls with it
label: __( 'welcome emails', 'my-addon' ), // lowercase and translated
view_url: statusPageUrl, // optional: adds the View status link
} ) );
| Field | Required | Description |
|---|---|---|
show_id |
Yes | The show the queue rows belong to |
related_id |
Yes | The rows’ related_id, as passed to this endpoint |
type |
Yes | The rows’ exact email_type. A near match finds no rows, and the banner shows nothing useful |
label |
Yes | What is being sent, lowercase and already translated; it is placed mid-sentence |
view_url |
No | Where View status → goes. Omit it and the banner has no link |
- Only one job is followed. The broadcast screen writes the same key while a broadcast sends, so the last writer wins.
- The banner clears the key when the podcaster dismisses its completion notice. If your job ends another way, clear it yourself with
localStorage.removeItem( 'benecaster_active_email_job' ). - The key name and the field names are a stable contract for add-ons.
See Also
Need this built rather than just documented? See our services →