Subscribers REST API
Admin-facing REST endpoints for reading and managing a show’s subscribers.
Base path: benecaster/v1
Auth: manage_options + X-WP-Nonce on every endpoint below.
GET /benecaster/v1/shows/{id}/subscribers
Paginated, filtered subscriber list with summary counts.
Query parameters:
| Parameter | Default | Notes |
|---|---|---|
page |
1 |
|
per_page |
20 |
Capped at 100 |
status |
active |
|
tier |
— | Tier slug |
search |
— | Matches email or display name |
last_seen |
— | Engagement filter |
migration_status |
— | |
token_type |
— | subscriber or follower. Narrows items only — the response summary always covers the whole show regardless. An unrecognized value is ignored, like last_seen and migration_status. |
orderby |
created_at |
|
order |
desc |
Response envelope:
{
"items": [ ... ],
"total": 226,
"pages": 12,
"per_page": 20,
"page": 1,
"summary": { "active": 226, "paying": 84, "followers": 142, "by_tier": {} },
"shadow_summary": { }
}
Each item:
{
"id": 47,
"user_id": 312,
"email": "listener@example.com",
"display_name": "Sam Reyes",
"avatar_url": "https://…",
"tier_slug": "supporter",
"tier_name": "Supporter",
"badges": [],
"token_prefix": "bc_9f2a",
"token_type": "subscriber",
"status": "active",
"created_at": "2026-03-04 11:20:00",
"last_accessed_at": "2026-08-30 07:41:00",
"last_accessed_human": "yesterday",
"engagement_level": "active",
"is_migration": false,
"is_over_limit": false,
"over_limit_effect": null,
"is_manual_grant": false,
"manual_expiry_at": null
}
over_limit_effect
"demoted" | "locked" | null.
What the over-limit flag actually does to this listener:
| Value | Meaning |
|---|---|
"demoted" |
The token still resolves and serves a feed — the public one. Every flagged paying token, and every flagged token under auto_upgrade. |
"locked" |
The feed URL returns 410 and the listener receives nothing. Only a free-tier token, only under cap enforcement. |
null |
Not flagged. The ordinary case. |
This is the outcome, not its inputs. Do not reconstruct it. is_over_limit is unchanged and still present beside it, but a flagged token is not enough to tell you what happened — a demoted listener and a locked one are both is_over_limit: true and their situations are opposites. The enforcement preference is deliberately not published, so the outcome cannot be derived client-side even with the tier in hand. Read this field.
That is the whole reason it exists. The Subscribers screen previously labelled every flagged token “Receiving public feed”, which for a locked listener is the one thing not happening to them, and a podcaster investigating a dead feed read that the feed was being served and stopped looking. The rule now lives once, in OverLimitEffect, and both the feed and this payload read it.
token_type — "subscriber" or "follower". See Follower Tier REST.
Other subscriber endpoints
| Method | Route | Purpose |
|---|---|---|
GET |
/subscribers/{id} |
Full subscriber detail |
GET |
/subscribers/duplicates |
Groups of subscribers sharing an email address |
POST |
/subscribers/{id}/reset-token |
Generate a new token, invalidate the old one |
PUT |
/subscribers/{id}/tier |
Change tier; fires benecaster_subscriber_tier_overridden |
DELETE |
/subscribers/{id} |
Revoke access; fires benecaster_subscriber_access_revoked |
POST |
/subscribers/bulk-reset-tokens |
Reset tokens for multiple subscribers |
POST |
/subscribers/bulk-revoke |
Revoke access for multiple subscribers |
POST |
/subscribers/bulk-tier-change |
Change tier for multiple subscribers |
POST /subscribers/{id}/reset-token response. feed_url is the full, working URL of the new token, built by benecaster_get_feed_url() (so a benecaster_token_url filter applies) — this is the only admin-facing response that ever carries a working URL; GET /subscribers/{id}‘s own feed_url stays masked. The response carries Cache-Control: no-store, and feed_url should never be logged. email_sent is true only when the request’s send_email was true and the subscriber’s show has its token-reset email switched on (Show Settings → Subscription → Welcome and Feed URL Emails) — with the switch off it is false even when send_email was requested, though the reset still happens and feed_url is still returned. true means the email was queued, not delivered.
POST /subscribers/bulk-reset-tokens response: { processed: int, emails_sent: int, errors: int[] }. emails_sent counts how many were requested and queued, not delivered, subject to the same per-show token-reset-email switch as the single-subscriber endpoint, checked per subscriber’s own show since a bulk selection can span shows.
⚠⚠ user_ids, on all three bulk routes above, is misnamed — every value is a subscriber ID, never a WordPress user ID. It’s the id field from GET /shows/{id}/subscribers or GET /subscribers/{id}, not the account behind it. A WordPress user ID passed here by mistake is not rejected — if it happens to collide with a real subscriber ID, the route silently acts on whichever subscriber holds that ID, possibly on a different show than you intended.