Licensing REST And Signed Requests

Routes

POST /wp-json/awecommerce/v1/licenses/activate
POST /wp-json/awecommerce/v1/licenses/deactivate
POST /wp-json/awecommerce/v1/licenses/check
POST /wp-json/awecommerce/v1/licenses/status
POST /wp-json/awecommerce/v1/updates/check
GET  /wp-json/awecommerce/v1/packages/{token}

Activation With License Key

curl -X POST "https://store.example.com/wp-json/awecommerce/v1/licenses/activate" \
  -H "Content-Type: application/json" \
  -d '{
    "license_key": "cf4287722dd983e4c61d88353906134d",
    "product_slug": "example-plugin",
    "software_kind": "wordpress_plugin",
    "site_url": "https://customer.example",
    "normalized_domain": "customer.example",
    "site_uuid": "site-uuid",
    "install_uuid": "install-uuid",
    "environment": "production",
    "multisite": false,
    "network_active": false,
    "installed_version": "1.2.3",
    "wp_version": "6.5",
    "php_version": "8.2",
    "locale": "en_US",
    "client": {
      "sdk_version": "1.4.0",
      "contract_version": 1
    }
  }'

Activation With Bootstrap Token

curl -X POST "https://store.example.com/wp-json/awecommerce/v1/licenses/activate" \
  -H "Content-Type: application/json" \
  -d '{
    "bootstrap_token": "awc_bt_...",
    "product_slug": "example-plugin",
    "software_kind": "wordpress_plugin",
    "site_url": "https://customer.example",
    "normalized_domain": "customer.example",
    "site_uuid": "site-uuid",
    "install_uuid": "install-uuid",
    "installed_version": "1.2.3"
  }'

Activation Response Handling

Store:

$state = [
    'license_status' => $response['license']['status'] ?? 'active',
    'runtime_behavior' => $response['license']['runtime_behavior'] ?? 'continue_with_notice',
    'chargeback_runtime_behavior' => $response['license']['chargeback_runtime_behavior'] ?? 'continue_with_notice',
    'license_site_id' => $response['activation']['license_site_id'] ?? '',
    'site_uuid' => $response['activation']['site_uuid'] ?? '',
    'site_secret' => $response['site_secret'] ?? '',
    'secret_version' => $response['secret_version'] ?? 1,
    'manifest_url' => $response['manifest_url'] ?? '',
];

update_option('awc_client_state_example-plugin', $state, false);

Signed Request Canonical String

The bundled client signs status and update checks with:

$canonical = implode("\n", [
    'POST',
    $path,
    '',
    $bodyHash,
    $timestamp,
    $nonce,
    $siteUuid,
    (string) $secretVersion,
]);

$signature = rtrim(strtr(
    base64_encode(hash_hmac('sha256', $canonical, $siteSecret, true)),
    '+/',
    '-_'
), '=');

Headers:

Content-Type: application/json
X-AWC-Timestamp: {timestamp}
X-AWC-Nonce: {nonce}
X-AWC-Site-UUID: {site_uuid}
X-AWC-Secret-Version: {secret_version}
X-AWC-Body-SHA256: {sha256_body_hash}
X-AWC-Signature: {signature}

Signed Status Request

curl -X POST "https://store.example.com/wp-json/awecommerce/v1/licenses/status" \
  -H "Content-Type: application/json" \
  -H "X-AWC-Timestamp: 1782300000" \
  -H "X-AWC-Nonce: abc123abc123abcd" \
  -H "X-AWC-Site-UUID: site-uuid" \
  -H "X-AWC-Secret-Version: 1" \
  -H "X-AWC-Body-SHA256: body-hash" \
  -H "X-AWC-Signature: signature" \
  -d '{
    "product_slug": "example-plugin",
    "software_kind": "wordpress_plugin"
  }'

The status response can include license.runtime_behavior and license.chargeback_runtime_behavior. Clients should persist refreshed values even when the license is rejected with a structured response.

Update Check Request

curl -X POST "https://store.example.com/wp-json/awecommerce/v1/updates/check" \
  -H "Content-Type: application/json" \
  -H "X-AWC-Timestamp: 1782300000" \
  -H "X-AWC-Nonce: abc123abc123abcd" \
  -H "X-AWC-Site-UUID: site-uuid" \
  -H "X-AWC-Secret-Version: 1" \
  -H "X-AWC-Body-SHA256: body-hash" \
  -H "X-AWC-Signature: signature" \
  -d '{
    "product_slug": "example-plugin",
    "software_kind": "wordpress_plugin",
    "installed_version": "1.2.2"
  }'

Protected Package Download

curl -L "https://store.example.com/wp-json/awecommerce/v1/packages/awc_pkg_..." \
  -o example-plugin.zip

Package tokens are scoped, expiring, limited-use URLs. Do not treat them as stable public package URLs.

Was this documentation helpful?