Skip to content

License Validation API

The License Validation API allows you to programmatically verify license keys. This is useful for custom launchers, build systems, or additional license checks within your game.

Endpoint

POST https://store.gamedna.studio/api/licenses/validate

Authentication

This endpoint does not require authentication headers. The license key itself serves as the credential.

Request

Headers

HeaderValueRequired
Content-Typeapplication/json

Body

{
"license_key": "GDNA-XXXX-XXXX-XXXX",
"machine_id": "unique-machine-identifier",
"product_slug": "online-subsystem-blueprintable"
}

Parameters

ParameterTypeRequiredDescription
license_keystringThe license key to validate (format: GDNA-XXXX-XXXX-XXXX)
machine_idstringUnique identifier for the machine (see below)
product_slugstringThe product slug to validate against

Machine ID Generation

The machine ID should be a stable, unique identifier for the device. Recommended approaches:

FString MachineId = FPlatformMisc::GetMachineId().ToString();

Response

Success Response (200 OK)

{
"valid": true,
"license": {
"id": "lic_abc123",
"type": "pro",
"seats_used": 3,
"seats_total": 5,
"owner_email": "user@example.com",
"product": {
"slug": "online-subsystem-blueprintable",
"name": "Online Subsystem Blueprintable",
"version": "3.2.0"
},
"created_at": "2025-06-15T10:30:00Z",
"expires_at": "2027-06-15T10:30:00Z",
"is_active": true
},
"activation": {
"id": "act_xyz789",
"machine_id": "unique-machine-identifier",
"activated_at": "2026-01-10T14:00:00Z",
"last_validated": "2026-01-22T09:30:00Z"
}
}

Error Responses

Invalid License Key (400)

{
"error": {
"code": "INVALID_LICENSE_KEY",
"message": "The provided license key is not valid.",
"details": {
"license_key": "Format should be GDNA-XXXX-XXXX-XXXX"
}
}
}

License Not Found (404)

{
"error": {
"code": "LICENSE_NOT_FOUND",
"message": "No license found with the provided key.",
"details": null
}
}

License Expired (403)

{
"error": {
"code": "LICENSE_EXPIRED",
"message": "This license has expired.",
"details": {
"expired_at": "2025-12-31T23:59:59Z",
"renewal_url": "https://store.gamedna.studio/dashboard/licenses/lic_abc123/renew"
}
}
}

No Seats Available (403)

{
"error": {
"code": "NO_SEATS_AVAILABLE",
"message": "All license seats are in use.",
"details": {
"seats_used": 5,
"seats_total": 5,
"upgrade_url": "https://store.gamedna.studio/dashboard/licenses/lic_abc123/upgrade"
}
}
}

Product Mismatch (400)

{
"error": {
"code": "PRODUCT_MISMATCH",
"message": "This license is not valid for the specified product.",
"details": {
"licensed_product": "discord-messenger",
"requested_product": "online-subsystem-blueprintable"
}
}
}

Rate Limited (429)

{
"error": {
"code": "RATE_LIMITED",
"message": "Too many validation requests. Please try again later.",
"details": {
"retry_after": 60
}
}
}

Error Codes

CodeHTTP StatusDescription
INVALID_LICENSE_KEY400License key format is invalid
MISSING_REQUIRED_FIELD400Required field missing from request
PRODUCT_MISMATCH400License doesn’t cover requested product
LICENSE_EXPIRED403License has expired
LICENSE_SUSPENDED403License has been suspended
NO_SEATS_AVAILABLE403All seats are in use
LICENSE_NOT_FOUND404License key doesn’t exist
RATE_LIMITED429Too many requests
INTERNAL_ERROR500Server error

Examples

cURL

Terminal window
curl -X POST https://store.gamedna.studio/api/licenses/validate \
-H "Content-Type: application/json" \
-d '{
"license_key": "GDNA-ABCD-1234-EFGH",
"machine_id": "my-dev-machine-001",
"product_slug": "online-subsystem-blueprintable"
}'

JavaScript/TypeScript

async function validateLicense(
licenseKey: string,
machineId: string,
productSlug: string
): Promise<LicenseValidation> {
const response = await fetch(
'https://store.gamedna.studio/api/licenses/validate',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
license_key: licenseKey,
machine_id: machineId,
product_slug: productSlug,
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
return response.json();
}
// Usage
try {
const result = await validateLicense(
'GDNA-ABCD-1234-EFGH',
getMachineId(),
'online-subsystem-blueprintable'
);
if (result.valid) {
console.log(`License valid! Type: ${result.license.type}`);
}
} catch (error) {
console.error('License validation failed:', error.message);
}

C++ (Unreal Engine)

void ULicenseManager::ValidateLicense(
const FString& LicenseKey,
const FString& ProductSlug)
{
FString MachineId = FPlatformMisc::GetMachineId().ToString();
TSharedRef<IHttpRequest> Request = FHttpModule::Get().CreateRequest();
Request->SetURL(TEXT("https://store.gamedna.studio/api/licenses/validate"));
Request->SetVerb(TEXT("POST"));
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
JsonObject->SetStringField(TEXT("license_key"), LicenseKey);
JsonObject->SetStringField(TEXT("machine_id"), MachineId);
JsonObject->SetStringField(TEXT("product_slug"), ProductSlug);
FString RequestBody;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&RequestBody);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
Request->SetContentAsString(RequestBody);
Request->OnProcessRequestComplete().BindUObject(
this, &ULicenseManager::OnValidationResponse);
Request->ProcessRequest();
}
void ULicenseManager::OnValidationResponse(
FHttpRequestPtr Request,
FHttpResponsePtr Response,
bool bSuccess)
{
if (!bSuccess || !Response.IsValid())
{
OnLicenseValidationFailed.Broadcast(TEXT("Network error"));
return;
}
TSharedPtr<FJsonObject> JsonResponse;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(
Response->GetContentAsString());
if (FJsonSerializer::Deserialize(Reader, JsonResponse))
{
bool bValid = JsonResponse->GetBoolField(TEXT("valid"));
if (bValid)
{
OnLicenseValidationSuccess.Broadcast();
}
else
{
FString ErrorMessage = JsonResponse
->GetObjectField(TEXT("error"))
->GetStringField(TEXT("message"));
OnLicenseValidationFailed.Broadcast(ErrorMessage);
}
}
}

Rate Limits

EndpointLimitWindow
/api/licenses/validate60 requests1 minute

Best Practices

  1. Cache Results - Don’t validate on every frame. Cache for the session.
  2. Handle Offline - Have graceful fallback when network is unavailable.
  3. Secure Machine ID - Don’t expose the machine ID generation method.
  4. Log Failures - Log validation failures for support purposes.
  5. User Feedback - Show clear messages when validation fails.