License Management API Documentation
Complete API reference and integration guide for license validation and management. Build secure, scalable license validation into your applications with our comprehensive API.
🚀 What You'll Find Here
- Complete API Reference: All endpoints with request/response examples
- Integration Guides: Step-by-step implementation instructions
- Code Examples: Ready-to-use code in Python, JavaScript, and C#
- Security Features: Hardware fingerprinting, fraud detection, and more
- Production Ready: Deployment, monitoring, and best practices
Quick Start
Get up and running with the License Management API in minutes.
1. Get Your API Credentials
POST /api/auth/login
Content-Type: application/json
{
"email": "admin@licensemanagement.com",
"password": "your_password"
}
2. Validate a License
POST /api/validation/validate
Content-Type: application/json
{
"license_key": "ABCD-EFGH-IJKL-MNOP",
"software_api_key": "sk_1234567890abcdef1234567890abcdef1234567890abcdef"
}
3. Handle the Response
import requests
import platform
import psutil
# Generate hardware fingerprint
hardware_fingerprint = {
'cpu': platform.processor(),
'memory': f"{psutil.virtual_memory().total // (1024**3)}GB",
'os': f"{platform.system()} {platform.release()}",
'device_id': 'unique_device_identifier'
}
response = requests.post('http://localhost:5000/api/validation/validate', json={
'license_key': 'ABCD-EFGH-IJKL-MNOP',
'software_api_key': 'sk_1234567890abcdef1234567890abcdef1234567890abcdef',
'user_email': 'user@example.com',
'hardware_fingerprint': hardware_fingerprint
})
if response.status_code == 200:
data = response.json()
if data['valid']:
print("✅ License is valid!")
print(f"Expires: {data['license']['expires_at']}")
print(f"License activations: {data['license']['current_activations']}/{data['license']['max_activations']}")
# Device activation info
if 'device_activation' in data:
device = data['device_activation']
print(f"Device: {'New' if device['is_new_device'] else 'Existing'}")
print(f"Device activations: {device['activation_count']}")
else:
print("❌ License is invalid")
print(f"Error: {data.get('message', 'Unknown error')}")
else:
print(f"Error: {response.status_code}")
// Generate hardware fingerprint
const hardwareFingerprint = {
cpu: navigator.userAgentData?.platform || navigator.platform,
memory: `${navigator.deviceMemory || 'Unknown'}GB`,
screen_resolution: `${screen.width}x${screen.height}`,
os: navigator.userAgentData?.platform || navigator.platform,
browser: navigator.userAgent,
device_id: 'unique_device_identifier'
};
const response = await fetch('http://localhost:5000/api/validation/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
license_key: 'ABCD-EFGH-IJKL-MNOP',
software_api_key: 'sk_1234567890abcdef1234567890abcdef1234567890abcdef',
user_email: 'user@example.com',
hardware_fingerprint: hardwareFingerprint
})
});
const data = await response.json();
if (response.ok && data.valid) {
console.log('✅ License is valid!');
console.log(`Expires: ${data.license.expires_at}`);
console.log(`License activations: ${data.license.current_activations}/${data.license.max_activations}`);
// Device activation info
if (data.device_activation) {
const device = data.device_activation;
console.log(`Device: ${device.is_new_device ? 'New' : 'Existing'}`);
console.log(`Device activations: ${device.activation_count}`);
}
} else {
console.log('❌ License is invalid');
console.log(`Error: ${data.message || 'Unknown error'}`);
}
using var client = new HttpClient();
var payload = new {
license_key = "ABCD-EFGH-IJKL-MNOP",
software_api_key = "sk_1234567890abcdef1234567890abcdef1234567890abcdef"
};
var response = await client.PostAsJsonAsync(
"http://localhost:5000/api/validation/validate",
payload
);
if (response.IsSuccessStatusCode) {
var data = await response.Content.ReadFromJsonAsync();
if (data.valid) {
Console.WriteLine("✅ License is valid!");
Console.WriteLine($"Expires: {data.license.expires_at}");
}
}
Authentication & Connection
The License Management API uses JWT (JSON Web Tokens) for authentication. All protected endpoints require a valid access token.
🔑 Authentication Flow
- Login: Exchange credentials for access and refresh tokens
- Access: Use access token for API requests (24-hour expiry)
- Refresh: Use refresh token to get new access tokens (30-day expiry)
- Logout: Invalidate tokens when done
Base URL Configuration
Development: http://localhost:5000
Production: https://api.yourdomain.com
Login Endpoint
POST
/api/auth/login
{
"email": "admin@licensemanagement.com",
"password": "admin123"
}
{
"message": "Login successful",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"expires_in": 86400,
"user": {
"id": 1,
"email": "admin@licensemanagement.com",
"role": "admin"
}
}
Using Access Tokens
Include the access token in the Authorization header for all protected requests:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
Content-Type: application/json
Software API Key Authentication
For license validation from client applications, use software API keys instead of JWT tokens. Each software entry automatically generates a unique API key when created.
🔑 API Key Format
- Prefix:
sk_(software key) - Length: 51 characters total (3 prefix + 48 hex characters)
- Example:
sk_1234567890abcdef1234567890abcdef1234567890abcdef
🛡️ Security Features
- Unique per Software: Each software application has its own API key
- Cryptographically Secure: Generated using
secrets.token_hex(24) - Database Constraints: Unique constraint prevents duplicates
- Regeneration Support: API keys can be regenerated for security
📍 Where to Find Your API Key
- Navigate to Dashboard → Software Management
- View the API Key column in the software table
- Click the View button for detailed software information
- Copy the API key using the copy button
Authentication API
User authentication, JWT token management, and session handling endpoints.
🔐 Key Features
- JWT Authentication: Secure token-based authentication
- Role-Based Access: Admin, Manager, and User role management
- Token Refresh: Automatic token renewal for seamless sessions
- User Registration: Account creation with email verification
User Login
POST
/api/auth/login
• No Authentication Required
Authenticate user credentials and receive JWT access and refresh tokens.
{
"email": "admin@example.com",
"password": "securepassword123"
}
{
"message": "Login successful",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"user": {
"id": 1,
"email": "admin@example.com",
"name": "System Administrator",
"role": "admin",
"status": "active"
}
}
{
"message": "Invalid email or password"
}
Refresh Token
POST
/api/auth/refresh
• Requires Refresh Token
Refresh an expired access token using a valid refresh token.
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"message": "Token refreshed successfully"
}
Get Current User
GET
/api/auth/me
• Requires Authentication
Get information about the currently authenticated user.
{
"user": {
"id": 1,
"email": "admin@example.com",
"name": "System Administrator",
"role": "admin",
"status": "active",
"created_at": "2025-01-01T10:00:00Z",
"last_login": "2025-07-10T15:30:00Z"
}
}
User Registration
POST
/api/auth/register
• No Authentication Required
Register a new user account with email verification.
{
"email": "newuser@example.com",
"password": "securepassword123",
"name": "John Smith",
"phone": "+1234567890",
"country_code": "+1"
}
{
"message": "User registered successfully",
"user": {
"id": 25,
"email": "newuser@example.com",
"name": "John Smith",
"role": "user",
"status": "active",
"created_at": "2025-07-10T15:30:00Z"
}
}
Users API
Manage user accounts, roles, and contact information.
List Users
GET
/api/users
• Requires Authentication
Query Parameters
page(int): Page number (default: 1)per_page(int): Items per page (default: 20)role(string): Filter by role (admin, manager, user)status(string): Filter by status (active, inactive, suspended)search(string): Search by name or email
GET /api/users?page=1&per_page=20&role=user
Authorization: Bearer
{
"users": [
{
"id": 1,
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"phone": "+1234567890",
"whatsapp": "+1234567890",
"telegram": "@johndoe",
"country_code": "+1",
"role": "user",
"status": "active",
"email_verified": true,
"two_factor_enabled": false,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 1,
"pages": 1
}
}
Create User
POST
/api/users
• Requires Authentication (Admin/Manager)
{
"email": "newuser@example.com",
"password": "SecurePassword123!",
"first_name": "Jane",
"last_name": "Smith",
"phone": "+1987654321",
"whatsapp": "+1987654321",
"telegram": "@janesmith",
"country_code": "+1",
"role": "user"
}
Users Statistics
GET
/api/users/stats
• Requires Authentication (Admin/Manager)
Get comprehensive user statistics for dashboard overview cards.
{
"total_users": 150,
"active_users": 142,
"users_with_active_licenses": 89,
"recent_registrations": 12
}
Licenses API
Create, manage, and validate software licenses.
Create License
POST
/api/licenses
• Requires Authentication (Admin/Manager)
{
"user_email": "customer@mail.com",
"license_type": "trial",
"device_limit": 1,
"validity_value": 72,
"validity_unit": "hours",
"start_on_activation": true,
"features": ["feature.id"],
"send_email": true
}
Validity & delayed start. Send the duration as validity_value + validity_unit (days | hours | minutes) — or the legacy validity_days. 0 / omitted = never expires. The duration is stored server-side as seconds and expires_at is precise to the second.
With "start_on_activation": true the license ships dormant: it is fully usable immediately, but expires_at stays NULL and the validity clock only starts on the license's first device activation (activated_at records the moment; later deactivations/reactivations never restart it). Such licenses appear under the Pending activation status. Plans can carry a default validity too (validity_seconds).
{
"message": "License created successfully",
"license": {
"id": 123,
"uuid": "550e8400-e29b-41d4-a716-446655440001",
"license_key": "ABCD-EFGH-IJKL-MNOP",
"software_id": 1,
"user_id": 456,
"type": "ultimate",
"status": "active",
"expires_at": "2025-01-15T10:30:00Z",
"max_activations": 3,
"current_activations": 0,
"allow_deactivation": true,
"created_at": "2024-01-15T10:30:00Z"
}
}
List Licenses — filters & CSV export
GET
/api.php?endpoint=licenses
• Requires Authentication • App selected
Query parameters: page, per_page, search (key/email), type, and status — one of all | active | pending | expiring | expired | inactive (expiring = active and expiring within 7 days; pending = start-on-activation licenses whose clock hasn't started yet). Add &export=csv to download the whole filtered set as CSV instead of JSON.
GET /api.php?endpoint=licenses&page=1&search=customer@mail.com&status=expiring
GET /api.php?endpoint=licenses&status=expired&export=csv
Extend Licenses (single or bulk)
POST
/api.php?endpoint=licenses/extend
• Requires Authentication • App selected
Adds time to one or many licenses: send value + unit (days | hours | minutes), or legacy days. Extends from the later of (now, current expiry): expired licenses restart from today, future ones keep their runway. Dormant (pending-activation) licenses get their stored duration extended, so they still receive the full added time after first activation. Perpetual (no-expiry) licenses are skipped.
{
"ids": [123, 124, 125],
"value": 6,
"unit": "hours"
}
{
"success": true,
"extended": 2,
"skipped_perpetual": 1,
"not_found": 0,
"message": "Extended 2 license(s) by 6 hours. 1 perpetual license(s) skipped."
}
Reduce Licenses (single or bulk)
POST
/api.php?endpoint=licenses/reduce
• Requires Authentication • App selected
Subtracts time from one or many licenses: send value + unit (days | hours | minutes), or legacy days. The expiry is pulled earlier by the given amount — the result may land in the past, expiring the license immediately. Only licenses with a running clock are touched: perpetual (no-expiry) licenses and already-expired ones are skipped, and a pending-activation license's stored duration shrinks only while time would remain (a reduction consuming all of it is skipped).
{
"ids": [123, 124, 125],
"value": 7,
"unit": "days"
}
{
"success": true,
"reduced": 1,
"skipped_perpetual": 0,
"skipped_expired": 1,
"skipped_too_small": 1,
"not_found": 0,
"message": "Reduced 1 license(s) by 7 days. skipped 1 already expired, skipped 1 (reduction exceeds remaining validity)."
}
Resend License Email
POST
/api.php?endpoint=licenses/resend
• Requires Authentication • App selected
Re-emails an existing license key to its customer. Requires email delivery to be enabled in Settings with an SMTP server (or the host's mailer) configured.
{ "id": 123 }
Send Test Email
POST
/api.php?endpoint=email-test
• Requires Authentication • App selected
Sends a short test message through the configured transport — the same pipeline license emails use. Configure SMTP in Settings (smtp_host, smtp_port, smtp_secure = tls/ssl/none, smtp_username, smtp_password, plus email_enabled, email_from_name, email_from_email). Without smtp_host, PHP mail() is used as a fallback. Saving an empty smtp_password keeps the stored one.
{ "to": "you@example.com" }
Change Own Password
POST
/api.php?endpoint=change-password
• Requires Authentication
Changes the logged-in admin's password. The current password is required so a hijacked session cannot silently take over the account. Minimum 8 characters.
{
"current_password": "old-pass",
"new_password": "new-strong-pass"
}
Activity Log Export & Device Details
GET
/api.php?endpoint=activity&export=csv
• Requires Authentication • App selected
The activity log supports the same filters as the Activity page (q, action, status, email, days) — add &export=csv to download. Likewise GET /api.php?endpoint=license-devices/{id}&export=csv exports a license's devices (now including CPU, hostname, MAC address and fingerprint hash, which are also returned in the JSON listing).
GET /api.php?endpoint=activity&action=heartbeat&days=7&export=csv
GET /api.php?endpoint=license-devices/123&export=csv
License Validation API
The core endpoint for validating licenses in your applications.
🔍 Key Features
- Hardware Fingerprinting: Unique device identification
- Fraud Detection: ML-powered risk assessment
- Geographic Validation: Location-based restrictions
- Real-time Monitoring: Live license status updates
Validate License
POST
/api/validation/validate
• No Authentication Required (uses software API key)
Validates a license key and tracks device activation. This endpoint implements comprehensive device activation tracking:
- New Device Activation: When a new device (unique hardware fingerprint) validates a license, it increments the license's
current_activationscount - Existing Device Re-validation: When an existing device re-validates, it updates the device's
activation_countandlast_seentimestamp without incrementing the license activation count - Activation Limit Enforcement: New device activations are blocked when the license's
max_activationslimit is reached - Device Identification: Uses SHA256 hashing of hardware fingerprint data for consistent device identification
- Comprehensive Response: Returns detailed device activation information including device ID, activation count, timestamps, and new device flag
{
"license_key": "ABCD-EFGH-IJKL-MNOP",
"software_api_key": "sk_1234567890abcdef1234567890abcdef1234567890abcdef",
"user_email": "user@example.com",
"hardware_fingerprint": {
"cpu": "Intel Core i7-9700K",
"memory": "16GB",
"storage": "512GB SSD",
"mac_address": "00:1B:44:11:3A:B7",
"screen_resolution": "1920x1080",
"os": "Windows 10 Pro",
"browser": "Chrome 91.0.4472.124",
"motherboard": "ASUS ROG STRIX Z390-E",
"gpu": "NVIDIA GeForce RTX 3080",
"device_id": "device001"
},
"ip_address": "192.168.1.100",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
license_key |
string | Yes | The license key to validate (format: XXXX-XXXX-XXXX-XXXX) |
software_api_key |
string | Yes | Software API key for authentication (format: sk_[random]) |
user_email |
string | No | Email address of the license owner (for email-license validation) |
hardware_fingerprint |
object | No | Hardware fingerprint data for device activation tracking |
ip_address |
string | No | Client IP address for fraud detection |
user_agent |
string | No | Client user agent for fraud detection |
{
"valid": true,
"message": "License is valid",
"license": {
"id": 123,
"license_key": "ABCD-EFGH-IJKL-MNOP",
"type": "ultimate",
"status": "active",
"expires_at": "2024-12-31T23:59:59Z",
"max_activations": 3,
"current_activations": 1,
"created_at": "2024-01-01T00:00:00Z",
"last_validated": "2024-07-10T14:30:00Z"
},
"software": {
"id": 1,
"name": "YouTube CPM GoLogin Automation Software",
"version": "1.0.1",
"max_devices_per_license": 3,
"allow_offline_validation": true,
"require_hardware_fingerprint": true,
"fraud_detection_enabled": true
},
"device_activation": {
"device_id": "b926d58a-d55e-40ea-b9db-b7b2a20d2fe0",
"fingerprint_hash": "a0a109eeb0ee4283...",
"is_new_device": false,
"activation_count": 3,
"first_seen": "2024-07-01T10:15:30.123456",
"last_seen": "2024-07-10T14:30:00.654321"
},
"validation_timestamp": "2024-07-10T14:30:00Z",
"hardware_fingerprint_received": true
}
Response Fields (Success)
| Field | Type | Description |
|---|---|---|
valid |
boolean | Whether the license validation was successful |
message |
string | Human-readable validation result message |
license |
object | License information including status, expiration, and activation counts |
software |
object | Software information including name, version, and configuration |
device_activation |
object | Device activation tracking information (when hardware_fingerprint provided) |
device_activation.device_id |
string | Unique device identifier (UUID) |
device_activation.fingerprint_hash |
string | Truncated SHA256 hash of hardware fingerprint (for security) |
device_activation.is_new_device |
boolean | Whether this is a new device activation (true) or existing device re-validation (false) |
device_activation.activation_count |
integer | Number of times this specific device has validated the license |
device_activation.first_seen |
string | ISO timestamp when this device first activated the license |
device_activation.last_seen |
string | ISO timestamp when this device last validated the license |
validation_timestamp |
string | ISO timestamp of the validation request |
hardware_fingerprint_received |
boolean | Whether hardware fingerprint data was included in the request |
{
"valid": false,
"message": "License has expired",
"error_code": "LICENSE_EXPIRED",
"license": {
"id": 123,
"license_key": "ABCD-EFGH-IJKL-MNOP",
"status": "expired",
"expires_at": "2023-12-31T23:59:59Z"
}
}
{
"valid": false,
"message": "Invalid software API key",
"error_code": "INVALID_API_KEY"
}
{
"valid": false,
"message": "License not found",
"error_code": "LICENSE_NOT_FOUND"
}
{
"valid": false,
"message": "Missing required parameters: license_key, software_api_key",
"error_code": "MISSING_PARAMETERS"
}
{
"valid": false,
"message": "License cannot be activated: maximum activations reached or license inactive",
"error_code": "ACTIVATION_LIMIT_EXCEEDED"
}
{
"valid": false,
"message": "Email-license key mismatch: provided email does not match license owner",
"error_code": "EMAIL_LICENSE_MISMATCH"
}
Licenses Statistics
GET
/api/licenses/stats
• Requires Authentication
Get comprehensive license statistics for dashboard overview cards. Regular users see only their own license statistics.
{
"total_licenses": 450,
"active_licenses": 380,
"expired_licenses": 45,
"total_revenue": 37999.20
}
{
"total_licenses": 3,
"active_licenses": 2,
"expired_licenses": 1,
"total_revenue": 199.98
}
Device Management API
Manage device activations and hardware fingerprints for licenses.
🔧 Key Features
- Device Tracking: Monitor all devices activated for each license
- Remote Deactivation: Remove devices from licenses remotely
- Activation Management: View device activation history and statistics
- Security Monitoring: Track device fingerprints and trust levels
Get License Devices
GET
/api/licenses/{license_id}/devices
• Requires Authentication
Retrieve all devices activated for a specific license. Regular users can only view devices for their own licenses.
GET /api/licenses/123/devices
Authorization: Bearer <access_token>
{
"devices": [
{
"id": 1,
"uuid": "30963ab6-dbea-42c6-8459-c5de221a96ea",
"device_id": "30963ab6-dbea-42c6-8459-c5de221a96ea",
"fingerprint_hash": "a1b2c3d4e5f6...",
"first_seen": "2024-07-01T10:15:30.123456",
"last_seen": "2024-07-10T14:30:00.654321",
"activation_count": 15,
"trust_level": "high",
"quality_score": 0.95,
"device_info": {
"cpu": "Intel Core i7-9700K",
"memory": "16GB",
"os": "Windows 10 Pro",
"browser": "Chrome 91.0.4472.124"
}
}
],
"total_devices": 1,
"license": {
"id": 123,
"license_key": "ABCD-EFGH-IJKL-MNOP",
"current_activations": 1,
"max_activations": 3
}
}
Remove Device from License
DELETE
/api/licenses/{license_id}/devices/{device_id}
• Requires Authentication (Admin/Manager)
Remove a specific device from a license. This decreases the license's current activation count.
DELETE /api/licenses/123/devices/30963ab6-dbea-42c6-8459-c5de221a96ea
Authorization: Bearer <access_token>
{
"message": "Device removed successfully",
"license": {
"id": 123,
"license_key": "ABCD-EFGH-IJKL-MNOP",
"current_activations": 0,
"max_activations": 3
}
}
Clear All License Devices
DELETE
/api/licenses/{license_id}/devices
• Requires Authentication (Admin/Manager)
Remove all devices from a license. This resets the license's current activation count to 0.
DELETE /api/licenses/123/devices
Authorization: Bearer <access_token>
{
"message": "All 2 devices cleared successfully",
"devices_removed": 2,
"license": {
"id": 123,
"license_key": "ABCD-EFGH-IJKL-MNOP",
"current_activations": 0,
"max_activations": 3
}
}
License Deactivation
POST
/api/validation/deactivate
• No Authentication Required (uses software API key)
Deactivate a license on the current device. This endpoint is designed for client software to deactivate licenses when uninstalling or transferring to another device.
{
"license_key": "ABCD-EFGH-IJKL-MNOP",
"software_api_key": "sk_1234567890abcdef1234567890abcdef1234567890abcdef",
"hardware_fingerprint": {
"cpu": "Intel Core i7-9700K",
"memory": "16GB",
"storage": "512GB SSD",
"mac_address": "00:1B:44:11:3A:B7",
"os": "Windows 10 Pro",
"device_id": "device001"
}
}
{
"message": "License deactivated successfully",
"success": true,
"license": {
"license_key": "ABCD-EFGH-IJKL-MNOP",
"current_activations": 2,
"max_activations": 3,
"status": "active"
}
}
Device Abuse Guard API
The device abuse guard protects trial licenses: when a device's last license (per app) was a trial, activating a different trial on it — under any account — is refused with DEVICE_ABUSE_BLOCKED until an admin releases the device. Paid licenses (pro / business / plans) are never blocked, and any paid activation resets the device's chain (trial → pro → trial is allowed). Each trial license carries a device_guard flag (on by default, controllable via the Create/Edit License modal) — an unguarded trial neither blocks nor marks devices. Guard history survives deactivation, device removal and license deletion, so removing a license is never an escape hatch.
Admin monitoring and release controls live on the Trial Devices page in the admin panel (Devices / Abuse Alerts tabs, with a pending-alerts badge in the sidebar).
Blocked Activation Response
POST /api/devices/activate • returned when the guard fires
Activating a guarded trial on a device whose last license was a different trial returns 403 with a machine-readable code. Before the block: the device cannot be hard-removed from a guarded trial either — POST /api/devices/delete and DELETE /api/devices answer 403 with "Trial devices cannot be permanently removed. Deactivate the device instead." Deactivation remains the supported exit, and re-activating the same license on the same device always works.
{
"success": false,
"error": "License abuse not allowed: this device was already used with a trial license. Please purchase a license or contact support.",
"code": "DEVICE_ABUSE_BLOCKED"
}
The desktop client should surface the error message; the code field lets clients branch (e.g. show a purchase link).
List Guarded Devices
GET /api/trial-devices?page=1&per_page=25&q=<search> • Admin (session or X-Admin-Key)
Lists one row per device the guard has seen (its last license per app), newest activation first. q searches device ID, email and license key; allowlisted=1|0 filters by status.
{
"data": [
{
"id": 12,
"device_id": "devGuard-01",
"device_hw_hash": "9f2a…e1",
"last_license_id": 341,
"last_license_key": "ABCD-EFGH-IJKL-MNOP",
"last_license_type": "trial",
"last_user_email": "user@example.com",
"last_trial_at": "2026-08-30 10:14:00",
"last_activation_at": "2026-08-30 10:14:00",
"trial_count": 1,
"is_allowlisted": 0,
"note": null
}
],
"total": 1, "page": 1, "per_page": 25
}
Allowlist / Re-guard a Device
POST /api/trial-devices/allowlist • Admin
Releases (or re-arms) the guard for a whole device. Allowlisted devices can activate any license.
{ "id": 12, "allowlisted": 1, "note": "approved by support" }
{ "success": true, "message": "Device allowlisted — guard released." }
Forget a Device (reset its history)
DELETE /api/trial-devices/{id} • Admin
Removes the device's guard record, its approvals and auto-resolves its pending alerts — fresh trials are allowed on it again.
{ "success": true, "message": "Device record reset. Fresh trials are allowed on it again." }
List Abuse Alerts
GET /api/abuse-alerts?status=pending&page=1 • Admin
One alert per (device, attempted license). Repeat blocked attempts bump attempts and re-pend a resolved alert. status accepts pending, resolved or all (default).
{
"data": [
{
"id": 7,
"device_id": "devGuard-01",
"license_id": 342,
"license_key": "KLMN-OPQR-STUV-WXYZ",
"user_email": "other@example.com",
"attempts": 2,
"first_attempt_at": "2026-08-30 10:20:03",
"last_attempt_at": "2026-08-30 10:22:41",
"status": 1,
"resolved_action": null,
"resolved_by": null,
"resolved_at": null,
"note": null
}
],
"total": 1, "page": 1, "per_page": 25
}
Resolve an Abuse Alert
POST /api/abuse-alerts/resolve • Admin
approve_device allowlists the device (everything is allowed on it). approve_license grants a narrow exemption for exactly this license + device. dismiss only closes the alert. After any approval the user simply retries activation.
{ "id": 7, "action": "approve_license", "note": "verified purchase" }
{ "success": true, "message": "Alert resolved (approve_license)." }
License Creation Parameter
POST /api/licenses PUT /api/licenses/{id} • Admin
Trial licenses accept device_guard (boolean, default true). Set it to false to issue a trial that is fully outside the guard — it neither blocks a device nor marks it. The admin panel shows this as the "Device abuse guard" checkbox in the Create/Edit License modal (trial type only). Omitting the field on create keeps the default (on); on update, omitting it keeps the stored value.
Software API
Manage software applications, versions, and API keys.
Create Software
POST
/api/software
• Requires Authentication (Admin/Manager)
Required Fields
name(string): Software nameversion(string): Software version (e.g., "1.0.0")description(string): Software description
Optional Fields
status(string): Software status (default: "active")max_activations(int): Max devices per license (default: 1)allow_offline_validation(bool): Allow offline validation (default: true)require_hardware_fingerprint(bool): Require hardware fingerprint (default: true)fraud_detection_enabled(bool): Enable fraud detection (default: true)
sk_ + 48-character hex string) is automatically generated for license validation purposes.
{
"name": "YouTube CPM GoLogin Automation Software",
"description": "Professional automation software for YouTube CPM optimization",
"version": "1.0.0",
"status": "active",
"max_activations": 3,
"allow_offline_validation": true,
"require_hardware_fingerprint": true,
"fraud_detection_enabled": true
}
{
"message": "Software created successfully",
"software": {
"id": 1,
"uuid": "550e8400-e29b-41d4-a716-446655440001",
"name": "YouTube CPM GoLogin Automation Software",
"description": "Professional automation software for YouTube CPM optimization",
"version": "1.0.0",
"api_key": "sk_1234567890abcdef1234567890abcdef1234567890abcdef",
"status": "active",
"license_validation_url": null,
"webhook_url": null,
"max_devices_per_license": 3,
"allow_offline_validation": true,
"require_hardware_fingerprint": true,
"fraud_detection_enabled": true,
"created_at": "2025-07-10T10:30:00Z",
"updated_at": "2025-07-10T10:30:00Z",
"active_licenses_count": 0,
"total_licenses_count": 0,
"recent_validations_count": 0,
"security_events_count": 0,
"fraud_detection_stats": {
"total_attempts": 0,
"blocked_attempts": 0,
"fraud_score_avg": 0.0
}
}
}
List Software
GET
/api/software
• Requires Authentication (Admin/Manager)
Query Parameters
page(int): Page number (default: 1)per_page(int): Items per page (default: 20, max: 100)status(string): Filter by status (active, inactive, maintenance)search(string): Search by name, description, or versionlicense_count_min(int): Filter by minimum license countlicense_count_max(int): Filter by maximum license countsort_by(string): Sort field (name, version, status, created_at)sort_order(string): Sort order (asc, desc)
{
"software": [
{
"id": 1,
"uuid": "550e8400-e29b-41d4-a716-446655440001",
"name": "YouTube CPM GoLogin Automation Software",
"description": "Professional automation software for YouTube CPM optimization",
"version": "1.0.1",
"api_key": "sk_1234567890abcdef1234567890abcdef1234567890abcdef",
"status": "active",
"license_validation_url": null,
"webhook_url": null,
"max_devices_per_license": 3,
"allow_offline_validation": true,
"require_hardware_fingerprint": true,
"fraud_detection_enabled": true,
"created_at": "2025-07-01T10:00:00Z",
"updated_at": "2025-07-10T15:30:00Z",
"active_licenses_count": 2,
"total_licenses_count": 4,
"recent_validations_count": 15,
"security_events_count": 0,
"fraud_detection_stats": {
"total_attempts": 25,
"blocked_attempts": 0,
"fraud_score_avg": 0.05
}
}
],
"pagination": {
"current_page": 1,
"total_pages": 1,
"total_items": 1,
"items_per_page": 20,
"start_item": 1,
"end_item": 1,
"has_prev": false,
"has_next": false
}
}
Update Software
PUT
/api/software/{software_id}
• Requires Authentication (Admin/Manager)
Updatable Fields
name(string): Software nameversion(string): Software versiondescription(string): Software descriptionstatus(string): Software status (active, inactive, maintenance)
{
"name": "YouTube CPM GoLogin Automation Software Pro",
"description": "Enhanced professional automation software for YouTube CPM optimization",
"version": "1.1.0",
"status": "active"
}
{
"message": "Software updated successfully",
"software": {
"id": 1,
"uuid": "550e8400-e29b-41d4-a716-446655440001",
"name": "YouTube CPM GoLogin Automation Software Pro",
"description": "Enhanced professional automation software for YouTube CPM optimization",
"version": "1.1.0",
"api_key": "sk_1234567890abcdef1234567890abcdef1234567890abcdef",
"status": "active",
"license_validation_url": null,
"webhook_url": null,
"max_devices_per_license": 3,
"allow_offline_validation": true,
"require_hardware_fingerprint": true,
"fraud_detection_enabled": true,
"created_at": "2025-07-10T10:30:00Z",
"updated_at": "2025-07-10T15:45:00Z",
"active_licenses_count": 2,
"total_licenses_count": 4,
"recent_validations_count": 15,
"security_events_count": 0,
"fraud_detection_stats": {
"total_attempts": 25,
"blocked_attempts": 0,
"fraud_score_avg": 0.05
}
}
}
Get Software by ID
GET
/api/software/{software_id}
• Requires Authentication
{
"software": {
"id": 1,
"uuid": "550e8400-e29b-41d4-a716-446655440001",
"name": "YouTube CPM GoLogin Automation Software",
"description": "Professional automation software for YouTube CPM optimization",
"version": "1.0.1",
"api_key": "sk_1234567890abcdef1234567890abcdef1234567890abcdef",
"status": "active",
"license_validation_url": null,
"webhook_url": null,
"max_devices_per_license": 3,
"allow_offline_validation": true,
"require_hardware_fingerprint": true,
"fraud_detection_enabled": true,
"created_at": "2025-07-01T10:00:00Z",
"updated_at": "2025-07-10T15:30:00Z",
"active_licenses_count": 2,
"total_licenses_count": 4,
"recent_validations_count": 15,
"security_events_count": 0,
"fraud_detection_stats": {
"total_attempts": 25,
"blocked_attempts": 0,
"fraud_score_avg": 0.05
}
}
}
Delete Software
DELETE
/api/software/{software_id}
• Requires Authentication (Admin only)
{
"message": "Software deleted successfully"
}
{
"message": "Cannot delete software with 5 active licenses"
}
Bulk Software Operations
POST
/api/software/bulk
• Requires Authentication (Admin/Manager)
Supported Operations
delete: Delete multiple software (Admin only)update_status: Update status of multiple software
{
"operation": "delete",
"software_ids": [1, 2, 3]
}
{
"operation": "update_status",
"software_ids": [1, 2, 3],
"status": "maintenance"
}
{
"message": "Bulk operation completed: 2 successful, 1 failed",
"results": {
"success": [
{
"id": 1,
"name": "Software A",
"action": "deleted"
},
{
"id": 2,
"name": "Software B",
"action": "deleted"
}
],
"failed": [
{
"id": 3,
"name": "Software C",
"error": "Cannot delete software with 5 active licenses"
}
]
}
}
Software Statistics
GET
/api/software/statistics
• Requires Authentication (Admin/Manager)
Get comprehensive software statistics for dashboard overview cards.
{
"total_software": 15,
"active_software": 12,
"inactive_software": 2,
"maintenance_software": 1,
"total_licenses": 450,
"total_revenue": 44999.50,
"avg_licenses_per_software": 30.0,
"top_performing_software": {
"id": 1,
"name": "YouTube CPM GoLogin Automation Software",
"license_count": 125,
"revenue": 12499.75
}
}
Bulk Operations API
Perform batch operations on multiple entities for efficient management.
⚡ Key Features
- Batch Processing: Operate on multiple entities simultaneously
- Atomic Operations: All operations succeed or fail together
- Detailed Results: Get success/failure status for each item
- Permission Control: Role-based access for different operations
Bulk License Operations
POST
/api/licenses/bulk
• Requires Authentication (Admin/Manager)
Supported Operations
delete: Delete multiple licenses (Admin only)update_status: Update status of multiple licensesupdate_type: Update type of multiple licensesupdate_expiration: Update expiration date of multiple licenses
{
"operation": "update_status",
"license_ids": [1, 2, 3, 4],
"status": "suspended"
}
{
"operation": "update_expiration",
"license_ids": [1, 2, 3],
"expires_at": "2024-12-31T23:59:59Z"
}
{
"message": "Bulk operation completed: 3 successful, 1 failed",
"results": {
"success": [
{"id": 1, "license_key": "ABCD-EFGH-IJKL-MNOP"},
{"id": 2, "license_key": "WXYZ-1234-5678-9012"},
{"id": 3, "license_key": "QRST-UVWX-YZAB-CDEF"}
],
"failed": [
{"id": 4, "error": "License not found"}
]
}
}
Bulk User Operations
POST
/api/users/bulk
• Requires Authentication (Admin/Manager)
Supported Operations
delete: Delete multiple users (Admin only)update_status: Update status of multiple usersupdate_role: Update role of multiple users
{
"operation": "update_role",
"user_ids": [2, 3, 4],
"role": "manager"
}
{
"message": "Bulk operation completed: 2 successful, 1 failed",
"results": {
"success": [
{"id": 2, "email": "user2@example.com"},
{"id": 3, "email": "user3@example.com"}
],
"failed": [
{"id": 4, "error": "Insufficient permissions to modify admin users"}
]
}
}
Bulk Software Operations
POST
/api/software/bulk
• Requires Authentication (Admin/Manager)
Supported Operations
delete: Delete multiple software (Admin only)update_status: Update status of multiple software
{
"operation": "update_status",
"software_ids": [1, 2, 3],
"status": "maintenance"
}
{
"message": "Bulk operation completed: 3 successful, 0 failed",
"results": {
"success": [
{"id": 1, "name": "Software A"},
{"id": 2, "name": "Software B"},
{"id": 3, "name": "Software C"}
],
"failed": []
}
}
Version Control API
Manage software versions, changelogs, and version-specific license assignments.
🔄 Key Features
- Semantic Versioning: Track MAJOR.MINOR.PATCH versions
- Changelog Management: Document changes with categorization
- Version Status: Control release status (draft, beta, stable, etc.)
- Version-Specific Licensing: Assign licenses to specific versions
Get Software Versions
GET
/api/versions/software/{software_id}/versions
• Requires Authentication
Retrieve all versions for a specific software with optional filtering.
include_changelog=true # Include changelog entries with versions
status=stable # Filter by release status (draft, beta, stable, etc.)
release_type=feature # Filter by release type (feature, bugfix, security, etc.)
{
"success": true,
"versions": [
{
"id": 1,
"software_id": 1,
"version_number": "1.0.0",
"version_name": "Initial Release",
"release_type": "stable",
"release_status": "published",
"release_notes": "First stable release",
"breaking_changes": false,
"is_latest": true,
"download_url": "https://example.com/download/v1.0.0",
"created_at": "2025-07-01T12:00:00Z",
"published_at": "2025-07-05T10:30:00Z",
"changelog_entries": [
{
"id": 1,
"title": "Initial feature set",
"change_type": "feature",
"description": "Complete initial feature implementation"
}
]
}
],
"total": 1
}
Create Software Version
POST
/api/versions/software/{software_id}/versions
• Requires Authentication (Admin, Manager)
{
"version_number": "1.1.0",
"version_name": "Feature Update",
"release_type": "feature",
"release_notes": "Added new dashboard features",
"breaking_changes": false,
"is_lts": false
}
{
"message": "Version created successfully",
"success": true,
"version": {
"id": 2,
"software_id": 1,
"version_number": "1.1.0",
"version_name": "Feature Update",
"release_type": "feature",
"release_status": "draft",
"created_at": "2025-07-10T14:30:00Z"
}
}
Get Version Details
GET
/api/versions/{version_id}
• Requires Authentication
{
"success": true,
"version": {
"id": 1,
"software_id": 1,
"version_number": "1.0.0",
"version_name": "Initial Release",
"release_type": "stable",
"release_status": "published",
"release_notes": "First stable release",
"breaking_changes": false,
"is_latest": true,
"download_url": "https://example.com/download/v1.0.0",
"file_size": 15728640,
"file_checksum": "a1b2c3d4e5f6...",
"created_at": "2025-07-01T12:00:00Z",
"published_at": "2025-07-05T10:30:00Z",
"created_by": 1,
"published_by": 1,
"changelog_entries": [
{
"id": 1,
"title": "Initial feature set",
"change_type": "feature",
"description": "Complete initial feature implementation",
"technical_details": "Implemented core modules X, Y, and Z",
"category": "core",
"priority": "high",
"affects_api": true,
"affects_ui": true,
"affects_database": false,
"requires_migration": false,
"created_at": "2025-07-01T14:20:00Z"
}
]
}
}
Update Version
PUT
/api/versions/{version_id}
• Requires Authentication (Admin, Manager)
{
"version_name": "Updated Release Name",
"release_notes": "Updated release notes with more details",
"breaking_changes": true,
"migration_guide": "Follow these steps to migrate from v0.9.x...",
"download_url": "https://example.com/download/v1.0.0-updated",
"file_size": 16777216,
"file_checksum": "updated-checksum-hash"
}
Publish Version
POST
/api/versions/{version_id}/publish
• Requires Authentication (Admin, Manager)
{
"set_as_latest": true
}
{
"success": true,
"message": "Version published successfully",
"version": {
"id": 2,
"version_number": "1.1.0",
"release_status": "published",
"is_latest": true,
"published_at": "2025-07-15T09:45:00Z"
}
}
Set Latest Version
POST
/api/versions/{version_id}/set-latest
• Requires Authentication (Admin, Manager)
{
"success": true,
"message": "Version set as latest successfully"
}
Get Version Changelog
GET
/api/versions/{version_id}/changelog
• Requires Authentication
change_type=feature # Filter by change type (feature, bugfix, security, etc.)
category=ui # Filter by category (ui, api, core, etc.)
{
"success": true,
"entries": [
{
"id": 1,
"version_id": 1,
"title": "New dashboard UI",
"change_type": "feature",
"description": "Completely redesigned dashboard with improved UX",
"category": "ui",
"priority": "high",
"affects_ui": true,
"created_at": "2025-07-01T14:20:00Z"
},
{
"id": 2,
"version_id": 1,
"title": "Performance improvements",
"change_type": "enhancement",
"description": "Optimized dashboard loading speed by 50%",
"category": "ui",
"priority": "medium",
"affects_ui": true,
"created_at": "2025-07-01T15:30:00Z"
}
],
"total": 2
}
Add Changelog Entry
POST
/api/versions/{version_id}/changelog
• Requires Authentication (Admin, Manager)
{
"change_type": "bugfix",
"title": "Fixed login issue",
"description": "Resolved authentication failure on certain browsers",
"technical_details": "Fixed CORS headers and cookie handling",
"category": "security",
"priority": "critical",
"affects_api": true,
"affects_ui": false,
"issue_number": "ISSUE-123",
"pull_request_number": "PR-456"
}
{
"success": true,
"message": "Changelog entry added successfully",
"entry": {
"id": 3,
"version_id": 1,
"title": "Fixed login issue",
"change_type": "bugfix",
"category": "security",
"priority": "critical",
"created_at": "2025-07-15T11:20:00Z"
}
}
Update Changelog Entry
PUT
/api/changelog/{entry_id}
• Requires Authentication (Admin, Manager)
{
"title": "Updated entry title",
"description": "Updated description with more details",
"priority": "high",
"affects_database": true,
"requires_migration": true
}
Delete Changelog Entry
DELETE
/api/changelog/{entry_id}
• Requires Authentication (Admin, Manager)
{
"success": true,
"message": "Changelog entry deleted successfully"
}
Webhooks
Receive real-time notifications about license events in your application.
🔔 Webhook Events
- license.created: New license created
- license.activated: License activated on a device
- license.deactivated: License deactivated from a device
- license.expired: License has expired
- license.suspended: License suspended by admin
- license.extended: License expiration extended
- license.reduced: License expiration reduced
- fraud.detected: Suspicious activity detected
Webhook Configuration
Configure webhooks when creating or updating software:
{
"webhook_url": "https://yourapp.com/webhooks/license",
"webhook_secret": "your_webhook_secret_key",
"webhook_events": [
"license.created",
"license.activated",
"license.expired",
"fraud.detected"
]
}
Webhook Payload
{
"event": "license.activated",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"license": {
"id": 123,
"license_key": "ABCD-EFGH-IJKL-MNOP",
"type": "ultimate",
"status": "active",
"expires_at": "2024-12-31T23:59:59Z",
"user_id": 456
},
"device": {
"fingerprint_hash": "abc123def456",
"ip_address": "192.168.1.100",
"location": "New York, US"
},
"software": {
"id": 1,
"name": "My Application",
"version": "2.1.0"
}
},
"signature": "sha256=a1b2c3d4e5f6..."
}
Webhook Security
Verify webhook authenticity using HMAC-SHA256 signatures:
import hmac
import hashlib
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret_key"
@app.route('/webhooks/license', methods=['POST'])
def handle_webhook():
# Get the signature from headers
signature = request.headers.get('X-Signature-256')
if not signature:
abort(400, 'Missing signature')
# Calculate expected signature
payload = request.get_data()
expected_signature = 'sha256=' + hmac.new(
WEBHOOK_SECRET.encode(),
payload,
hashlib.sha256
).hexdigest()
# Verify signature
if not hmac.compare_digest(signature, expected_signature):
abort(401, 'Invalid signature')
# Process webhook
data = request.get_json()
event = data['event']
if event == 'license.activated':
handle_license_activation(data['data'])
elif event == 'license.expired':
handle_license_expiration(data['data'])
elif event == 'fraud.detected':
handle_fraud_detection(data['data'])
return {'status': 'success'}
def handle_license_activation(data):
license_key = data['license']['license_key']
print(f"License {license_key} activated!")
def handle_license_expiration(data):
license_key = data['license']['license_key']
print(f"License {license_key} expired!")
def handle_fraud_detection(data):
license_key = data['license']['license_key']
print(f"Fraud detected for license {license_key}!")
const express = require('express');
const crypto = require('crypto');
const app = express();
const WEBHOOK_SECRET = 'your_webhook_secret_key';
app.use(express.raw({ type: 'application/json' }));
app.post('/webhooks/license', (req, res) => {
const signature = req.headers['x-signature-256'];
if (!signature) {
return res.status(400).send('Missing signature');
}
// Calculate expected signature
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
// Verify signature
if (!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
)) {
return res.status(401).send('Invalid signature');
}
// Process webhook
const data = JSON.parse(req.body);
const event = data.event;
switch (event) {
case 'license.activated':
handleLicenseActivation(data.data);
break;
case 'license.expired':
handleLicenseExpiration(data.data);
break;
case 'fraud.detected':
handleFraudDetection(data.data);
break;
}
res.json({ status: 'success' });
});
function handleLicenseActivation(data) {
const licenseKey = data.license.license_key;
console.log(`License ${licenseKey} activated!`);
}
function handleLicenseExpiration(data) {
const licenseKey = data.license.license_key;
console.log(`License ${licenseKey} expired!`);
}
function handleFraudDetection(data) {
const licenseKey = data.license.license_key;
console.log(`Fraud detected for license ${licenseKey}!`);
}
'success']);
function handleLicenseActivation($data) {
$licenseKey = $data['license']['license_key'];
error_log("License {$licenseKey} activated!");
}
function handleLicenseExpiration($data) {
$licenseKey = $data['license']['license_key'];
error_log("License {$licenseKey} expired!");
}
function handleFraudDetection($data) {
$licenseKey = $data['license']['license_key'];
error_log("Fraud detected for license {$licenseKey}!");
}
?>
Dashboard API
Comprehensive analytics and statistics endpoints for administrative dashboards and reporting.
📊 Key Features
- Time-Range Filtering: Get statistics for specific time periods
- Role-Based Access: Different data visibility based on user roles
- Real-time Analytics: Live dashboard statistics and metrics
- Revenue Tracking: Financial analytics and revenue calculations
Dashboard Statistics
GET
/api/dashboard/stats
• Requires Authentication
Get comprehensive dashboard statistics with optional time-range filtering.
Query Parameters
range(string): Time range filter (24h, 7d, 30d, 90d, 1y)
{
"users": {
"total": 150,
"active": 142,
"new_registrations": 12
},
"licenses": {
"total": 450,
"active": 380,
"expired": 45,
"revenue": 37999.20
},
"software": {
"total": 15,
"active": 12,
"total_licenses": 450,
"avg_licenses_per_software": 30.0
},
"time_range": "30d",
"generated_at": "2025-07-10T15:30:00Z"
}
Advanced Analytics
GET
/api/dashboard/analytics
• Requires Authentication (Admin/Manager)
Get detailed analytics data including trends, performance metrics, and security insights.
Query Parameters
range(string): Time range filter (24h, 7d, 30d, 90d, 1y)include_trends(bool): Include trend analysis (default: true)include_security(bool): Include security metrics (default: true)
{
"overview": {
"total_revenue": 37999.20,
"revenue_growth": 15.3,
"license_activation_rate": 89.2,
"user_retention_rate": 94.1
},
"trends": {
"daily_registrations": [2, 3, 1, 4, 2, 5, 3],
"daily_revenue": [299.99, 199.98, 99.99, 399.96, 199.98, 499.95, 299.99],
"license_activations": [5, 7, 3, 8, 4, 9, 6]
},
"security": {
"total_events": 1250,
"high_risk_events": 23,
"blocked_attempts": 45,
"fraud_score_avg": 0.125
},
"top_performing": {
"software": {
"id": 1,
"name": "YouTube CPM GoLogin Automation Software",
"license_count": 125,
"revenue": 12499.75
},
"user": {
"id": 15,
"name": "John Smith",
"license_count": 8,
"total_spent": 799.92
}
},
"time_range": "30d",
"generated_at": "2025-07-10T15:30:00Z"
}
Security API
Security event tracking, fraud detection statistics, and security monitoring endpoints.
🔒 Key Features
- Event Tracking: Monitor security events and suspicious activities
- Fraud Detection: ML-powered fraud scoring and risk assessment
- Real-time Monitoring: Live security statistics and alerts
- Risk Analysis: Comprehensive security metrics and trends
Security Statistics
GET
/api/security/stats
• Requires Authentication (Admin/Manager)
Get comprehensive security statistics including event counts, fraud metrics, and risk analysis.
Query Parameters
hours(int): Time range in hours (default: 24, max: 8760)
{
"total_events": 1250,
"high_risk_events": 23,
"blocked_attempts": 45,
"fraud_score_avg": 0.125,
"time_range_hours": 24,
"event_breakdown": {
"license_validation": 980,
"failed_authentication": 45,
"suspicious_activity": 23,
"fraud_detection": 12,
"hardware_mismatch": 8,
"geographic_anomaly": 5
},
"risk_levels": {
"low": 1180,
"medium": 47,
"high": 23,
"critical": 0
},
"generated_at": "2025-07-10T15:30:00Z"
}
Statistics API
Comprehensive statistics and analytics endpoints for dashboard cards and reporting.
📊 Key Features
- Real-time Data: Live statistics updated in real-time
- Role-based Access: Different data visibility based on user roles
- Comprehensive Metrics: Detailed breakdowns and analytics
- Dashboard Integration: Optimized for dashboard card displays
License Statistics
GET
/api/licenses/stats
• Requires Authentication
Get license statistics for dashboard overview cards. Regular users see only their own license statistics.
{
"total_licenses": 450,
"active_licenses": 380,
"expired_licenses": 45,
"total_revenue": 37999.20
}
User Statistics
GET
/api/users/stats
• Requires Authentication (Admin/Manager)
Get user statistics for dashboard overview cards.
{
"total_users": 125,
"active_users": 118,
"new_users_this_month": 12
}
Dashboard Statistics
GET
/api/dashboard/stats
• Requires Authentication
Query Parameters
range(string): Time range (24h, 7d, 30d, 12m) - default: 30d
GET /api/dashboard/stats?range=30d
Authorization: Bearer <access_token>
{
"revenue": {
"total": 45999.50,
"monthly": 3850.75,
"growth": 12.5
},
"licenses": {
"total": 450,
"active": 380,
"expired": 45,
"trial": 25
},
"users": {
"total": 125,
"active": 118,
"new_this_month": 12
},
"software": {
"total": 8,
"active": 7
},
"validations": {
"total": 15420,
"successful": 14890,
"failed": 530,
"success_rate": 96.6
},
"time_range": "30d",
"generated_at": "2025-07-10T15:30:00Z"
}
Dashboard Analytics
GET
/api/dashboard/analytics
• Requires Authentication (Admin/Manager)
Get detailed analytics data including top software performance and trends.
{
"top_software": [
{
"name": "YouTube CPM GoLogin Automation Software",
"licenses": 125,
"revenue": 12499.75
},
{
"name": "Social Media Manager Pro",
"licenses": 89,
"revenue": 8899.11
}
],
"license_trends": {
"daily_activations": [5, 8, 12, 6, 9, 15, 11],
"daily_expirations": [2, 1, 3, 4, 2, 1, 2]
},
"validation_trends": {
"hourly_validations": [45, 52, 38, 67, 89, 76, 54],
"success_rates": [98.2, 97.8, 99.1, 96.5, 98.7, 97.9, 98.4]
},
"generated_at": "2025-07-10T15:30:00Z"
}
Announcements API
Manage in-app announcements to communicate with your users — push updates, warnings, and critical notices directly to client applications.
📢 Key Features
- Public Fetch Endpoint: Client apps can fetch active announcements without authentication
- Delivery & Read Tracking: When clients fetch announcements with their
device_id, each announcement returned is recorded as delivered; when a client reports a view it's marked read. The admin panel shows Delivered / Read / Unread counts and the per-user list with status. - CRUD Operations: Full create, read, update, delete for admin users
- Typed Announcements: Categorize as
info,warning,update, orcritical - Active/Inactive Toggle: Draft announcements and publish when ready
- Paginated Listing: Server-side pagination, search, and type filtering for admin panel
- App-Scoped: Each announcement belongs to a specific application
📋 Announcement Object
{
"id": 1,
"app_id": 3,
"title": "v2.5.0 Released — Performance Boost",
"message": "We've improved rendering speed by 40%. Update now to get the latest features.",
"type": "update",
"icon": "ph-megaphone",
"is_active": 1,
"created_at": "2025-07-10 14:30:00",
"updated_at": "2025-07-10 14:30:00"
}
| Field | Type | Description |
|---|---|---|
id | int | Unique announcement ID |
app_id | int | Owning application ID |
title | string | Announcement headline |
message | string | Full announcement body (supports HTML) |
type | enum | One of: info, warning, update, critical |
icon | string | Phosphor Icons class name (e.g. ph-megaphone) |
is_active | int (0|1) | Whether the announcement is visible to clients |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
view_count | int | Devices that reported reading this announcement. Admin list responses only. |
delivered_count | int | Devices this announcement was delivered to (fetched with device_id). Unread = delivered_count − view_count. Admin list responses only. |
Fetch Active Announcements (Public)
GET
/api.php?endpoint=announcements&app_unique_id={app_unique_id}
• No Authentication Required
Client applications call this endpoint to retrieve all active (published) announcements. The app is identified by either app_unique_id or app_name. Only announcements where is_active = 1 are returned.
Delivery tracking: if device_id is provided, every announcement returned in the response is recorded as delivered to that device (repeat fetches are deduplicated server-side and never overwrite read timestamps). Passing device_id also excludes announcements this device has already dismissed. Clients should always send their device_id here so the admin panel can show Delivered / Read / Unread statistics.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
endpoint | string | Yes | Must be announcements |
app_unique_id | string | Yes* | App's unique identifier (e.g. 3181d3032534bcb13e2411b321dc17e0) |
app_name | string | Yes* | App name (alternative to app_unique_id) |
device_id | string | No | Device identifier — records deliveries, excludes dismissed announcements |
* Provide either app_unique_id or app_name — at least one is required.
GET /api.php?endpoint=announcements&app_unique_id=3181d3032534bcb13e2411b321dc17e0
{
"success": true,
"announcements": [
{
"id": 1,
"title": "v2.5.0 Released — Performance Boost",
"message": "We've improved rendering speed by 40%...",
"type": "update",
"icon": "ph-megaphone",
"created_at": "2025-07-10 14:30:00",
"updated_at": "2025-07-10 14:30:00"
},
{
"id": 2,
"title": "Scheduled Maintenance — July 15",
"message": "Services will be briefly unavailable...",
"type": "warning",
"icon": "ph-warning",
"created_at": "2025-07-09 10:00:00",
"updated_at": "2025-07-09 10:00:00"
}
]
}
{
"error": "App not found or inactive"
}
Record Announcement View (Public)
POST
/api.php?endpoint=announcements/view&app_unique_id={app_unique_id}
• Requires X-API-Key
Client applications call this endpoint when an announcement is displayed to the user to record a read receipt. The server deduplicates on (announcement_id, device_id), so calling it repeatedly is safe — only the first read per device is stored. Reporting a view also marks the announcement as delivered for that device (even if the delivery wasn't recorded at fetch time). The admin panel uses these records to show Delivered / Read / Unread counts and the per-user list with status.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
endpoint | string | Yes | Must be announcements/view |
app_unique_id | string | Yes* | App's unique identifier |
app_name | string | Yes* | App name (alternative to app_unique_id) |
* Provide either app_unique_id or app_name — at least one is required. The request must also send a valid X-API-Key header (the same key used for other client calls).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
device_id | string | Yes | The device identifier the client uses for activation |
announcement_id | int | Yes | ID of the announcement being displayed |
POST /api.php?endpoint=announcements/view&app_unique_id=3181d3032534bcb13e2411b321dc17e0
X-API-Key: {your_api_key}
Content-Type: application/json
{
"device_id": "0ba8dd16f3d7ec39a1e33721be7131dc53ae8c8dabdd690871979e0a920a95c7",
"announcement_id": 1
}
{
"success": true,
"message": "View recorded."
}
Response Fields
| Field | Type | Description |
|---|---|---|
success | bool | Whether the view was recorded (or already existed — duplicates succeed silently) |
message | string | Human-readable confirmation |
Errors: 400 if device_id or announcement_id is missing, 404 if the announcement doesn't exist or is inactive, 403 for a missing/invalid API key.
List All Announcements (Admin)
GET
/api.php?endpoint=announcements
• Requires Authentication
Retrieve a paginated list of all announcements for the currently selected app. Supports search and filtering.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number |
per_page | int | 20 | Results per page |
search | string | — | Search in title and message |
type | string | all | Filter by type: info, warning, update, critical |
is_active | int (0|1) | — | Filter by active status |
GET /api.php?endpoint=announcements&page=1&per_page=10&search=maintenance&type=warning
Authorization: Bearer
{
"data": [
{
"id": 2,
"app_id": 3,
"title": "Scheduled Maintenance — July 15",
"message": "Services will be briefly unavailable...",
"type": "warning",
"icon": "ph-warning",
"is_active": 1,
"created_at": "2025-07-09 10:00:00",
"updated_at": "2025-07-09 10:00:00",
"view_count": 7,
"delivered_count": 12
}
],
"total": 1,
"page": 1,
"per_page": 10
}
Get Single Announcement (Admin)
GET
/api.php?endpoint=announcements/{id}
• Requires Authentication
Retrieve a single announcement by its ID.
GET /api.php?endpoint=announcements/2
Authorization: Bearer
{
"id": 2,
"app_id": 3,
"title": "Scheduled Maintenance — July 15",
"message": "Services will be briefly unavailable...",
"type": "warning",
"icon": "ph-warning",
"is_active": 1,
"created_at": "2025-07-09 10:00:00",
"updated_at": "2025-07-09 10:00:00"
}
Get Announcement Readers (Admin)
GET
/api.php?endpoint=announcements/{id}&action=readers
• Requires Authentication
Retrieve the delivery/read status list for an announcement — used by the admin panel's Who Read This modal. Every device that received the announcement is listed with a Read or Unread status; rows where viewed_at is null were delivered but not read yet. Each row is resolved to the license holder's email via the device that reported the view. Entries from unrecognized devices are still included (with user_email as null) so nothing is silently dropped.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
endpoint | string | Yes | Must be announcements/{id} |
action | string | Yes | Must be readers |
GET /api.php?endpoint=announcements/2&action=readers
Authorization: Bearer
{
"success": true,
"total": 2,
"readers": [
{
"device_id": "0ba8dd16f3d7ec39a1e33721be7131dc53ae8c8dabdd690871979e0a920a95c7",
"user_email": "adminuserstrong@gmail.com",
"device_name": "TINY-FC60EC5F",
"operating_system": "Windows 11 Pro",
"delivered_at": "2025-07-10 14:30:05",
"viewed_at": "2025-07-10 14:35:22"
},
{
"device_id": "legacy-device-001",
"user_email": "user@example.com",
"device_name": "rdp-1",
"operating_system": "Windows 10",
"delivered_at": "2025-07-10 15:00:41",
"viewed_at": null
}
]
}
Reader Fields
| Field | Type | Description |
|---|---|---|
device_id | string | Device that reported the read |
user_email | string|null | License holder's email; null if the device can't be matched to a license |
device_name | string|null | Human-readable device name, if known |
operating_system | string|null | OS reported at activation, if known |
delivered_at | datetime | When the announcement was delivered to this device (first fetch that included it) |
viewed_at | datetime|null | When the device reported reading it; null means delivered but unread |
Errors: 404 if the announcement doesn't exist or belongs to a different app.
Create Announcement (Admin)
POST
/api.php?endpoint=announcements
• Requires Authentication
Create a new announcement for the currently selected application.
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | — | Announcement headline |
message | string | Yes | — | Full announcement body |
type | string | No | info | One of: info, warning, update, critical |
icon | string | No | ph-megaphone | Phosphor Icons class name |
is_active | int (0|1) | No | 1 | Set to 0 to create as draft |
POST /api.php?endpoint=announcements
Content-Type: application/json
Authorization: Bearer
{
"title": "v2.5.0 Released — Performance Boost",
"message": "We've improved rendering speed by 40%. Update now to get the latest features.",
"type": "update",
"icon": "ph-rocket-launch",
"is_active": 1
}
{
"success": true,
"id": 3,
"message": "Announcement created successfully."
}
{
"error": "Title and message are required"
}
Update Announcement (Admin)
PUT
/api.php?endpoint=announcements/{id}
• Requires Authentication
Update an existing announcement. Only announcements belonging to the currently selected app can be updated.
PUT /api.php?endpoint=announcements/3
Content-Type: application/json
Authorization: Bearer
{
"title": "v2.5.1 Hotfix Released",
"message": "Fixed a critical rendering bug. Please update immediately.",
"type": "critical",
"icon": "ph-warning-circle",
"is_active": 1
}
{
"success": true,
"message": "Announcement updated successfully."
}
{
"error": "Announcement not found or access denied"
}
Delete Announcement (Admin)
DELETE
/api.php?endpoint=announcements/{id}
• Requires Authentication
Permanently delete an announcement. Only announcements belonging to the currently selected app can be deleted.
DELETE /api.php?endpoint=announcements/3
Authorization: Bearer
{
"success": true,
"message": "Announcement deleted."
}
{
"error": "Announcement not found or access denied"
}
Client Integration Example
import requests
APP_UNIQUE_ID = "3181d3032534bcb13e2411b321dc17e0"
API_KEY = "your_api_key"
BASE_URL = "http://localhost/licensemanager"
def fetch_announcements(device_id):
"""Fetch active announcements from the license server.
Pass device_id so deliveries are tracked and dismissed ones are excluded."""
try:
resp = requests.get(
f"{BASE_URL}/api.php",
params={
"endpoint": "announcements",
"app_unique_id": APP_UNIQUE_ID,
"device_id": device_id,
},
timeout=10,
)
data = resp.json()
if data.get("success"):
return data.get("announcements", [])
return []
except Exception as e:
print(f"Failed to fetch announcements: {e}")
return []
def report_view(device_id, announcement_id):
"""Report that the user saw this announcement (read receipt).
Duplicates are ignored server-side — call it every time you show one."""
try:
requests.post(
f"{BASE_URL}/api.php",
params={
"endpoint": "announcements/view",
"app_unique_id": APP_UNIQUE_ID,
},
headers={"X-API-Key": API_KEY},
json={"device_id": device_id, "announcement_id": announcement_id},
timeout=10,
)
except Exception as e:
print(f"Failed to report view: {e}")
# Usage
DEVICE_ID = "your_device_id_here"
announcements = fetch_announcements(DEVICE_ID)
for ann in announcements:
print(f"[{ann['type'].upper()}] {ann['title']}")
print(f" {ann['message'][:80]}...")
print(f" Posted: {ann['created_at']}")
# Call this when the announcement UI is actually shown to the user,
# so the admin panel's "Read by N" counter reflects real reads.
report_view(DEVICE_ID, ann["id"])
print()
const APP_UNIQUE_ID = "3181d3032534bcb13e2411b321dc17e0";
const API_KEY = "your_api_key";
const BASE_URL = "http://localhost/licensemanager";
const DEVICE_ID = "your_device_id_here";
async function fetchAnnouncements(deviceId) {
// Pass device_id so deliveries are tracked and dismissed ones are excluded.
try {
const url = `${BASE_URL}/api.php?endpoint=announcements&app_unique_id=${APP_UNIQUE_ID}&device_id=${deviceId}`;
const resp = await fetch(url);
const data = await resp.json();
if (data.success) {
return data.announcements || [];
}
return [];
} catch (err) {
console.error("Failed to fetch announcements:", err);
return [];
}
}
// Report that the user saw this announcement (read receipt).
// Duplicates are ignored server-side — call it every time you show one.
async function reportView(announcementId) {
try {
const url = `${BASE_URL}/api.php?endpoint=announcements/view&app_unique_id=${APP_UNIQUE_ID}`;
await fetch(url, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ device_id: DEVICE_ID, announcement_id: announcementId }),
});
} catch (err) {
console.error("Failed to report view:", err);
}
}
// Usage
(async () => {
const announcements = await fetchAnnouncements(DEVICE_ID);
for (const ann of announcements) {
console.log(`[${ann.type.toUpperCase()}] ${ann.title}`);
console.log(` ${ann.message.substring(0, 80)}...`);
console.log(` Posted: ${ann.created_at}`);
// Call this when the announcement UI is actually shown to the user,
// so the admin panel's "Read by N" counter reflects real reads.
await reportView(ann.id);
}
})();
💡 Best Practices
- Polling interval: Client apps should poll every 30–60 minutes, not on every startup, to reduce server load.
- Cache locally: Store fetched announcements with a timestamp and only re-fetch after the polling interval elapses.
- Use types wisely: Reserve
criticalfor security alerts or mandatory updates. Useinfofor general news. - Draft first: Create announcements with
is_active: 0and toggle to1when ready to publish. - Keep it brief: Titles should be under 80 characters. Use the message body for details.
- Handle offline: If the fetch fails, show the last cached announcements rather than an empty state.
- Report reads & deliveries: Always send
device_idwhen fetching announcements (records deliveries, filters dismissed ones), and callannouncements/viewwhen an announcement is actually displayed to the user — together these power the admin panel's Delivered / Read / Unread statistics. Repeat calls are deduplicated server-side.
OTA Updates API
Deliver application updates over the air: push an installer from the admin panel, activate it, and every client app detects it through the public check-update endpoint, downloads it, verifies a SHA-256 checksum, and installs it.
🚀 Key Features
- Public Check Endpoint: Client apps poll
check-updatewith theirapp_unique_id— the sameX-API-Keygate as announcements - Forced Updates: Tick Force update when pushing and clients cannot skip the version — it downloads and installs automatically
- SHA-256 Checksums:
fileHashis computed on upload so clients can verify their download - Version History: The public
changelogendpoint returns every pushed version with its release notes for in-app "What's new" views - One Active Version: Activating a version automatically deactivates the previous one; clients always get the newest active update
Check for Update (Public)
GET
/api.php?endpoint=check-update&app_unique_id={app_unique_id}
• X-API-Key header required
Returns the newest active update for the app. Optional extra query parameters (current_version, device_id) are accepted for analytics and are ignored by older deployments.
{
"latestVersion": "1.2.1",
"releaseNotes": "[New] Force updates\n[Imp] faster startup\n[Fix] crash on export",
"fileSize": 125829120,
"fileHash": "9f2c6b...64-hex-sha256",
"releaseDate": "2026-08-22",
"downloadUrl": "https://licenses.example.com/lm/updates/Setup-1.2.1.zip",
"forceUpdate": true
}
Important: when no update is activated, the server replies with HTTP 404 and a JSON body — do not treat the status code as an error:
{ "error": "No active update is available." }
| Field | Type | Description |
|---|---|---|
latestVersion | string | Version of the active update (compare semantically: MAJOR.MINOR.PATCH) |
releaseNotes | string | Multi-line notes, one change per line using [New] / [Imp] / [Fix] tags |
fileSize | int | Installer size in bytes |
fileHash | string | SHA-256 hex of the file as served (64 chars); may be empty on very old uploads |
releaseDate | string | Release date (YYYY-MM-DD) |
downloadUrl | string | Full HTTPS URL of the installer. Note: uploaded .exe files are stored and served as .zip (host restriction) — the bytes are unchanged, so save and run them as the original installer |
forceUpdate | bool | true = forced update: clients must not offer skip/postpone; download and install automatically |
Fetch Changelog (Public)
GET
/api.php?endpoint=changelog&app_unique_id={app_unique_id}
• X-API-Key header required
Version history for in-app "What's new" views — every pushed version, newest first (up to 50), including versions that are no longer the active update. isActive marks the currently live one.
{
"success": true,
"versions": [
{
"version": "1.2.1",
"releaseNotes": "[New] Force updates\n[Fix] finisher hang",
"fileSize": 125829120,
"releaseDate": "2026-08-22",
"forceUpdate": true,
"isActive": true
},
{
"version": "1.2.0",
"releaseNotes": "[Imp] web GUI",
"fileSize": 119600000,
"releaseDate": "2026-08-20",
"forceUpdate": false,
"isActive": false
}
]
}
🛠️ Pushing an Update (Admin Panel)
- Open OTA Updates in the admin panel (per selected app)
- Push New Update: pick the installer file (
.exe,.zip,.msi,.apk… up to 1 GB) — the upload to the server starts immediately with a live progress bar while you fill in the rest of the form. Once it finishes, the uploaded file is shown in the modal with an Uploaded badge (you can still swap it via Change) - Set the version (must be higher than your clients' current version; the next patch version is suggested automatically), add release notes one per line with
[New]/[Imp]/[Fix]tags, optionally tick Force update — clients on older versions then cannot skip it - Push Update submits the metadata and links the already-uploaded file — no second upload, so the final step is instant. Closing the modal without pushing deletes the staged file again
- Activate the version — this is what makes it visible to
check-update(activating also deactivates the previous version)
Client responsibilities: compare latestVersion semantically against the running version, download downloadUrl over HTTPS, verify fileHash (SHA-256), then run the installer (NSIS-style installers accept /S for silent installation). Forced updates should download and install without offering a skip.
Stage an Update File (Admin)
POST
/api.php?endpoint=updates-upload
• Requires Admin Authentication (session cookie or X-Admin-Key) • multipart/form-data
Uploads the installer payload before the update row exists — the admin panel calls this the moment a file is picked so the user watches progress while filling in the form. The file lands in updates/ with the same validation as a direct push (extension whitelist, no hidden/dot files, 1 GB max; .exe is stored as .zip with bytes unchanged). No database row is created — the staged file only becomes an update when POST updates is called with uploaded_file.
POST /api.php?endpoint=updates-upload
Content-Type: multipart/form-data
update_file=@MyApp-1.2.2-Setup.exe
{
"success": true,
"file_name": "MyApp-1.2.2-Setup.zip",
"file_size": 125829120,
"message": "File uploaded. Fill in the details and push the update."
}
Persist the returned file_name and send it as uploaded_file in the subsequent POST updates metadata submission (all other fields — app_name, version, release_notes, developer_name, release_date, force_update — are identical to a direct push). Submitting the same staged file twice fails with HTTP 409.
Discard a Staged File (Admin)
DELETE
/api.php?endpoint=updates-upload&file={file_name}
• Requires Admin Authentication
Removes a staged file that was never pushed (the admin panel calls this when the push modal is cancelled or the file is swapped). Refuses with HTTP 409 if any updates row still references the file, and normalises the name with basename() so paths outside updates/ cannot be targeted.
{ "success": true, "message": "Staged file removed." }
Integration Guide
Step-by-step instructions for integrating license validation into your applications.
🚀 Integration Steps
- Obtain API Credentials: Create software entry and get API keys
- Implement Validation: Add license validation to your application
- Handle Responses: Process validation results and errors
- Add Security: Implement hardware fingerprinting and fraud detection
- Go Live: Deploy to production with monitoring
Step 1: Create Software Entry
POST /api/software
Authorization: Bearer
Content-Type: application/json
{
"name": "Your Application Name",
"description": "Your application description",
"version": "1.0.0",
"max_devices_per_license": 3,
"require_hardware_fingerprint": true,
"fraud_detection_enabled": true
}
Save the returned api_key securely. This key will be used for license validation requests.
Step 2: Implement Basic Validation
import requests
import json
def validate_license(license_key, software_api_key):
"""Validate a license key"""
url = "http://localhost:5000/api/validation/validate"
payload = {
"license_key": license_key,
"software_api_key": software_api_key
}
try:
response = requests.post(url, json=payload, timeout=30)
data = response.json()
if response.status_code == 200 and data.get('valid'):
return {
'valid': True,
'license': data['license'],
'expires_at': data['license']['expires_at']
}
else:
return {
'valid': False,
'error': data.get('message', 'Unknown error'),
'error_code': data.get('error_code')
}
except requests.exceptions.RequestException as e:
return {
'valid': False,
'error': f'Network error: {str(e)}',
'error_code': 'NETWORK_ERROR'
}
# Usage
result = validate_license("ABCD-EFGH-IJKL-MNOP", "sk_1234567890abcdef1234567890abcdef1234567890abcdef")
if result['valid']:
print("✅ License is valid!")
else:
print(f"❌ License validation failed: {result['error']}")
async function validateLicense(licenseKey, softwareApiKey) {
const url = 'http://localhost:5000/api/validation/validate';
const payload = {
license_key: licenseKey,
software_api_key: softwareApiKey
};
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const data = await response.json();
if (response.ok && data.valid) {
return {
valid: true,
license: data.license,
expiresAt: data.license.expires_at
};
} else {
return {
valid: false,
error: data.message || 'Unknown error',
errorCode: data.error_code
};
}
} catch (error) {
return {
valid: false,
error: `Network error: ${error.message}`,
errorCode: 'NETWORK_ERROR'
};
}
}
// Usage
const result = await validateLicense('ABCD-EFGH-IJKL-MNOP', 'sk_1234567890abcdef1234567890abcdef1234567890abcdef');
if (result.valid) {
console.log('✅ License is valid!');
} else {
console.log(`❌ License validation failed: ${result.error}`);
}
using System.Text.Json;
public class LicenseValidator
{
private readonly HttpClient _httpClient;
public LicenseValidator()
{
_httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
}
public async Task ValidateLicenseAsync(
string licenseKey,
string softwareApiKey)
{
var url = "http://localhost:5000/api/validation/validate";
var payload = new {
license_key = licenseKey,
software_api_key = softwareApiKey
};
try {
var response = await _httpClient.PostAsJsonAsync(url, payload);
var content = await response.Content.ReadAsStringAsync();
var data = JsonDocument.Parse(content);
if (response.IsSuccessStatusCode &&
data.RootElement.GetProperty("valid").GetBoolean()) {
return new LicenseValidationResult {
Valid = true,
ExpiresAt = data.RootElement
.GetProperty("license")
.GetProperty("expires_at")
.GetString()
};
} else {
return new LicenseValidationResult {
Valid = false,
Error = data.RootElement.GetProperty("message").GetString(),
ErrorCode = data.RootElement.TryGetProperty("error_code", out var code)
? code.GetString() : null
};
}
} catch (Exception ex) {
return new LicenseValidationResult {
Valid = false,
Error = $"Network error: {ex.Message}",
ErrorCode = "NETWORK_ERROR"
};
}
}
}
public class LicenseValidationResult
{
public bool Valid { get; set; }
public string ExpiresAt { get; set; }
public string Error { get; set; }
public string ErrorCode { get; set; }
}
Step 3: Handle Grace Periods
Implement grace periods for expired licenses to provide a better user experience:
function checkLicenseStatus(validationResponse) {
if (!validationResponse.valid) {
if (validationResponse.error_code === 'LICENSE_EXPIRED') {
const expiredDate = new Date(validationResponse.license.expires_at);
const gracePeriodDays = 7; // Configure as needed
const gracePeriodEnd = new Date(
expiredDate.getTime() + (gracePeriodDays * 24 * 60 * 60 * 1000)
);
if (new Date() <= gracePeriodEnd) {
// Allow limited functionality during grace period
return {
status: 'grace_period',
daysRemaining: Math.ceil(
(gracePeriodEnd - new Date()) / (24 * 60 * 60 * 1000)
),
allowedFeatures: ['basic'] // Limit features
};
} else {
return { status: 'expired', action: 'block_access' };
}
}
}
return { status: 'valid' };
}
Complete Code Examples
Production-ready code examples with error handling, caching, and retry logic.
Advanced Python Client
import requests
import hashlib
import platform
import uuid
import time
import json
from datetime import datetime, timedelta
from typing import Dict, Optional, Tuple
class LicenseClient:
def __init__(self, base_url: str, software_api_key: str):
self.base_url = base_url.rstrip('/')
self.software_api_key = software_api_key
self.session = requests.Session()
self.session.headers.update({
'Content-Type': 'application/json',
'X-Software-API-Key': software_api_key
})
def generate_hardware_fingerprint(self) -> Dict:
"""Generate hardware fingerprint for the current device"""
try:
import psutil
memory_info = f"{round(psutil.virtual_memory().total / (1024**3))}GB"
disk_info = f"{round(psutil.disk_usage('/').total / (1024**3))}GB"
except ImportError:
memory_info = "unknown"
disk_info = "unknown"
# Get MAC address
mac = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff)
for elements in range(0,2*6,2)][::-1])
return {
"cpu": platform.processor(),
"memory": memory_info,
"storage": disk_info,
"mac_address": mac,
"os": f"{platform.system()} {platform.release()}",
"python_version": platform.python_version(),
"hostname": platform.node()
}
def validate_license_with_retry(self, license_key: str,
max_retries: int = 3) -> Tuple[bool, Dict]:
"""Validate license with exponential backoff retry"""
for attempt in range(max_retries):
try:
is_valid, data = self.validate_license(license_key)
return is_valid, data
except requests.exceptions.RequestException as e:
if attempt == max_retries - 1:
return False, {"error": "network_error", "message": str(e)}
# Exponential backoff
delay = (2 ** attempt) + (time.time() % 1)
time.sleep(delay)
return False, {"error": "max_retries_exceeded"}
def validate_license(self, license_key: str) -> Tuple[bool, Dict]:
"""Validate a license key"""
payload = {
"license_key": license_key,
"software_api_key": self.software_api_key,
"hardware_fingerprint": self.generate_hardware_fingerprint()
}
response = self.session.post(
f"{self.base_url}/api/validation/validate",
json=payload,
timeout=30
)
data = response.json()
return response.status_code == 200 and data.get('valid', False), data
# Usage
client = LicenseClient("http://localhost:5000", "sk_1234567890abcdef1234567890abcdef1234567890abcdef")
is_valid, response = client.validate_license_with_retry("ABCD-EFGH-IJKL-MNOP")
if is_valid:
print("✅ License is valid!")
print(f"Expires: {response['license']['expires_at']}")
else:
print(f"❌ Validation failed: {response.get('message', 'Unknown error')}")
Error Handling
Comprehensive guide to handling API errors and implementing robust error recovery.
HTTP Status Codes
- 200 OK: Request successful
- 201 Created: Resource created successfully
- 400 Bad Request: Invalid request data
- 401 Unauthorized: Authentication required
- 403 Forbidden: Access denied or license invalid
- 404 Not Found: Resource not found
- 429 Too Many Requests: Rate limit exceeded
- 500 Internal Server Error: Server error
Error Response Format
{
"error": true,
"message": "Human-readable error message",
"error_code": "MACHINE_READABLE_CODE",
"details": {
"field": "Additional error details",
"validation_errors": ["List of validation issues"]
},
"timestamp": "2024-01-15T10:30:00Z",
"request_id": "req_1234567890abcdef"
}
Common Error Codes
License Validation Errors
LICENSE_NOT_FOUND- License key doesn't existLICENSE_EXPIRED- License has expiredLICENSE_SUSPENDED- License is suspendedLICENSE_REVOKED- License has been revokedMAX_ACTIVATIONS_EXCEEDED- Too many device activationsHARDWARE_MISMATCH- Device doesn't match fingerprintFRAUD_DETECTED- Suspicious activity detected
Authentication Errors
INVALID_CREDENTIALS- Wrong email/passwordTOKEN_EXPIRED- Access token has expiredTOKEN_INVALID- Malformed or invalid tokenACCOUNT_LOCKED- Account is locked due to failed attempts
Security Features
Advanced security features to protect your licenses and prevent fraud.
Hardware Fingerprinting
Hardware fingerprinting creates a unique identifier for each device to prevent license sharing.
Collected Data Points
- CPU model and specifications
- Memory configuration
- Storage devices and capacity
- MAC addresses
- Operating system details
- Screen resolution and display info
- Browser fingerprint (for web apps)
Fraud Detection
ML-powered fraud detection analyzes patterns to identify suspicious activity:
{
"fraud_risk": {
"score": 0.85,
"level": "high",
"factors": [
"rapid_device_switching",
"geographic_anomaly",
"vpn_detected",
"suspicious_timing"
],
"recommended_action": "require_additional_verification"
}
}
Rate Limiting
Default Limits
- Authentication: 5 requests per minute
- License Validation: 100 requests per minute
- Management APIs: 60 requests per minute
- Bulk Operations: 10 requests per minute
Rate limits are returned in response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642248000
Testing & Validation
Test your integration with our comprehensive testing tools and procedures.
Test License Keys
Use these test license keys for development and testing:
TEST-VALID-LICENSE-KEY- Always returns validTEST-EXPIRED-LICENSE- Always returns expiredTEST-SUSPENDED-KEY- Always returns suspendedTEST-FRAUD-DETECTED- Triggers fraud detectionTEST-MAX-ACTIVATIONS- Exceeds activation limit
Integration Testing
import unittest
from license_client import LicenseClient
class TestLicenseValidation(unittest.TestCase):
def setUp(self):
self.client = LicenseClient(
"http://localhost:5000",
"sk_test_1234567890abcdef"
)
def test_valid_license(self):
"""Test valid license validation"""
is_valid, response = self.client.validate_license("TEST-VALID-LICENSE-KEY")
self.assertTrue(is_valid)
self.assertIn('license', response)
self.assertEqual(response['license']['status'], 'active')
def test_expired_license(self):
"""Test expired license handling"""
is_valid, response = self.client.validate_license("TEST-EXPIRED-LICENSE")
self.assertFalse(is_valid)
self.assertEqual(response['error_code'], 'LICENSE_EXPIRED')
def test_fraud_detection(self):
"""Test fraud detection response"""
is_valid, response = self.client.validate_license("TEST-FRAUD-DETECTED")
self.assertFalse(is_valid)
self.assertIn('fraud_risk', response)
self.assertEqual(response['fraud_risk']['level'], 'high')
def test_network_error_handling(self):
"""Test network error handling"""
# Test with invalid URL
client = LicenseClient("http://invalid-url", "test_key")
is_valid, response = client.validate_license_with_retry("TEST-VALID-LICENSE-KEY")
self.assertFalse(is_valid)
self.assertIn('network_error', response['error'])
if __name__ == '__main__':
unittest.main()
Production Deployment
Best practices for deploying the License Management API in production environments.
Environment Configuration
# Database Configuration
DATABASE_URL=mysql://user:password@localhost/license_management
REDIS_URL=redis://localhost:6379/0
# Security
SECRET_KEY=your-super-secret-key-here
JWT_SECRET_KEY=your-jwt-secret-key
ENCRYPTION_KEY=your-32-byte-encryption-key
# API Configuration
API_BASE_URL=https://api.yourdomain.com
CORS_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
# Rate Limiting
RATE_LIMIT_STORAGE_URL=redis://localhost:6379/1
DEFAULT_RATE_LIMIT=100
# Monitoring
SENTRY_DSN=https://your-sentry-dsn
LOG_LEVEL=INFO
# Email Configuration
SMTP_SERVER=smtp.yourdomain.com
SMTP_PORT=587
SMTP_USERNAME=noreply@yourdomain.com
SMTP_PASSWORD=your-smtp-password
Docker Deployment
version: '3.8'
services:
api:
build: .
ports:
- "5000:5000"
environment:
- DATABASE_URL=mysql://user:password@db/license_management
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
restart: unless-stopped
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: license_management
MYSQL_USER: user
MYSQL_PASSWORD: password
volumes:
- mysql_data:/var/lib/mysql
restart: unless-stopped
redis:
image: redis:7-alpine
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- api
restart: unless-stopped
volumes:
mysql_data:
Monitoring & Alerts
Key Metrics to Monitor
- Response Time: API endpoint response times
- Error Rate: 4xx and 5xx error percentages
- Validation Rate: License validations per minute
- Fraud Detection: High-risk validation attempts
- Database Performance: Query execution times
- Cache Hit Rate: Redis cache performance
Recommended Alerts
- Error rate > 5% for 5 minutes
- Response time > 2 seconds for 3 minutes
- Database connection failures
- High fraud risk score patterns
- Unusual license validation patterns
Support & Resources
Get help and additional resources for your integration.
📞 Contact Information
- Technical Support: support@licensemanagement.com
- Sales Inquiries: sales@licensemanagement.com
- Emergency Support: +1-555-0123 (24/7)
- Documentation: docs.licensemanagement.com
🎯 Service Level Agreement
- API Uptime: 99.9% guaranteed
- Response Time: < 200ms average
- Support Response: < 4 hours business days
- Critical Issues: < 1 hour response time
🔄 Migration Assistance
Need help migrating from another license management system? Our team provides:
- Data migration scripts and tools
- Custom integration development
- Dedicated migration support engineer
- Testing and validation assistance