Skip to main content

License REST API

Endpoints for reading and managing the plugin’s license state. All admin license endpoints require manage_options and a valid X-WP-Nonce header. The subscriber-count endpoint uses a separate Bearer token authentication scheme — it is called by the license server, not the WordPress admin.


GET /benecaster/v1/license

Returns the current license status and all associated metadata.

Auth: manage_options + X-WP-Nonce

Response:

{
  "status": "active",
  "plan": "growth",
  "expires_at": "2027-05-17",
  "site_url": "https://yoursite.com",
  "activated_at": "2025-03-15T09:00:00",
  "last_validated_at": "2026-05-17T08:00:00",
  "validation_failure_count": 0,
  "subscriber_count": 41,
  "subscriber_limit": 250,
  "overage_up_count": 0,
  "upgrade_scheduled_at": null,
  "upgrade_target_tier": null,
  "downgrade_lock_active": false,
  "downgrade_lock_until": null,
  "active_addons": ["email-editor", "blocks-library"],
  "show_limit": 1,
  "active_show_count": 1,
  "all_addons_included": false,
  "priority_support": false,
  "beta_access": false,
  "license_key_masked": "XXXX-XXXX-XXXX-••••",
  "activation_count": 1,
  "activation_limit": 1
}

Field notes:

Field Notes
status active, grace_period, expired, cancelled, suspended, invalid, or none
subscriber_limit 10 for Launch plan; 50 for Starter; 250 for Growth; null for Pro, Multi-Show, Studio (unlimited)
show_limit null for Multi-Show and Studio (unlimited). 1 for all other plans.
all_addons_included true for Studio only — all add-on entitlements granted regardless of active_addons
license_key_masked Last 4 characters of the license key, rest replaced with bullet characters. null when no key stored.
activation_count null when absent from license server response
activation_limit null for unlimited-activation plans (Studio). When null, the Account tab hides the “Sites: N / M” display.
downgrade_lock_active true for one full billing period after a Launch→Starter automatic upgrade
downgrade_lock_until ISO date when the downgrade lock expires; null when not locked
validation_failure_count Consecutive days of failed validation attempts

POST /benecaster/v1/license/activate

Activates a license key for this site. Calls the external Benecaster license server.

Auth: manage_options + X-WP-Nonce

Body:

{
  "license_key": "XXXX-XXXX-XXXX-XXXX"
}

Response:

{
  "status": "activated",
  "plan": "growth",
  "expires_at": "2027-05-17",
  "active_addons": []
}

Errors:

Code HTTP Meaning
missing_license_key 400 No license_key in request body
license_not_found 404 Key not recognized by the license server
site_mismatch 409 Key is already registered to a different domain
license_expired 403 Key exists but the subscription has lapsed
activation_limit_reached 403 All activation slots in use; fail-open — feeds on existing sites continue, only new activations are blocked; ActivationLimitNotice fires in wp-admin and clears automatically on next successful activation
license_server_unreachable 503 Outbound HTTPS connection to benecaster.com failed

POST /benecaster/v1/license/deactivate

Deactivates the license on this site and clears all locally stored license state.

Auth: manage_options + X-WP-Nonce

Response:

{
  "deactivated": true
}

Calling this endpoint on a site with no active license returns 200 with { "deactivated": true } — it is idempotent.


POST /benecaster/v1/license/validate

Forces an immediate validation check against the Benecaster license server. Validation normally happens on a daily WP-Cron schedule; this endpoint triggers it on demand.

Auth: manage_options + X-WP-Nonce

Response: Updated license status object — same shape as GET /license.

Use case: After making a payment or upgrading a plan on benecaster.com, call this endpoint to pull in the updated plan details without waiting for the next daily cron run.


GET /benecaster/v1/license/subscriber-count

Returns the current paying subscriber count and total token count for this site. Used by the Benecaster license server to independently verify subscriber counts against the plugin’s telemetry payload.

Auth: Bearer token — Authorization: Bearer {license_key}

