Skip to main content

Account REST API (Subscriber-Facing)

These are the endpoints called by the [benecaster_account] shortcode on the subscriber-facing account page. They use cookie authentication and verify the current user owns the resource — they do not use X-WP-Nonce or require manage_options. Each endpoint checks that the currently logged-in user is requesting their own data, so responses are always scoped to the caller’s own account.

Who this is for. Cookie authentication means these endpoints answer to a logged-in subscriber’s own browser session, so they are called from front-end JavaScript, not from server-side code. That is exactly what you want when you are replacing the account panel with your own interface — a custom theme template, a React or Vue account page, or a block that surfaces one piece of account data on its own. Your script runs in the subscriber’s session and gets their data back.

What you cannot do is call them server to server, or on behalf of another user. There is no application-password or token path here, and no endpoint will return a different subscriber’s data however it is called. For back-end work — provisioning, reporting, reacting to lifecycle events — use the hooks and public functions instead.


GET /benecaster/v1/account/subscriptions

Returns all active Benecaster subscriptions for the currently logged-in user — one entry per show the user holds an active token for.

Auth: Cookie (logged-in subscriber). No manage_options required.

Response:

{
  "items": [
    {
      "show_id": 45,
      "show_title": "My Podcast",
      "show_artwork": "https://example.com/artwork.jpg",
      "tier_name": "Premium",
      "feed_url": "https://yoursite.com/podcast-feed/?token=abc...",
      "status": "active",
      "token_prefix": "abc12345"
    }
  ]
}

Response fields:

Field Description
show_id Show database ID
show_title Show display name
show_artwork Show artwork URL; null if no artwork set
tier_name The display name of the subscriber’s current plan, from whichever tier list the show uses (built-in membership or a membership plugin). Falls back to the tier slug only when neither knows it
feed_url Subscriber’s full private RSS feed URL
status active (only active tokens returned)
token_prefix First 8 characters of the token — displayed in the account UI for identification; the full token is in feed_url

Errors:

Code HTTP Condition
rest_forbidden 401 User not logged in

The [benecaster_account] shortcode calls this endpoint on render to populate the subscription list. The response is not cached — each page load reflects the current token state.

Rate limit: 300 requests per 5 minutes per IP (catch-all standard bucket — no bespoke bucket for this endpoint). Shared across all IPs on the same network (household NAT, coworking spaces). Returns 429 Too Many Requests with Retry-After header when exceeded. Operators can tighten this via the benecaster_rest_rate_limit_buckets filter.


POST /benecaster/v1/account/reset-token

Subscriber resets their own feed token for a specific show. Generates a new token and immediately invalidates the old one. The subscriber’s podcast app will receive the new URL on its next feed poll — the app doesn’t need to be manually updated unless the subscriber uses a direct feed subscription (not a token-aware app link).

Auth: Cookie (logged-in subscriber).

Body:

{
  "show_id": 45
}

Response:

{
  "feed_url": "https://yoursite.com/podcast-feed/?token=xyz...",
  "token_prefix": "xyz98765",
  "app_links": {
    "apple_podcasts": "podcast://yoursite.com/podcast-feed/?token=xyz...",
    "overcast": "overcast://x-callback-url/add?url=...",
    "pocket_casts": "pktc://subscribe/yoursite.com/...",
    "castro": "castro://subscribe/yoursite.com/..."
  }
}

Response fields:

Field Description
feed_url Complete new feed URL including the full token
token_prefix First 8 characters of the new token
app_links Deep links for major podcast apps, pre-loaded with the new feed URL

Notes:

  • This endpoint returns the full token in feed_url. The admin endpoint (POST /subscribers/{id}/reset-token) truncates to a prefix — this endpoint does not, because the subscriber needs the complete URL.
  • Auth check: the endpoint only resets the token for the currently logged-in user — it is not possible to reset another subscriber’s token via this endpoint.
  • A 404 is returned when the user has no active token for the given show.

Errors:

Code HTTP Condition
invalid_param 400 show_id missing or not an integer
rest_forbidden 401 User not logged in
rest_not_found 404 User has no active token for show_id

Rate limit: 5 requests per hour per user. Returns 429 Too Many Requests with Retry-After header when exceeded.


GET /benecaster/v1/account/qr-code

Returns QR code image data for the subscriber’s feed URL for a specific show.

