POST /validate
POST /api/v1/validate
Checks whether a license key is valid. Returns license info but does not increment any counter. Use this on every app launch to confirm the key is still active. The key is sent in the POST body (never the URL) to keep it out of access logs and browser history.
Request fields
licenseKey is required. Optional version contains the running app version; send it consistently when using version restrictions, since omitting it skips the version check.
Validation checks license entitlement, not proof that the current machine owns a registered activation seat. Also check that the returned product or bundle belongs to your app.
Examples
curl -X POST https://api.permitcore.dev/api/v1/validate \ -H "Content-Type: application/json" \ -d '{"licenseKey":"PERMIT-A3K7-MN2P-QR9X-TZ5W"}'
const res = await fetch('https://api.permitcore.dev/api/v1/validate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ licenseKey: 'PERMIT-A3K7-MN2P-QR9X-TZ5W' }), }); const data = await res.json(); if (data.isValid) { console.log('License OK for:', data.productName); // data.remainingActivations tells you how many more activations are allowed } else { console.error('Invalid license:', data.message); // Show error to user }
import requests resp = requests.post( 'https://api.permitcore.dev/api/v1/validate', json={'licenseKey': 'PERMIT-A3K7-MN2P-QR9X-TZ5W'} ) data = resp.json() if data['isValid']: print(f"Valid! Product: {data['productName']}, " f"Remaining activations: {data['remainingActivations']}") else: print(f"Invalid: {data.get('message', 'unknown error')}")
using var http = new HttpClient(); var resp = await http.PostAsJsonAsync( "https://api.permitcore.dev/api/v1/validate", new { licenseKey = "PERMIT-A3K7-MN2P-QR9X-TZ5W" } ); var result = await resp.Content.ReadFromJsonAsync<LicenseResult>(); if (result?.IsValid == true) Console.WriteLine($"Valid! Product: {result.ProductName}"); else Console.WriteLine($"Invalid: {result?.Message}"); record LicenseResult( bool IsValid, string? ProductName, int? RemainingActivations, DateTime? ExpiresAt, string? Message, Dictionary<string, string>? CustomFields );
Success response (200 OK)
{
"isValid": true,
"productName": "Acme Video Editor Pro",
"remainingActivations": 3,
"expiresAt": null,
"customFields": { "edition": "Pro", "seats": "5" },
"features": ["export", "api", "sso"],
"isTrial": false,
"trialDaysRemaining": null,
"nodeLocked": false,
"offlineGraceDays": 7,
"vendorWarning": null
}Invalid / revoked key
{
"isValid": false,
"message": "License key not found"
}Additional fields include errorCode, minVersion, maxVersion, bundleProducts, isInGracePeriod, maintenanceExpiresAt, and signed offlineCacheToken. These may be null when not applicable. A maintenance date is metadata for your app's update policy, not an automatic license rejection.
Response fields
| Field | Type | Description |
|---|---|---|
| isValid | boolean | true if the key exists, is active, not expired, and not revoked |
| productName | string | The product this key belongs to |
| remainingActivations | number | maxActivations minus currentActivations — how many more devices can activate |
| expiresAt | string | null | ISO 8601 expiry datetime, or null for perpetual licenses |
| customFields | object | null | Arbitrary key-value pairs attached to the license |
| features | string[] | null | Feature flag strings e.g. ["export","api","sso"]. Use these to conditionally unlock functionality in your app without issuing separate keys. |
| isTrial | boolean | true if this is a time-limited trial license |
| trialDaysRemaining | number | null | Days left on the trial period. Only present when isTrial is true. Show this to the customer to prompt them to purchase. |
| nodeLocked | boolean | true if the license is bound to specific devices (HWID mode) |
| offlineGraceDays | number | null | How many days your SDK may run without contacting the server. Use the signed offlineCacheToken with a trusted public key; do not trust an editable cached JSON response. null = no grace period. |
| message | string | Reason for invalidity — only present when isValid = false |
| vendorWarning | string | null | Present when the vendor's PermitCore subscription is nearing expiry. Surface this to the user. null during normal operation. |
vendorWarning in your app:
When this field is non-null, show it to the user (e.g. in a settings dialog or tooltip).
It tells them the vendor's subscription is expiring and they may lose access soon —
prompting them to contact the vendor to renew.