Skip to main content

Add-on Activation REST API

Powers the per-show on/off control on every entitled card in Settings → Add-ons.

The distinction this API exists to hold apart is entitlement versus activation. Entitlement is what a license grants and is decided entirely by the license server. Activation is the podcaster’s own choice about which of their shows actually runs an add-on they already own. This API writes the second and never the first — it never calls the license server.

Purchase is license-wide; activation is per show. A Multi-Show customer who owns an add-on can run it on shows A and B and leave it off on C, with no second purchase.

A third, read-only route, GET /addons, lists every add-on in the catalogue with the state its card shows.


GET /addons

Returns every add-on in Benecaster’s pricing catalogue, with the state its card on Settings → Add-ons shows. It is the same data the Add-ons screen renders, so a script or dashboard reading it sees exactly what the screen does.

Auth: manage_options plus a valid wp_rest nonce. Read-only.

It never contacts Benecaster.com. It answers from what the site has already cached. If the site holds no copy of the catalogue, the answer is { "catalog_available": false, "currency": null, "items": [] }, and this route does not fetch one.

Response (200):

{
  "catalog_available": true,
  "currency": "usd",
  "items": [
    {
      "slug": "email-editor",
      "entitlement_slug": "benecaster-addon-email-editor",
      "name": "Email Editor",
      "note": null,
      "phase": 1,
      "state": "active",
      "coming_soon": false,
      "free": false,
      "price": { "annual_price": 49, "annual_price_cents": 4900, "currency": "usd" },
      "scope": "show",
      "licensed_show_ids": [ 42, 43 ],
      "active_show_ids": [ 42 ],
      "installed": true,
      "version": "1.2.0",
      "update_available": false,
      "settings_url": "https://example.com/wp-admin/admin.php?page=benecaster#/email-editor"
    },
    {
      "slug": "guest-manager",
      "entitlement_slug": "benecaster-addon-guest-manager",
      "name": "Guest Manager",
      "note": null,
      "phase": 1,
      "state": "purchase",
      "coming_soon": false,
      "free": false,
      "price": { "annual_price": 49, "annual_price_cents": null, "currency": "usd" },
      "scope": "show",
      "licensed_show_ids": [],
      "active_show_ids": [],
      "installed": false,
      "version": null,
      "update_available": null,
      "purchase_url": "https://benecaster.com/shop/add-ons/guest-manager/"
    }
  ]
}
Field Description
slug / entitlement_slug The add-on’s short slug, and the slug a license grants it under.
state active: owned and installed. install: owned, not installed. no_entitlement: installed, but nobody on this site owns it. purchase: neither owned nor installed. Owned means some published show’s license grants it. A free add-on is always owned.
coming_soon true for an add-on that is not released yet. It keeps its state and price but carries no purchase or install link.
free true only when the catalogue prices the add-on at exactly 0. A missing price is unknown, not free.
price The yearly price as the catalogue gives it: annual_price in major units, annual_price_cents when present, and the currency. Never pre-formatted.
scope show for an add-on switched on and off per show, site for one that runs site-wide.
licensed_show_ids Published shows whose license grants the add-on. A show with no license connected is never listed.
active_show_ids Those of licensed_show_ids where it is not switched off. For a site add-on the two lists are the same.
installed / version Whether the add-on plugin is installed, and its version. version is null when it is not installed.
update_available From WordPress’s own update check: null until WordPress has checked, or when the add-on is not installed. Benecaster never installs or updates an add-on itself.

Links follow the state. Each row carries only the link that makes sense for it:

Link On
purchase_url purchase and no_entitlement rows only. An add-on someone already owns never carries one.
install_action install rows only: install when the Add-on Manager is installed and you can install plugins, otherwise download, with downloads_url pointing at your account’s Downloads page on Benecaster.com.
settings_url active rows whose add-on has its own settings screen.

A coming_soon row carries neither purchase_url nor install_action.


GET /addons/activation

Returns per-show activation state for the Add-ons screen.

Auth: manage_options plus a valid wp_rest nonce.

Body: none.

Response (200): one row per published show.

{
  "shows": [
    {
      "id": 42,
      "uuid": "8f3c1a90-...",
      "title": "The Example Show",
      "granted": [ "email-editor", "analytics-dashboard" ],
      "disabled": [ "analytics-dashboard" ]
    }
  ]
}
Field Type Description
id int Show post ID
uuid string The show’s _benecaster_show_uuid
title string Show title
granted string[] Slugs this show’s license entitles, decided the same way as benecaster_addon_is_active(): the license’s add-on list when it sent one, the all-add-ons plan flag only when it sent none
disabled string[] Slugs the podcaster has switched off for this show

Read disabled, not an enabled list. Only switch-offs are stored, so absence means on. That is why a newly granted add-on is active the moment the grant arrives, with no migration and no backfill.

Shows whose license does not grant an add-on are still listed, with the add-on simply absent from granted. The screen renders them with the reason — “Not on this show’s license” — rather than hiding them. A missing control on one show of three reads as a bug rather than a license boundary, and that is the failure this control exists to prevent.


POST /addons/activation

Switches one add-on on or off for one show.

Auth: manage_options plus a valid wp_rest nonce.

Body

Name Type Description
show_id int The show to change
addon_slug string The add-on to switch
enabled bool true to switch on, false to switch off

Response (200): the rebuilt row set, in the same shape as GET.

The write returns the full refreshed state rather than an acknowledgement, so the screen renders confirmed server state instead of its own optimistic guess. A client that patches its local copy on a bare 200 will eventually disagree with the server, and the disagreement shows up only after a save.

Errors (400)

Code Meaning
invalid_show_id show_id missing, non-positive, or not a benecaster_show post
missing_addon_slug addon_slug empty after sanitisation
no_show_uuid the show has no _benecaster_show_uuid
addon_not_granted this show’s license does not grant the slug

addon_not_granted is a refusal, not a validation nicety. A stored “off” for an add-on that was never granted is indistinguishable from a deliberate choice on the day the license does grant it — the customer would buy the add-on and find it already switched off on that one show, with nobody having switched it off.


What a successful write fires

do_action( 'benecaster_addon_activation_changed', string $addon_slug, int $show_id, bool $enabled );

This is an activation change, never an entitlement change. The customer still owns the add-on and is expected to switch it back on. A listener may drop per-show caches, transients and scheduled tasks — but must not destroy anything the customer authored or paid for, because the next switch-on cannot undo a delete.

See benecaster_addon_activation_changed.


Reading activation state in code is benecaster_addon_is_active(), which takes a required show context. Its install-wide companion, benecaster_addon_is_active_for_any_show(), answers a different question and must not be substituted for it. benecaster_addon_is_active() answers false for a show that is not connected to a license, and the install-wide one answers false when nothing on the install is connected — on every plan, including one that includes every add-on.

See Also

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