API Docs
Two real endpoints, both under /api/v1. Everything here mirrors exactly what the Protect Software wizard and every official SDK actually call — nothing on this page is capability those don't already use.
Prefer not to hand-roll requests? See /sdks for typed clients in Python, .NET, Go, PHP/WordPress and Chrome — or /metatrader for the no-code wizard.
Authentication
Two mutually exclusive ways to call the validate endpoint:
Protected Build (default)
Send productPublicId in the body — no Authorization header. A product's public ID (Dashboard → Products) can only check licenses for that one product, with no administrative access, so it's safe to embed directly in software you distribute. This is what the wizard and every SDK use.
Developer Integration
Send Authorization: Bearer sk_live_... plus productId instead. Your API key (Dashboard → API Keys) has full account access — never embed it in software you distribute, server-side use only.
POST /api/v1/license/validate
Validate a license
Checks a license key and activates it on this device if it has activation slots remaining. Safe to call on every launch — an already-activated device just gets its last-seen timestamp bumped, it doesn't consume a second slot.
Request body
{
"productPublicId": "pub_abc123",
"licenseKey": "LF-XXXX-XXXX-XXXX",
"hwid": "a stable per-device/per-install id you generate",
"accountNumber": "optional — an MT4/MT5 login number, etc."
}Response — valid
HTTP 200
{
"valid": true,
"expiresAt": "2027-01-01T00:00:00.000Z", // or null — no expiry
"graceHours": 72
}graceHours is this product's currently-configured offline-grace window — cache it and honor it locally if a later call can't reach this endpoint at all. See /reliability.
Response — not valid
HTTP 403 (or 404/401 — see error codes below)
{
"valid": false,
"error": "LICENSE_REVOKED"
}A well-formed "not valid" response is a normal answer, not a failure — only a genuine connection problem (timeout, DNS, malformed response) means LicenseFort couldn't actually be reached. Every SDK keeps that distinction explicit rather than folding both into one boolean.
POST /api/v1/license/trial
Issue a self-serve trial
Public, no API key — call this directly from your own signup/trial flow to issue a real, working license key on the spot. Only works for products with trials enabled (Dashboard → Products → Self-serve free trial); one trial per customer email, forever, per product.
Request body
{
"productPublicId": "pub_abc123",
"customerEmail": "customer@example.com",
"hwid": "optional — activates the key for this device immediately"
}Response
HTTP 201
{
"licenseKey": "LF-XXXX-XXXX-XXXX",
"expiresAt": "2026-08-27T00:00:00.000Z",
"productName": "Your Product",
"activated": false
}TRIAL_ALREADY_USED (409) if this email already claimed a trial for this product; TRIALS_NOT_ENABLED (400) if the product doesn't have trials turned on; RATE_LIMITED (429) past 5 requests/hour from one IP.
Error codes
Returned in the error field on any non-2xx response from validate.
| INVALID_INPUT | The request body didn't match the expected shape. |
| PRODUCT_NOT_FOUND | No product matches the given productId / productPublicId. |
| MISSING_API_KEY | Developer Integration mode was used with no Authorization header. |
| INVALID_API_KEY | The supplied API key doesn't match any active key. |
| INVALID_LICENSE | No license matches the given key for this product. |
| LICENSE_REVOKED | The license exists but has been revoked. |
| LICENSE_EXPIRED | The license exists but has passed its expiry date. |
| ACCOUNT_MISMATCH | The license is bound to a different account number than the one supplied. |
| ACTIVATION_LIMIT_REACHED | This device isn't already activated, and the license has no activation slots left. |
MT4/MT5 integration snippet
The Protect Software wizard inserts and compiles this for you automatically — see /metatrader. If you'd rather hand-write it, here's the minimum: a gate at the top of OnInit() that refuses to run without a valid license.
int OnInit()
{
if(!LF_ValidateLicense()) return(INIT_FAILED);
// ... your own OnInit() logic ...
return(INIT_SUCCEEDED);
}LF_ValidateLicense() itself is the part that actually calls the API above via WebRequest() — the wizard writes a complete, working version of it (device id, the real request/response parsing, offline-grace caching) for you. See the C#/Python/JavaScript examples on /metatrader for the equivalent call in other languages.
Not built yet: license CRUD via API, outbound webhooks
Creating, listing and revoking licenses programmatically with your API key, and getting a webhook when a license activates or a trial converts, aren't exposed as public API surface today — those are dashboard actions right now. If you need either, tell us at support@licensefort.com; this page will say so the day either ships, not before.