This endpoint does not accept X-WP-Nonce or cookie authentication. The intended caller is the Benecaster license server, not a WordPress admin. The license key in the Authorization header must exactly match the value stored in benecaster_license_key; mismatches return 401.

Response:

{
  "paying_subscriber_count": 41,
  "total_token_count": 167
}
Field Definition
paying_subscriber_count Count of active tokens joined to benecaster_tier_map rows where is_free_tier = 0
total_token_count Count of all active tokens regardless of tier type (paying + free combined)

Errors:

Code HTTP Meaning
rest_forbidden 401 Authorization header absent or license key mismatch

Security rationale: Reading counts directly from the database at this layer means manipulation requires database access, not just PHP access. The endpoint intentionally bypasses WordPress authentication to prevent a compromised admin account from blocking the license server’s independent count.


Daily Validation Payload

The plugin sends a POST request to the Benecaster license server once per day via WP-Cron (LicenseValidationCron). This is separate from the admin-triggered POST /benecaster/v1/license/validate endpoint above — that endpoint triggers the cron to run immediately. The payload is constructed by TelemetryCalculator immediately before the outbound request.

Core fields (always sent):

Field Type Description
license_key string The stored license key.
site_url string get_site_url() — the WordPress site URL.
subscriber_count int Paying subscriber count. On staging with a subscriber cap (Free/Starter/Growth plans): min(actual_count, staging_cap). On staging with an unlimited plan (Pro/Multi-Show/Studio, where staging_cap is null): actual count sent uncapped.
total_token_count int All active tokens (paying + free tiers combined).
plugin_version string Installed Benecaster plugin version.
wp_version string WordPress version.
php_version string PHP version.
active_bridge_slugs string[] Slugs of active subscription bridge plugins across all shows (e.g. ["memberpress"]). Always sent — software metadata, not subscriber data.
is_staging bool true when StagingManager::is_staging() is active. This field is omitted entirely in production. Absence of the field is the canonical signal that a ping is from a production site.

Telemetry fields (sent only when telemetry_opted_in = true):

Field Type Description
geo_distribution object Nested JSON of token counts by country and region: {"US": {"TX": 45, "CA": 32}, "GB": {"ENG": 67}}.
listening_hour_distribution object Download counts by UTC hour over the last 30 days: {"0": 8, "6": 22, ...}.
listening_dow_distribution object Download counts by day of week (1=Sunday) over the last 30 days.
app_distribution object Download counts by resolved podcast app name over the last 30 days.
subscriber_tenure_distribution object Active token counts by tenure bucket: {"0-30d": N, "31-90d": N, "91-365d": N, "365d+": N}.

Staging-mode payload adjustments:

When is_staging: true is present, the license server:

  • Excludes the ping from tier upgrade logic (subscriber count changes on staging do not trigger upgrades or overage counters).
  • Does not increment the site’s activation count.
  • Excludes the snapshot row from benchmark aggregation and announcement estimated-reach queries.

See Staging Mode Internals for the StagingManager API and the constant that controls staging subscriber count capping.


WP-Option State

The license endpoints read from and write to the following wp_options. These are documented here for diagnostic and test purposes — external code should call the REST endpoints rather than reading options directly.

Option Type Description
benecaster_license_key string The stored license key
benecaster_license_status string active, grace_period, expired, cancelled, suspended, invalid, or none
benecaster_license_plan string Plan slug from last validation response
benecaster_license_expires_at string ISO 8601 expiry date
benecaster_license_validated_at string ISO 8601 datetime of last successful validation
benecaster_validation_failure_days int Consecutive days of failed validation
benecaster_validation_first_failure_at string ISO 8601 datetime of first failure in current streak
benecaster_activation_count int Site activation count from license server
benecaster_activation_limit int|null Site activation limit; null when unlimited
benecaster_activation_limit_reached bool Set when activation rejected at limit; cleared on successful activation
benecaster_license_activated_at string ISO 8601 datetime of activation; cleared on deactivation
benecaster_is_new_activation bool Set on activation; deleted after first validation response