Auth: Cookie (logged-in subscriber).

Parameters:

Param Type Default Description
show_id int — Required
format string svg svg or png
size int 200 Display size in pixels (does not affect download resolution)

Response: Binary image data with Content-Type: image/svg+xml or Content-Type: image/png.

For SVG: print-ready at 50 mm × 50 mm regardless of size parameter. For PNG: 1000 × 1000 pixels regardless of size parameter.

The size parameter controls the width and height attributes on the <img> element when the [benecaster_qr_code] shortcode renders the image in the page — it is a display hint only.

Errors:

Code HTTP Condition
invalid_param 400 show_id missing; format not svg or png; size not a positive integer
rest_forbidden 401 User not logged in
rest_not_found 404 User has no active token for show_id

Rate limit: 30 requests per 5 minutes. Returns 429 Too Many Requests with Retry-After header when exceeded.


GET /benecaster/v1/account/cadence-switch

Despite its name, this route also covers moving to another tier. A billing-period switch and a plan move share one mechanism and one pending slot, so there is no separate /account/tier-switch route: tier_options below lists the tiers, and POST accepts a price from either list.

Returns the subscriber’s current billing period, what it can switch to and at what price, why switching is blocked if it is, and any pending switch — for a Built-in Membership subscription. Bridge (external membership plugin) subscribers get 404; their membership plugin owns their billing.

Auth: Cookie (logged-in subscriber).

Parameters:

Param Type Required Description
show_id int Yes Query parameter

Response — 200 OK:

