Authentication
The License Server API uses Bearer token authentication for administrative endpoints, while public endpoints require no authentication.
Which key depends on how you're deployed
Everything below describes a self-hosted deployment, where ADMIN_API_KEY is genuinely the credential for every admin endpoint. On the hosted (SaaS) tier, almost all of these same endpoints instead authenticate with your tenant API key — ADMIN_API_KEY is never accepted there at all. The one exception, on every deployment mode, is GET /admin/build-info, which always requires ADMIN_API_KEY specifically. If you're on the hosted tier, mentally substitute "your tenant API key" everywhere this page says ADMIN_API_KEY, except for that one route.
Authentication Levels
Public Endpoints
Most endpoints are public and require no authentication:
- License verification (
/verify-license) and validation (/validate-license) - License activation (
/activate-license) and deactivation (/deactivate-license) - Usage tracking (
/track-usage) and reporting (/usage-report) - Signature verification of a submitted license (
/verify-license-fileand/verify-license-file-base64) - Export license data (
/export-license), including the file variant (/export-license/:key/file), the offline RSA-signed variant (/export-license/:key/offline), and its public key (/public-key)
Admin Endpoints (Authentication Required)
Administrative operations require a Bearer token:
- Issue licenses (
/issue-license) - Revoke licenses (
/revoke-license) - Update a license's terms —
expires_at,max_activations,limits(/admin/update-license-terms) - Delete licenses (
/delete-license) - List all licenses (
/list-licenses) - Update a license's internal notes (
/admin/update-notes) - View admin statistics (
/admin/stats) - View recent activations across all licenses (
/recent-activations) - List activations for a specific license key (
/list-activations/:key) — returns per-device instance IDs and timestamps, sensitive enough that it requires admin auth - Look up a single license's full record (
/admin/license/:key) - Reissue an activation token (
/admin/reissue-token) - Force-free a seat by
instance_id, without its token (/admin/deactivate-by-instance-id) - Reset a license's tracked usage (
/admin/reset-usage) - View build/watermark info (
/admin/build-info) - Check your subscription plan/status, hosted tier only (
/billing/status) - Start an upgrade to a paid plan, hosted tier only (
/billing/checkout)
Setting Up Authentication
1. Get Your Admin API Key
(Self-hosted only — on the hosted tier, use your tenant API key instead, from your console dashboard.)
The admin API key is configured via environment variable:
export ADMIN_API_KEY="your-secure-admin-api-key-here"Security Best Practices
- Use a strong, randomly generated key (minimum 32 characters)
- Store the key securely (environment variables, secrets manager)
- Rotate the key regularly
- Never log or expose the key in client-side code
2. Making Authenticated Requests
Include the Bearer token in the Authorization header:
curl -X POST https://your-license-api.com/v1/issue-license \
-H "Authorization: Bearer your-admin-api-key" \
-H "Content-Type: application/json" \
-d '{
"tier": "pro",
"product_id": "your-product",
"issued_to": "customer@company.com",
"expires_at": "2025-12-31T23:59:59Z"
}'Client Examples
JavaScript/Node.js
class LicenseAdminClient {
constructor(baseURL, adminKey) {
this.baseURL = baseURL;
this.headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${adminKey}`
};
}
async issueLicense(licenseData) {
const response = await fetch(`${this.baseURL}/issue-license`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(licenseData)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
return response.json();
}
async revokeLicense(key, revoked = true) {
const response = await fetch(`${this.baseURL}/revoke-license`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({ key, revoked })
});
return response.json();
}
}
// Usage
const adminClient = new LicenseAdminClient(
'https://your-license-api.com/v1',
process.env.ADMIN_API_KEY
);
const license = await adminClient.issueLicense({
tier: 'pro',
product_id: 'your-product',
issued_to: 'customer@example.com',
expires_at: '2025-12-31T23:59:59Z',
limits: { users: 100 }
});Python
import os
import requests
from typing import Dict, Any
class LicenseAdminClient:
def __init__(self, base_url: str, admin_key: str = None):
self.base_url = base_url
self.admin_key = admin_key or os.getenv('ADMIN_API_KEY')
if not self.admin_key:
raise ValueError("Admin API key is required")
self.headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.admin_key}'
}
def issue_license(self, license_data: Dict[str, Any]) -> Dict[str, Any]:
response = requests.post(
f'{self.base_url}/issue-license',
json=license_data,
headers=self.headers
)
response.raise_for_status()
return response.json()
def revoke_license(self, key: str, revoked: bool = True) -> Dict[str, Any]:
response = requests.post(
f'{self.base_url}/revoke-license',
json={'key': key, 'revoked': revoked},
headers=self.headers
)
response.raise_for_status()
return response.json()
# Usage
admin_client = LicenseAdminClient('https://your-license-api.com/v1')
license = admin_client.issue_license({
'tier': 'pro',
'product_id': 'your-product',
'issued_to': 'customer@example.com',
'expires_at': '2025-12-31T23:59:59Z',
'limits': {'users': 100}
})cURL Examples
Issue a License
curl -X POST https://your-license-api.com/v1/issue-license \
-H "Authorization: Bearer $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tier": "pro",
"product_id": "your-product",
"issued_to": "customer@company.com",
"expires_at": "2025-12-31T23:59:59Z",
"limits": {
"users": 100,
"api_calls_per_day": 10000
},
"max_activations": 5
}'Revoke a License
curl -X POST https://your-license-api.com/v1/revoke-license \
-H "Authorization: Bearer $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "7yChyZcfMG23Dx1sjBoLziPFrH4n-6f28",
"revoked": true
}'Get Admin Statistics
curl -H "Authorization: Bearer $ADMIN_API_KEY" \
https://your-license-api.com/v1/admin/statsError Handling
403 Forbidden
When authentication fails, you'll receive a 403 status with an error message:
{
"error": "Unauthorized"
}Common causes:
- Missing
Authorizationheader - The key doesn't match — the server's configured
ADMIN_API_KEYon self-hosted, or your own tenant API key on the hosted tier (see the warning at the top of this page). Either way, the key itself never expires on its own — there's no rotation/TTL mechanism, it's a static comparison. - Incorrect token format (should be
Bearer <key>)
Example Error Handling
try {
const response = await fetch('/issue-license', {
method: 'POST',
headers: {
'Authorization': `Bearer ${adminKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(licenseData)
});
if (response.status === 403) {
throw new Error('Admin authentication failed. Check your API key.');
}
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Request failed');
}
return await response.json();
} catch (error) {
console.error('License operation failed:', error.message);
throw error;
}Security Considerations
Token Storage
- Server-side: Store in environment variables or secure secret managers
- Never store admin tokens in:
- Client-side JavaScript
- Version control systems
- Log files
- URLs or query parameters
Token Rotation
- Use different keys for different environments (dev/staging/production)
ADMIN_API_KEYis a single static value with no built-in versioning or TTL — rotating it means setting a new value and restarting the server; the old key stops working the moment the new deployment is live, with no overlap window where both are accepted. Plan rotations around a brief window where in-flight admin requests using the old key will fail, rather than "without downtime."- On a hosted deployment, individual tenant API keys are a separate mechanism from the single
ADMIN_API_KEYabove, and do support live rotation with no downtime — a platform-operator operation, not something a tenant does themselves.
Network Security
- Always use HTTPS in production
- Consider IP allowlisting for admin operations
- Implement request signing for additional security if needed
Next Steps
- Learn about rate limits to understand request quotas
- Explore the Integration Guide for implementation examples
- Check out Admin Operations for license management workflows
