Promote to Bridge REST API
Admin REST endpoints for the Promote to Bridge wizard and Promotion History panel. All endpoints require manage_options capability and a valid WP REST nonce.
Base path: /wp-json/benecaster/v1/shows/{show_id}/promote
Get Writable Bridge Targets
GET /shows/{show_id}/promote/targets
Returns all bridges that implement BridgeWritable — the destination options shown in the wizard’s first step. Each entry includes the bridge’s available tiers (external membership levels).
Response:
[
{
"slug": "memberpress",
"name": "MemberPress",
"tiers": [
{ "id": "1", "name": "Gold Membership" },
{ "id": "2", "name": "Silver Membership" }
]
}
]
Preview a Promotion Run
GET /shows/{show_id}/promote/preview
Takes no query parameters. Returns, for each of the show’s built-in tiers, how many subscribers a run would promote: active, trialing and past-due subscriptions on that tier. The wizard uses it for Map tiers and the Preview step.
Response:
{
"show_id": 14,
"tiers": [
{ "tier_slug": "promote_gold", "label": "Gold Patrons", "subscriber_count": 47 },
{ "tier_slug": "promote_silver", "label": "Silver Patrons", "subscriber_count": 23 }
]
}
label— the tier’s display name. Never empty: the slug is the last resort.- There is no total; sum
subscriber_countfor one.
Run a Promotion
POST /shows/{show_id}/promote/run
Executes the promotion. Long-running for large subscriber lists; runs synchronously.
Request body:
{
"target_bridge_slug": "memberpress",
"tier_map": {
"gold": "1",
"silver": "2"
},
"grace_days": 30
}
Two tier_map keys may name the same external level, and the run is not refused. The destination plugin’s tier map holds one row per level, so the key that comes first in tier_map keeps the level; members of a later key are created on that level and resolve to the first key’s access. Key order is therefore significant. The admin wizard sends keys in its Map tiers order and warns on its Preview step.
grace_days is the default for the run rather than a flat value for every row: the benecaster_promote_grace_length_days filter can vary it per tier or per subscriber, so individual rows may differ from the number sent. The grace_period_ends_at in the response is calculated from this baseline and is not a summary of what each row actually received — read promote_grace_period_ends_at per row from the Promotion History listing when the filter is in use.
Response: 200 OK
{
"ok": true,
"promoted": 68,
"skipped": 2,
"errors": 0,
"grace_period_ends_at": "2026-07-28T00:00:00Z",
"rows": [
{ "user_id": 12, "tier_slug": "promote_gold", "outcome": "promoted" },
{ "user_id": 15, "tier_slug": "promote_silver", "outcome": "skipped", "message": "row status cancelled is not active" }
]
}
Per-row outcome: promoted, skipped or error; skipped and error rows carry a message. A row error does not stop the run.
Get Promotion History
GET /shows/{show_id}/promote/history
Returns the show’s subscription rows that have been through a promotion: those with promoted_to_bridge set, or with status expired_grace.
Response:
{
"show_id": 14,
"rows": [
{
"id": 88,
"user_id": 12,
"show_id": 14,
"tier_slug": "promote_gold",
"status": "active",
"promoted_to_bridge": "memberpress",
"promote_grace_period_ends_at": "2026-07-28 00:00:00",
"tier_name": "Gold Patrons",
"user_display_name": "Alice Example",
"user_email": "alice@example.com",
"promoted_to_bridge_name": "MemberPress",
"promote_grace_period_ends_at_display": "July 27, 2026 8:00 pm"
}
]
}
Each row is the full subscription row (only some of its columns are shown above), plus five keys added for display:
tier_name— the tier’s display name. Never empty: the slug is the last resort.user_display_name,user_email—nullwhen the account has since been deleted.promote_grace_period_ends_at_display— the UTCpromote_grace_period_ends_atin your site’s timezone and date and time formats;nullwhen there is no end date.promoted_to_bridge_name— the plugin’s name; its slug if that plugin is not registered;nullwhen there is no plugin.
There is no grace status key. Read it from the row: status expired_grace means the grace period ended and the token was revoked; a promote_grace_period_ends_at value means it is still in grace (past-due if that date has passed); neither means it was marked re-authorized.
Clear a Grace Period
POST /shows/{show_id}/promote/history/{subscription_id}/clear-grace
Removes the grace period marker and re-enables the subscriber’s feed token. Used by the Promotion History panel’s “Clear grace” action.
Response: 200 OK with { "ok": true } when a row was found and cleared, or { "ok": false } when no subscription has that id. Both are 200 — read ok, not the status code.
{ "ok": false } here means nothing was changed — a client that treats this endpoint as always succeeding will be wrong about a mistyped id.
Extend a Grace Period
POST /shows/{show_id}/promote/history/{subscription_id}/extend-grace
Request body:
{ "days": 14 }
days must be a positive integer; a zero or negative value is rejected with 400. Omitting it entirely applies a default of 7.
Adds days to the subscriber’s existing grace period end date. If that date is already in the past, the extension is measured from it, not from now — extending an expired row by 3 days when it lapsed a week ago leaves it still in the past. Extend by enough to clear the elapsed time, or clear the grace period instead.
Response: 200 OK with { "ok": true }, or { "ok": false } when the subscription has no grace period set or no row has that id.
The response carries no other fields. In particular there is no grace_period_ends_at — read the new date back from the Promotion History listing rather than from this response.
See Also
Need this built rather than just documented? See our services →