Activation API
The Activation API handles license activation, validation, device management, and session tracking for ThriveTech products.
Base URL: /api/v1/activate
Authentication: All requests must include either:
- Product API Key (PAK):
Authorization: Bearer pak_live_xxxx - Channel API Key:
X-Channel-API-Key: ch_live_xxxx
Endpoints
| # | Method | Endpoint | Description |
|---|---|---|---|
| 1 | POST | /api/v1/activate/trial | Activate a trial license |
| 2 | POST | /api/v1/activate/purchase | Activate a purchased license |
| 3 | POST | /api/v1/activate/validate | Validate a license token |
| 4 | POST | /api/v1/activate/deactivate | Deactivate current device |
| 5 | GET/POST | /api/v1/activate/check-trial | Check trial eligibility |
| 6 | GET | /api/v1/activate/devices | List activated devices |
| 7 | POST | /api/v1/activate/devices/:deviceId/deactivate | Deactivate device by ID |
| 8 | POST | /api/v1/activate/heartbeat | Session heartbeat |
| 9 | GET | /api/v1/activate/sessions | List active sessions |
| 10 | POST | /api/v1/activate/sessions/:sessionId/terminate | Terminate session |
1. Trial Activation
POST /api/v1/activate/trial
Request Body
{"deviceFingerprint": "unique-hardware-identifier-min-16-chars","deviceInfo": {"name": "My MacBook Pro","type": "laptop","osType": "macOS","osVersion": "14.0","appVersion": "1.0.0"},"userInfo": {"email": "user@example.com","firstName": "John","lastName": "Doe"}}
Success Response (200)
{"success": true,"activation": {"id": "uuid","licenseToken": "eyJhbGciOiJIUzI1NiIs...","expiresAt": "2026-03-05T00:00:00.000Z","trialDaysRemaining": 14,"features": ["basic", "export", "sync"],"deviceId": "uuid","activationType": "trial"},"purchase": {"purchase_url": "https://example.com/buy","has_purchase_url": true}}
Errors
| Code | Error | Description |
|---|---|---|
| 400 | TRIAL_ALREADY_USED | Device has already used a trial |
| 400 | TRIAL_NOT_ENABLED | Trial not enabled for this product |
| 400 | DEVICE_BLOCKED | Device has been blocked |
Notes: One trial per device per product. If the device has an active trial, calling this returns the existing trial with a fresh token.
2. Purchase Activation
POST /api/v1/activate/purchase
Request Body (Direct License Key)
{"deviceFingerprint": "unique-hardware-identifier","deviceInfo": { "type": "desktop", "osType": "Windows", "appVersion": "1.0.0" },"storeReceipt": {"channelType": "direct","receiptData": "XXXX-XXXX-XXXX-XXXX"},"previousTrialToken": "optional-trial-token-for-conversion"}
Request Body (Store Purchase)
{"deviceFingerprint": "unique-hardware-identifier","deviceInfo": { "type": "desktop", "osType": "Windows", "appVersion": "1.0.0" },"storeReceipt": {"channelType": "microsoft_store","receiptData": "store-receipt-or-token","transactionId": "store-transaction-id","productId": "store-product-sku"}}
Channel Types
| Value | Description |
|---|---|
direct | License key from direct sales |
microsoft_store | Microsoft Store purchase |
google_play | Google Play purchase |
apple_app_store | Apple App Store purchase |
Success Response (200)
{"success": true,"activation": {"id": "uuid","licenseToken": "eyJhbGciOiJIUzI1NiIs...","expiresAt": "2027-01-15T00:00:00.000Z","features": ["basic", "advanced", "professional"],"activationType": "purchase","productTier": "professional","convertedFromTrial": false,"subscription": {"id": "sub_12345","status": "active","currentPeriodEnd": "2027-01-15T00:00:00.000Z"}}}
Seat Limit Reached (400)
When a license reaches its max device limit, the API returns the active device list:
{"success": false,"error": {"code": "SEAT_LIMIT_REACHED","message": "Maximum devices reached. Deactivate a device to continue.","activeDevices": [{"id": "device-uuid-1","deviceName": "Work Laptop","deviceType": "laptop","activatedAt": "2026-01-01T00:00:00.000Z","lastActiveAt": "2026-01-10T00:00:00.000Z"}],"seatInfo": { "maxSeats": 2, "usedSeats": 2, "availableSeats": 0 }}}
Errors
| Code | Error | Description |
|---|---|---|
| 400 | SEAT_LIMIT_REACHED | Max devices — includes device list for management |
| 400 | LICENSE_KEY_INVALID | Invalid license key format |
| 400 | LICENSE_KEY_NOT_FOUND | License key not found |
| 400 | LICENSE_KEY_EXPIRED | License has expired |
| 400 | LICENSE_KEY_REVOKED | License has been revoked |
| 400 | STORE_VALIDATION_FAILED | Store receipt validation failed |
Notes: Reactivation on a previously activated device doesn't consume an additional seat. Pass previousTrialToken to convert trial to paid while maintaining history.
3. License Validation
POST /api/v1/activate/validate
Call at app startup and every 24 hours.
Request Body
{"licenseToken": "eyJhbGciOiJIUzI1NiIs...","deviceFingerprint": "unique-hardware-identifier"}
Success Response (200)
{"valid": true,"license": {"type": "subscription","expiresAt": "2027-01-15T00:00:00.000Z","features": ["basic", "advanced", "professional"],"status": "active","daysRemaining": 45},"device": { "id": "device-uuid", "name": "My MacBook" }}
Errors
| Code | Error | Description |
|---|---|---|
| 400 | INVALID_TOKEN | Token malformed or invalid |
| 400 | TOKEN_EXPIRED | Token has expired |
| 400 | DEVICE_MISMATCH | Token not valid for this device |
| 400 | LICENSE_REVOKED | License has been revoked |
| 400 | LICENSE_EXPIRED | License has expired |
Notes: Implement a 7-day offline grace period. Cache results locally to reduce API calls. Use the features array for feature gating.
4. Device Deactivation
POST /api/v1/activate/deactivate
Request Body
{"licenseToken": "eyJhbGciOiJIUzI1NiIs...","deviceFingerprint": "unique-hardware-identifier","reason": "User uninstalled application"}
Call when the user uninstalls or signs out. The seat is freed immediately.
5. Trial Eligibility Check
GET /api/v1/activate/check-trial?deviceFingerprint=xxx
POST /api/v1/activate/check-trial
Response (Eligible)
{ "eligible": true, "trialDuration": 14, "features": ["basic", "export", "sync"] }
Response (Not Eligible)
{ "eligible": false, "reason": "A trial has already been used on this device", "expired": true }
6. List Devices
GET /api/v1/activate/devices?licenseToken=xxx&deviceFingerprint=current
Response
{"success": true,"devices": [{"id": "device-uuid-1","deviceName": "Work Laptop","deviceType": "laptop","osType": "Windows","activatedAt": "2026-01-01T00:00:00.000Z","lastActiveAt": "2026-01-15T10:30:00.000Z","isCurrentDevice": true}],"seatInfo": { "maxSeats": 3, "usedSeats": 2, "availableSeats": 1 }}
Use this to build a "Manage Devices" UI. Mark isCurrentDevice: true with a "This device" label.
7. Deactivate Device by ID
POST /api/v1/activate/devices/:deviceId/deactivate
Request Body
{"licenseToken": "eyJhbGciOiJIUzI1NiIs...","reason": "User removed device"}
Response
{ "success": true, "message": "Device deactivated successfully", "availableSeats": 2 }
8. Session Heartbeat
POST /api/v1/activate/heartbeat
Send every 5 minutes while the app is active. Sessions missing heartbeats for 10+ minutes are automatically terminated.
Request Body
{"licenseToken": "eyJhbGciOiJIUzI1NiIs...","deviceFingerprint": "unique-hardware-identifier","sessionId": "optional-existing-session-id"}
Response
{"success": true,"sessionId": "session-uuid","nextHeartbeatDue": "2026-01-15T10:35:00.000Z","sessionValid": true}
9. List Sessions
GET /api/v1/activate/sessions?licenseToken=xxx
Response
{"success": true,"sessions": [{"id": "session-uuid-1","deviceName": "Work Laptop","startedAt": "2026-01-15T08:00:00.000Z","lastHeartbeat": "2026-01-15T10:30:00.000Z","isCurrentSession": true}],"maxConcurrent": 3,"currentCount": 1}
10. Terminate Session
POST /api/v1/activate/sessions/:sessionId/terminate
Request Body
{"licenseToken": "eyJhbGciOiJIUzI1NiIs...","reason": "User signed out remotely"}
Error Code Reference
Activation Errors
| Code | Description |
|---|---|
TRIAL_ALREADY_USED | Device has already used a trial for this product |
TRIAL_NOT_ENABLED | Trial not enabled for this product |
DEVICE_BLOCKED | Device has been blocked |
SEAT_LIMIT_REACHED | License at maximum device limit |
License Key Errors
| Code | Description |
|---|---|
LICENSE_KEY_INVALID | Invalid key format |
LICENSE_KEY_NOT_FOUND | Key not found |
LICENSE_KEY_EXPIRED | Key has expired |
LICENSE_KEY_REVOKED | Key has been revoked |
Store Validation Errors
| Code | Description |
|---|---|
STORE_RECEIPT_INVALID | Malformed receipt |
STORE_VALIDATION_FAILED | Store API validation failed |
STORE_PRODUCT_MISMATCH | Product ID doesn't match |
STORE_SUBSCRIPTION_CANCELLED | Subscription was cancelled |
STORE_REFUNDED | Purchase was refunded |
Validation Errors
| Code | Description |
|---|---|
INVALID_TOKEN | Token malformed or tampered |
TOKEN_EXPIRED | Token has expired |
DEVICE_MISMATCH | Token not valid for this device |
LICENSE_REVOKED | License has been revoked |
LICENSE_EXPIRED | License has expired |
Session Errors
| Code | Description |
|---|---|
CONCURRENT_SESSION_LIMIT_REACHED | Max concurrent sessions reached |
SESSION_NOT_FOUND | Session ID not found |
SESSION_ALREADY_TERMINATED | Session already terminated |
Implementation Checklist
Required
- Generate consistent device fingerprint (hash of hardware IDs, min 16 chars)
- Store license token securely (encrypted local storage)
- Handle
SEAT_LIMIT_REACHEDwith device management UI - Implement periodic validation (startup + every 24h)
- Call deactivate on uninstall/sign-out
Recommended
- Session heartbeat every 5 minutes
- "Manage Devices" UI for self-service
- Offline grace period (7 days recommended)
- Cache validation results locally
- Trial-to-purchase conversion flow
Was this helpful?
