Licenses
License validation, activation, and device management API
License API
Manage license validation, activation, and device tracking.
Rate Limiting: All license endpoints are protected with rate limiting and progressive blocking. Excessive requests will result in temporary IP blocks with exponentially increasing durations.
Validate License
Validate and activate a license key. This endpoint is rate-limited to 5 requests per minute per IP address.
Endpoint: POST /api/licenses/validate
| Parameter | Type | Required | Description |
|---|---|---|---|
license_key | string | Yes | License key to validate |
device_id | string | No | Unique device identifier for activation tracking |
curl -X POST "$BASE_URL/api/licenses/validate" \
-H "Content-Type: application/json" \
-d '{"license_key":"XXXX-XXXX-XXXX-XXXX","device_id":"device_abc123"}'
interface LicenseValidation {
valid: boolean
license: {
id: string
status: string
product: object
activation_date: string
expiration_date: string
current_activations: number
}
}
const result: LicenseValidation = await $fetch('/api/licenses/validate', {
method: 'POST',
body: {
license_key: 'XXXX-XXXX-XXXX-XXXX',
device_id: 'device_abc123'
}
})
import requests
response = requests.post(
"$BASE_URL/api/licenses/validate",
json={
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123"
}
)
result = response.json()
package main
import (
"bytes"
"encoding/json"
"net/http"
)
func validateLicense() {
payload := map[string]string{
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123",
}
body, _ := json.Marshal(payload)
resp, _ := http.Post(
"$BASE_URL/api/licenses/validate",
"application/json",
bytes.NewBuffer(body),
)
defer resp.Body.Close()
}
<?php
$response = file_get_contents(
'$BASE_URL/api/licenses/validate',
false,
stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode([
'license_key' => 'XXXX-XXXX-XXXX-XXXX',
'device_id' => 'device_abc123'
])
]
])
);
$result = json_decode($response, true);
use reqwest::Client;
use serde_json::json;
let client = Client::new();
let response = client
.post("$BASE_URL/api/licenses/validate")
.json(&json!({
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123"
}))
.send()
.await?;
Response (Success):
{
"valid": true,
"license": {
"id": "uuid",
"status": "active",
"product": { "id": "uuid", "name": "PRO", "features": [] },
"activation_date": "2024-01-01T00:00:00Z",
"expiration_date": "2025-12-31T23:59:59Z",
"current_activations": 1
}
}
Response (Error):
{
"statusCode": 400,
"statusMessage": "Invalid license key or validation failed"
}
For security, all license validation errors return the same generic message to prevent enumeration attacks.
Deactivate License
Authentication RequiredDeactivate a license from a specific device or all devices.
Endpoint: POST /api/licenses/deactivate
| Parameter | Type | Required | Description |
|---|---|---|---|
license_key | string | Yes | License key to deactivate |
device_id | string | No | Specific device to deactivate (omit for all devices) |
curl -X POST "$BASE_URL/api/licenses/deactivate" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"license_key":"XXXX-XXXX-XXXX-XXXX","device_id":"device_abc123"}'
const result = await $fetch('/api/licenses/deactivate', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: {
license_key: 'XXXX-XXXX-XXXX-XXXX',
device_id: 'device_abc123'
}
})
import requests
response = requests.post(
"$BASE_URL/api/licenses/deactivate",
headers={"Authorization": f"Bearer {token}"},
json={
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123"
}
)
req, _ := http.NewRequest("POST",
"$BASE_URL/api/licenses/deactivate",
bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
<?php
$response = file_get_contents(
'$BASE_URL/api/licenses/deactivate',
false,
stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n" .
"Authorization: Bearer $token",
'content' => json_encode([
'license_key' => 'XXXX-XXXX-XXXX-XXXX',
'device_id' => 'device_abc123'
])
]
])
);
let response = client
.post("$BASE_URL/api/licenses/deactivate")
.header("Authorization", format!("Bearer {}", token))
.json(&json!({
"license_key": "XXXX-XXXX-XXXX-XXXX",
"device_id": "device_abc123"
}))
.send()
.await?;
Response:
{
"success": true,
"message": "Device deactivated successfully",
"remaining_activations": 4
}
License Status
Authentication RequiredCheck the current status of a license. Rate-limited to 30 requests per minute per IP.
Endpoint: GET /api/licenses/status
| Parameter | Type | Required | Description |
|---|---|---|---|
license_key | query | Yes | License key to check |
curl -X GET "$BASE_URL/api/licenses/status?license_key=XXXX-XXXX-XXXX-XXXX" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
const status = await $fetch('/api/licenses/status', {
headers: { Authorization: `Bearer ${token}` },
query: { license_key: 'XXXX-XXXX-XXXX-XXXX' }
})
import requests
response = requests.get(
"$BASE_URL/api/licenses/status",
headers={"Authorization": f"Bearer {token}"},
params={"license_key": "XXXX-XXXX-XXXX-XXXX"}
)
req, _ := http.NewRequest("GET",
"$BASE_URL/api/licenses/status?license_key=XXXX-XXXX-XXXX-XXXX",
nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, _ := http.DefaultClient.Do(req)
<?php
$response = file_get_contents(
'$BASE_URL/api/licenses/status?license_key=XXXX-XXXX-XXXX-XXXX',
false,
stream_context_create([
'http' => [
'header' => "Authorization: Bearer $token"
]
])
);
let response = client
.get("$BASE_URL/api/licenses/status")
.header("Authorization", format!("Bearer {}", token))
.query(&[("license_key", "XXXX-XXXX-XXXX-XXXX")])
.send()
.await?;
Response:
{
"license": {
"id": "uuid",
"license_key": "XXXX-XXXX-XXXX-XXXX",
"status": "active",
"activation_date": "2024-01-01T00:00:00Z",
"expiration_date": "2025-12-31T23:59:59Z",
"days_until_expiration": 365,
"activation_limit": 5,
"current_activations": 2,
"available_activations": 3,
"is_expired": false,
"is_active": true,
"product": { "id": "uuid", "name": "PRO" },
"user": { "id": "uuid", "email": "user@example.com" }
},
"active_devices": [
{
"id": "uuid",
"device_id": "device_abc123",
"activated_at": "2024-01-01T00:00:00Z",
"ip_address": "192.168.1.1"
}
]
}
Rate Limiting
All license endpoints include rate limiting headers:
| Header | Description |
|---|---|
X-RateLimit-Remaining | Requests remaining in current window |
Retry-After | Seconds until rate limit resets (when blocked) |
Rate Limits by Endpoint
| Endpoint | Requests/Minute | Block Duration |
|---|---|---|
/api/licenses/validate | 5 | 2-60 minutes (progressive) |
/api/licenses/status | 30 | 1-10 minutes (progressive) |
/api/licenses/deactivate | 10 | 2-30 minutes (progressive) |
Repeated violations result in exponentially increasing block durations up to the maximum.
License Types
| Type | Description | Features |
|---|---|---|
TRIAL | 10-minute trial session | Limited to single room |
PRO | Professional license | Unlimited rooms, custom branding |
ENTERPRISE | Enterprise license | All features, priority support, SSO |