{
  "current": {
    "price_id": 12,
    "label": "Monthly",
    "amount_cents": 900,
    "amount_formatted": "USD 9.00",
    "currency": "USD",
    "tier_slug": "gold",
    "tier_name": "Gold"
  },
  "options": [
    {
      "price_id": 14,
      "label": "Annual",
      "interval_unit": "year",
      "interval_count": 1,
      "currency": "USD",
      "amount_cents": 9000,
      "amount_formatted": "USD 90.00",
      "starts_at": "2027-03-01 00:00:00",
      "starts_date": "1 March 2027",
      "charged_today": 0,
      "buyups": [
        { "buyup_id": 3, "amount_cents": 1200 }
      ],
      "buyups_dropped": [],
      "buyups_included": [
        { "buyup_id": 7, "name": "Transcripts" }
      ],
      "buyups_amount_cents": 1200,
      "total_amount_cents": 10200,
      "total_formatted": "USD 102.00",
      "later_change": null,
      "available": true,
      "unavailable_reason": null
    }
  ],
  "tier_options": [
    {
      "price_id": 21,
      "label": "Monthly",
      "tier_slug": "platinum",
      "tier_name": "Platinum",
      "direction": "up",
      "amount_cents": 1500,
      "amount_formatted": "USD 15.00",
      "starts_date": "1 February 2027",
      "charged_today": null,
      "lands": "now",
      "buyups_ending": [],
      "…": "the same fields as options[]"
    }
  ],
  "blocked": null,
  "pending": null,
  "free_option": {
    "tier_slug": "free",
    "tier_name": "Free",
    "switch_at": "2027-03-01 00:00:00",
    "switch_date": "1 March 2027"
  },
  "pending_free": null,
  "upgrade_url": ""
}
Field Meaning
current The subscriber’s active price row, or null when it could not be resolved
options Other billing periods of the same tier the subscriber could switch to. Empty when blocked is set
options[].starts_at / starts_date When the switch would land — the subscriber’s current renewal — as a UTC timestamp and a formatted date
options[].amount_cents / amount_formatted The tier price on that billing period, alone — not including buy-ups
options[].buyups The buy-ups the subscriber holds that move with them, each { buyup_id, amount_cents } at that buy-up’s price for the target period. A buy-up they hold free today that the target period sells for money is here too, at its paid price
options[].buyups_amount_cents The sum of buyups[].amount_cents — what the buy-ups will cost per period from the switch date
options[].total_amount_cents / total_formatted Tier price plus buy-ups: what the subscriber will actually pay per period from the switch date
options[].buyups_included Buy-ups free on the target period that the subscriber will be given when the switch lands, each { buyup_id, name }. A buy-up whose subscriber cap is already full is not listed
options[].buyups_dropped Buy-ups the subscriber holds free today that the target period doesn’t sell at all, each { buyup_id, name }. They end when the switch lands, at no cost
options[].charged_today Always 0 — nothing is ever charged by scheduling a switch
options[].later_change Set when the podcaster has already scheduled a price change on that billing period taking effect after the switch would land — carries new_amount_cents, new_amount_formatted, effective_at, effective_date. null otherwise
options[].available / unavailable_reason false when the subscriber pays for a buy-up that the target period doesn’t sell. There is nothing to move it onto, and the plugin won’t drop something they pay for. unavailable_reason carries the translated message, naming the buy-ups: “You pay for Transcripts, which is not sold on this billing period. Remove it first, or stay on your current period.” If a name can’t be resolved, the message gives a count instead (“You hold 2 extras that…”). A free buy-up in the same position doesn’t block; it appears in buyups_dropped instead
blocked { code, message } when no switch can be scheduled right now (see the cadence_switch_* codes below), null otherwise
tier_options Other tiers the subscriber could move to, in the same shape as options plus tier_slug, tier_name, direction (up, down or same) and lands. A tier is listed only when it sells the subscriber’s current billing period in the same currency. The show’s free tier is never listed here, because it has no price to post; it has its own field, free_option. Empty when blocked is set
tier_options[].lands now for an upgrade — a tier priced higher, at the subscriber’s billing period, than their current price — and renewal for every other move. A label for the list, not the quote. It is worked out from Benecaster’s price rows so the page doesn’t call Stripe on every load. Before asking the subscriber to agree, call POST /account/cadence-switch/preview, which reads Stripe and is what decides
tier_options[].charged_today / starts_date For a lands: now option, charged_today is null — the amount isn’t known until the preview, and a 0 would be untrue — and starts_date is today
tier_options[].buyups_ending Paid buy-ups the subscriber holds that the target tier doesn’t offer (its Available to tiers leaves it out), each { buyup_id, name }. They don’t block the move: each ends at the end of the current billing period, with no refund, exactly as if the subscriber had removed it. Name them before the subscriber confirms. Tier moves only — a billing-period switch still blocks on a paid buy-up the new period doesn’t sell
current.tier_slug / current.tier_name The tier the subscriber is on now
pending The subscriber’s already-scheduled change onto a price — price_id, label, switch_at, switch_date, plus is_tier_change (true for a plan move; a missing value means a billing-period switch), tier_slug and awaiting_payment — or null
pending.awaiting_payment true for an upgrade whose payment the bank hasn’t confirmed yet (the subscriber left a 3D Secure check unfinished). Nothing has moved and nothing is scheduled for a date: it lands the moment the payment clears, or is withdrawn with /cancel. switch_at is then only when Stripe stops waiting
free_option The show’s free tier and when a move onto it would land — tier_slug, tier_name, switch_at, switch_date — or null when the show has no free tier or blocked is set. Schedule it with POST /account/cadence-switch/free
pending_free An already-scheduled move onto the free tier, in the same shape as free_option, or null. It is present whatever blocked says (which is then pending). A subscriber has at most one of pending and pending_free
upgrade_url For a subscription with no Stripe billing behind it (a member on the free tier), the page where they can take out a paid plan — the show’s page by default, filterable with benecaster_signup_url. An empty string for anyone paying

Errors:

Code HTTP Condition
rest_forbidden 401 User not logged in
rest_invalid_param 400 show_id missing or zero
rest_not_found 404 No active Built-in Membership subscription for this show — including bridge (Phase 1) subscribers

POST /benecaster/v1/account/cadence-switch

Moves the subscriber to another billing period of their current tier, or to another tier.

  • A tier upgrade is charged and lands now. The subscriber pays the difference for the rest of the current billing period, prorated, and moves tier as soon as the payment succeeds. Call /preview first and send its figures back — see Upgrades below.
  • Everything else lands at the next renewal, with nothing charged — a billing-period switch, a move down, or a move to a tier at the same price. The new price is billed starting at the renewal it replaces.

Auth: Cookie (logged-in subscriber).

Body:

{
  "show_id": 45,
  "price_id": 14,
  "confirm_amount_cents": 9000,
  "confirm_new_amount_cents": null,
  "confirm_buyups_amount_cents": 1200,
  "declined_buyups": [7]
}
Field Type Required Meaning
show_id int Yes
price_id int Yes The target price row’s id, from GET /account/cadence-switch’s options[].price_id or tier_options[].price_id. A price on another tier schedules a plan move
confirm_amount_cents int Yes The tier price the subscriber saw and is agreeing to pay from the switch date, in minor units — options[].amount_cents, not the total
confirm_new_amount_cents int Only when options[].later_change was shown The later price the subscriber is agreeing to, when the target billing period has a price change taking effect after the switch
confirm_buyups_amount_cents int When options[].buyups_amount_cents is more than 0 The buy-up total the subscriber is agreeing to pay from the switch date. Ignored otherwise
declined_buyups int[] No IDs of free buy-ups from options[].buyups_included that the subscriber unticked. Each is recorded as declined and not given when the switch lands. An ID the switch isn’t offering is ignored and not recorded
confirm_prorated_cents int Upgrades, except during a trial The amount charged today, from /preview’s prorated_cents. Ignored for every other move
proration_date int Upgrades, except during a trial /preview’s proration_date, sent back unchanged so the invoice charges exactly the amount quoted. A date in the future, or one more than a short while old, is refused with fresh figures

GET /account/cadence-switch lists every free buy-up the target period includes, even one the subscriber declined before. Choosing a period is a new decision, so the list is what the period offers, not what the subscriber hasn’t refused.

Response — 200 OK: the same shape as GET /account/cadence-switch, refreshed — pending is now set.

Upgrades

For an upgrade, confirm_amount_cents (and confirm_buyups_amount_cents, if sent) is the recurring price from the renewal, and confirm_prorated_cents is what is charged today. During a free trial nothing is charged today, so the two upgrade fields are not needed.

The answer is the refreshed GET shape plus one of:

Answer Meaning
upgraded: true The payment went through and the subscriber is on the new tier now
upgraded: false, requires_action: true, client_secret, stripe_publishable_key The bank wants to verify the payment (3D Secure). Nothing has moved. Run Stripe.js handleNextAction( { clientSecret } ) with that publishable key, then call /confirm. pending.awaiting_payment is true until it clears
402 payment_declined The card was declined. Nothing moved and the invoice is voided, so nothing is collected later. The message is the card issuer’s reason when Stripe gives one

An upgrade that also asks for a different billing period is split: the tier moves today at the current period, and the new period is scheduled for the renewal. The same combination on a move down is refused — change the plan and the billing period one at a time.

An upgrade doesn’t send the Plan Change Scheduled email and doesn’t fire benecaster_subscription_tier_switch_scheduled — nothing is scheduled. It fires benecaster_subscription_tier_changed when the payment succeeds, and the Tier Change email goes out then.

Never a blind POST, the same pattern as buy-up purchase: the caller states the price (and, when relevant, the later price) the subscriber actually saw, and the server refuses if either no longer matches what it would charge — so a price edited between render and click is rejected rather than silently applied at the new amount.

confirm_amount_cents is the tier price alone, not the total. When buy-ups move with the subscriber, send the tier price in confirm_amount_cents and the buy-up total in confirm_buyups_amount_cents. Sending total_amount_cents in confirm_amount_cents is refused with 409 amount_mismatch on every call.

Free buy-ups, whether gained or lost, cost nothing and aren’t part of any consent value. Show buyups_included and buyups_dropped to the subscriber anyway, so nothing is added or taken away without being named.

A mismatch on either field returns 409 amount_mismatch carrying the live figures, so the client re-asks rather than guesses:

{
  "code": "amount_mismatch",
  "message": "The price of this billing period has changed. Please review the new price and try again.",
  "data": {
    "status": 409,
    "amount_cents": 9000,
    "new_amount_cents": null,
    "buyups_amount_cents": 1200,
    "total_amount_cents": 10200,
    "currency": "USD"
  }
}

Errors:

Code HTTP Condition
rest_forbidden 401 User not logged in
rest_invalid_param 400 show_id or price_id missing or zero
confirm_amount_invalid 400 A confirmation field sent but not a whole number
confirm_amount_required 400 confirm_amount_cents missing, or confirm_buyups_amount_cents missing when buy-ups with a cost move with the subscriber
rest_not_found 404 No active Built-in Membership subscription for this show
cadence_unavailable 400 price_id is in neither options nor tier_options — not another billing period of their tier, and not a price on another tier the show sells at their billing period
409 amount_mismatch 409 Confirmed amount(s) no longer match the live price. On an upgrade the error data also carries fresh prorated_cents and proration_date, so the page re-asks once
confirm_amount_required (upgrade) 400 An upgrade outside a trial sent without confirm_prorated_cents
402 payment_declined 402 An upgrade’s payment was declined — nothing changed
409 cadence_switch_not_billed 409 The membership is not billed through Stripe
409 cadence_switch_pending 409 A billing change is already scheduled — cancel it first
409 cadence_switch_billing_problem 409 The subscription is not active or trialing (e.g. a payment has failed)
409 cadence_switch_paused 409 Billing for the membership is paused
409 cadence_switch_cancelling 409 The membership is set to end — resume it first
409 cadence_switch_unknown_plan 409 The subscriber’s current tier or price could not be resolved
409 cadence_switch_buyups 409 Billing-period switches only: the subscriber pays for a buy-up that the target billing period doesn’t sell — they can remove it or stay on their current period. A tier move never gets this; the buy-up ends instead (buyups_ending)
409 benecaster_billing_unavailable 409 This install is a copy of another site, so the change is refused — see Buy-ups Subscriber REST API
502 stripe_error 502 Stripe could not be reached

On success the plugin sends the subscriber the Billing Change Scheduled email (see Built-in Membership Billing Emails) and fires benecaster_subscription_cadence_switch_scheduled.


POST /benecaster/v1/account/cadence-switch/preview

Quotes a tier move from Stripe — what the account page shows in its confirm step before the subscriber agrees. Changes nothing.

Auth: Cookie (logged-in subscriber).

Body: { "show_id": 45, "price_id": 21 } — a price_id from tier_options.

Response — an upgrade (200 OK):

{
  "price_id": 21,
  "tier_slug": "platinum",
  "tier_name": "Platinum",
  "currency": "USD",
  "renewal_at": "2027-03-01 00:00:00",
  "renewal_date": "1 March 2027",
  "lands": "now",
  "trialing": false,
  "changes_period": false,
  "prorated_cents": 412,
  "prorated_formatted": "USD 4.12",
  "prorated_before_tax": false,
  "proration_date": 1790000000,
  "amount_cents": 1500,
  "amount_formatted": "USD 15.00",
  "buyups_amount_cents": 0,
  "total_amount_cents": 1500,
  "total_formatted": "USD 15.00",
  "later_change": null
}

prorated_cents is charged today (0 while trialing). On a subscription that collects tax it includes the tax; prorated_before_tax is true only when Stripe could not work the tax out for this preview, so the figure is before tax, and is always false on an untaxed subscription. Send prorated_cents back unchanged either way; amount_cents / total_amount_cents is the price from renewal_date. changes_period is true when the chosen price is on another billing period, in which case the tier moves today and the period follows at the renewal. Send prorated_cents back as confirm_prorated_cents, and proration_date unchanged.

Response — any other tier move: the same first six fields, plus lands: "renewal", amount_cents and amount_formatted. Nothing is charged today.

Errors: 400 tier_unavailable when the price isn’t a tier the subscriber can move to (or is a move down that also changes billing period), the 409 cadence_switch_* refusals of the main route, and 502 stripe_error.


POST /benecaster/v1/account/cadence-switch/confirm

Lands an upgrade once the subscriber has passed their bank’s 3D Secure check, without waiting for Stripe’s webhook. The webhook lands it anyway if this is never called.

Auth: Cookie (logged-in subscriber).

Body: { "show_id": 45 }

Response — 200 OK: the refreshed GET shape plus upgraded — true once Stripe has applied the upgrade, false if it hasn’t yet (or there was no upgrade waiting). A false isn’t a failure; the webhook finishes it.


POST /benecaster/v1/account/cadence-switch/cancel

Withdraws the subscriber’s pending change — a scheduled billing-period switch or plan move, or an upgrade still waiting on the bank (pending.awaiting_payment), whose invoice is voided so nothing is charged. No email is sent.

Auth: Cookie (logged-in subscriber).

Body:

{
  "show_id": 45
}

Response — 200 OK: the same shape as GET /account/cadence-switch — pending is now null.

Errors:

Code HTTP Condition
rest_forbidden 401 User not logged in
rest_invalid_param 400 show_id missing or zero
rest_not_found 404 No active Built-in Membership subscription for this show
409 cadence_switch_not_pending 409 Nothing is currently scheduled to cancel

POST /benecaster/v1/account/cadence-switch/free

Schedules a move onto the show’s free tier, landing at the end of the period the subscriber has already paid for. Nothing is charged by this call, and nothing is charged afterwards: at Stripe’s level the subscription is set to cancel at the period end, and when it ends the subscriber becomes a member on the free tier with the same feed URL. Buy-ups they pay for end on the same date.

Auth: Cookie (logged-in subscriber).

Body:

{
  "show_id": 45
}

There is no consent amount: the move takes the subscriber’s bill to nothing, so there is no figure to confirm.

Response — 200 OK: the same shape as GET /account/cadence-switch, refreshed — pending_free is now set and blocked.code is pending.

Errors:

Code HTTP Condition
rest_forbidden 401 User not logged in
rest_invalid_param 400 show_id missing or zero
rest_not_found 404 No active Built-in Membership subscription for this show
free_tier_unavailable 400 The show has no free tier to move to
409 cadence_switch_* 409 The same refusals as POST /account/cadence-switch: not billed through Stripe, a change already pending, a failed payment, billing paused, set to cancel, or plan unknown
409 benecaster_billing_unavailable 409 This install is a copy of another site
502 stripe_error 502 Stripe could not be reached

On success the plugin sends the subscriber the Move to Free Plan Scheduled email (see Built-in Membership Billing Emails) and fires benecaster_subscription_free_switch_scheduled.


POST /benecaster/v1/account/cadence-switch/free/cancel

Withdraws a pending move onto the free tier. The subscriber’s paid plan and billing carry on as before. No email is sent.

Auth: Cookie (logged-in subscriber).

Body:

{
  "show_id": 45
}

Response — 200 OK: the same shape as GET /account/cadence-switch — pending_free is now null.

Errors:

Code HTTP Condition
rest_forbidden 401 User not logged in
rest_invalid_param 400 show_id missing or zero
rest_not_found 404 No active Built-in Membership subscription for this show
409 cadence_switch_not_pending 409 No move onto the free tier is scheduled
409 benecaster_billing_unavailable 409 This install is a copy of another site
502 stripe_error 502 Stripe could not be reached

There is no route for the other direction. A member on the free tier has no card on file, so they take out a paid plan through the ordinary signup form at upgrade_url, and are charged straight away. | 409 benecaster_billing_unavailable | 409 | This install is a copy of another site, so the change is refused | | 502 stripe_error | 502 | Stripe could not be reached |

Fires benecaster_subscription_cadence_switch_cancelled with reason: subscriber.


POST /benecaster/v1/account/supporter-wall-visibility

Sets whether the currently logged-in subscriber appears on the Supporter Wall for their show(s). Called by the opt-in checkbox in the Profile picture / Badges block of [benecaster_account].

Auth: Cookie (logged-in subscriber).

Body:

{
  "visible": true
}

Response:

{
  "visible": true
}

Behavior:

  • Writes _benecaster_subscriber_wall_visible user meta and returns the value that was written.
  • Idempotent — submitting visible: true when the subscriber is already opted in still succeeds and still fires the benecaster_subscriber_wall_visible action (always-fires semantics; see benecaster_subscriber_wall_visible).
  • Returns 401 when the request is not authenticated.
  • The checkbox on the account page only renders when benecaster_supporter_wall_active returns true for the subscriber — i.e. at least one of their active shows has the wall enabled. The endpoint itself does not enforce this gate server-side; it accepts requests regardless.

Errors:

Code HTTP Condition
invalid_param 400 visible missing or not a boolean
rest_forbidden 401 User not logged in

Rate limit: 300 requests per 5 minutes per IP (catch-all standard bucket — no bespoke bucket for this endpoint). Operators can tighten this via the benecaster_rest_rate_limit_buckets filter.


Authentication Note

All /account/* endpoints reject requests that are not from a logged-in WordPress user — they return 401 rest_forbidden. This means they cannot be called from server-to-server contexts or external tools without a valid session cookie. If you need to read or modify subscriber data programmatically, use the admin subscriber endpoints (GET /shows/{id}/subscribers, POST /subscribers/{id}/reset-token, etc.) with manage_options + nonce authentication instead.

See Also

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