Licenses
Endpoints for activating, verifying and deactivating license keys from inside an installed plugin.
POST /wp-json/pfw-portal/v1/license/activate
Activates a license against a specific site fingerprint. Returns the per-site token used for subsequent calls.
Request body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
license_key | string | yes | PFW-XXXX-XXXX-XXXX-XXXX-XXXX |
product_slug | string | yes | wp-pfworkflow, wp-pfagent, wp-pfmanagement |
site_fingerprint | string | yes | sha256(home_url + secret_salt) |
site_url | string | yes | Customer-visible site URL |
Successful response (200):
{
"status": "ok",
"data": {
"token": "<hmac-signed-token>",
"expires_at": "2027-05-17T11:33:46Z",
"site_limit": 5,
"sites_used": 2
}
}
Failures:
| Code | error | Meaning |
|---|---|---|
| 404 | license_not_found | The key does not exist |
| 422 | license_not_active | License is expired, revoked, etc. |
| 422 | license_slot_limit_exceeded | You hit the site_limit on this license |
| 422 | product_mismatch | Key is valid but for a different product |
POST /wp-json/pfw-portal/v1/license/verify
Used by the plugin's 24h heartbeat. Confirms the license is still good and refreshes the activation timestamp on the portal side.
Request body: only Authorization: Bearer <token> is needed; the token contains the license id internally.
Successful response (200):
{
"status": "ok",
"data": {
"status": "active",
"expires_at": "2027-05-17T11:33:46Z"
}
}
Plugin behaviour on each response status:
active: normal operation continues.expired: lock out premium features but keep the plugin loaded so the user can still see the banner + click through to the customer dashboard.revoked: same as expired but with a different banner copy (contact support).
POST /wp-json/pfw-portal/v1/license/deactivate
Frees a slot. Called from the plugin's Settings → Deactivate button. Token is invalidated.
Successful response (200):
{ "status": "ok", "data": { "slots_freed": 1 } }