Licenses

Licenses are cryptographically signed JWT tokens that authorize software usage. Each license is tied to a customer, tier, and optionally specific features.

How It Works

1

Issue

Create a license via API. Blackwalnut signs it with your app's private key.

2

Deliver

Send the JWT token to your customer. They enter it in your software.

3

Verify

Your software verifies the token using the public key. Works offline.

License Properties

Field Type Description
installation_id UUID Unique identifier for this installation (becomes JWT subject)
customer_id string Reference to the customer
tier_id string Reference to the licensing tier
features string[]? Array of feature slugs to enable
duration_days integer? License duration (mutually exclusive with expires_at, forever)
expires_at datetime? Specific expiration date
forever boolean? Set to true for perpetual licenses (no expiration)

Creating Licenses

POST /api/v1/apps/:slug/licenses
# Time-limited license (1 year)
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/licenses \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "installation_id": "550e8400-e29b-41d4-a716-446655440000",
    "customer_id": "cus_abc123",
    "tier_id": "tier_xyz789",
    "features": ["api_access", "export"],
    "duration_days": 365
  }'

# Forever license
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/licenses \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "installation_id": "660e8400-e29b-41d4-a716-446655440000",
    "customer_id": "cus_abc123",
    "tier_id": "tier_xyz789",
    "forever": true
  }'

JWT Token Claims

The license token contains these standard and custom claims:

Claim Description
sub Subject - the installation_id
aud Audience - the app slug
iss Issuer - configurable (default: "license-server")
iat Issued at timestamp
exp Expiration timestamp (omitted for forever licenses)
jti Unique token ID (format: lic_xxxxx)
tier Tier slug
features Array of enabled feature slugs
lic_id License UUID - unique identifier for the license record

License Status

Licenses have one of three statuses:

Active

Not expired and not revoked. Valid for use.

Expired

Past the expiration date. Can be extended.

Revoked

Manually deactivated. Cannot be reactivated.

License Actions

Extend License

Add more time to an existing license:

POST /api/v1/apps/:slug/licenses/:id/extend
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/licenses/lic_abc123/extend \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"duration_days": 30}'

# Response includes new token with updated expiration

Revoke License

Permanently deactivate a license:

POST /api/v1/apps/:slug/licenses/:id/revoke
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/licenses/lic_abc123/revoke \
  -H "Authorization: Bearer sk_live_..."

Regenerate Token

Generate a new token with refreshed features from the current tier configuration. This is useful after key rotation or when tier features have been updated.

POST /api/v1/apps/:slug/licenses/:id/regenerate
# With API key authentication
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/licenses/lic_abc123/regenerate \
  -H "Authorization: Bearer sk_live_..."

# Or with license token authentication (self-renewal)
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/licenses/lic_abc123/regenerate \
  -H "Content-Type: application/json" \
  -d '{"current_token": "eyJhbGciOiJSUzI1NiIs..."}''

Feature refresh: When regenerating, the new token includes all current features from the license's tier, plus any per-license custom features. This allows licenses to automatically receive new tier features.