License Regeneration

Regenerate license tokens to refresh features from the current tier configuration. This enables licenses to automatically receive new tier features without re-issuing.

When to Regenerate

  • Tier features updated - When you add new features to a tier
  • Key rotation - After rotating RSA keys, licenses need new tokens
  • Feature sync - Ensure licenses have the latest feature set
  • Self-renewal - Allow customers to refresh their own tokens

How It Works

When you regenerate a license token, Blackwalnut:

  1. Fetches the current feature set from the license's tier
  2. Merges tier features with any per-license custom features
  3. Generates a new JWT token with the updated features
  4. Preserves original installation_id, customer_id, tier, and expiration
  5. Updates the token's jti (JWT ID) and iat (issued at) claims

Tip: The lic_id claim (license UUID) remains the same across regenerations, allowing you to track the same license even after token updates.

Authentication Methods

The regenerate endpoint supports two authentication methods:

1. API Key Authentication (Admin)

Use a secret API key (sk_*) for server-side operations. This method has full access and can regenerate any license.

API Key Auth
$ curl -X POST /api/v1/apps/my-app/licenses/lic_abc123/regenerate \
  -H "Authorization: Bearer sk_live_your_api_key"

2. License Token Authentication (Self-Renewal)

Provide the current license token in the request body. This enables end-user software to refresh its own license without exposing your API key.

License Token Auth
$ curl -X POST /api/v1/apps/my-app/licenses/lic_abc123/regenerate \
  -H "Content-Type: application/json" \
  -d '{"current_token": "eyJhbGciOiJSUzI1NiIs..."}''

Important: When using license token authentication, the token must be valid, non-expired, and belong to the license being regenerated. Expired tokens cannot be used for self-renewal.

Use Case: Feature Updates

When you add a new feature to a tier, existing licenses don't automatically receive it. Use regeneration to update licenses with the new feature:

Feature Update Flow
# 1. Original license has tier "pro" with features: ["api_access"]

# 2. You add "webhooks" feature to the "pro" tier
$ curl -X POST /api/v1/apps/my-app/tiers/pro/features \
  -H "Authorization: Bearer sk_live_..." \
  -d '{"name": "Webhooks", "slug": "webhooks"}'

# 3. Regenerate the license to get updated features
$ curl -X POST /api/v1/apps/my-app/licenses/lic_abc123/regenerate \
  -H "Authorization: Bearer sk_live_..."

# Response - new token includes "webhooks"
{
  "data": {
    "token": "eyJhbGciOiJSUzI1NiIs...",
    "features": ["api_access", "webhooks"]
  }
}

Use Case: Client-Side Refresh

Enable your software to refresh its license token without exposing your API key:

client_license.py
import requests
import jwt

class LicenseClient:
    def __init__(self, app_slug, license_id, token):
        self.app_slug = app_slug
        self.license_id = license_id
        self.token = token
        self.base_url = "https://license.yourapp.com"

    def refresh_token(self):
        """Refresh license to get latest tier features."""
        url = f"{self.base_url}/api/v1/apps/{self.app_slug}/licenses/{self.license_id}/regenerate"
        response = requests.post(url, json={"current_token": self.token})

        if response.status_code == 200:
            self.token = response.json()["data"]["token"]
            return True
        return False

    def get_features(self):
        """Decode token and return features."""
        # Note: Verify signature with public key in production
        payload = jwt.decode(self.token, options={"verify_signature": False})
        return payload.get("features", [])

Custom Features Preservation

If a license has per-license custom features (beyond the tier's features), these are preserved and merged during regeneration:

  • Tier features - Updated from current tier configuration
  • Custom features - Preserved from the license record
  • Result - Union of both sets in the new token

Example: License has custom feature ["custom_integration"]. Tier "pro" has ["api_access", "webhooks"]. Regenerated token features: ["api_access", "webhooks", "custom_integration"]

Error Handling

Error Cause Solution
401 Unauthorized No authentication provided Include API key or current_token
400 Token Expired current_token has expired Use API key auth instead
400 Token Mismatch Token belongs to different license Use correct license ID
400 License Revoked License has been revoked Cannot regenerate revoked licenses
400 Missing Tier License's tier was deleted Update license to a valid tier
403 Forbidden Used public key (pk_*) Use secret key (sk_*) or license token

Best Practices

  • Implement automatic token refresh in your client software
  • Refresh tokens periodically (e.g., daily) to get feature updates
  • Cache the refreshed token locally to avoid unnecessary API calls
  • Handle 400 errors gracefully - expired licenses cannot be refreshed
  • Use license token auth for client-side refresh, API key for server-side bulk operations
  • After key rotation, trigger regeneration for all active licenses