Application API
Complete reference for the Proudly Application API. All requests go through POST /api/v1 with an action parameter.
Initiates a handshake session for your application. Returns a unique session identifier and encryption secret.
How it works
Send init
POST to /api/v1 with your admin ID, app ID, and action=init
Server validates
Verifies your app exists and the owner account is active
Session created
A secure session ID and encryption secret are generated
Encrypt requests
Use the returned secret to encrypt all subsequent API calls
Request parameters
Parameters can be sent as query string, form body, or as part of a route string in the format {admin_id}/{app_id}&action=init.
| Parameter | Type | Required | Description |
|---|---|---|---|
| admin_id | string | Required | Your admin user ID (found in your dashboard settings) |
| app_id | string | Required | The application ID you want to initialize a session for |
| action | string | Required | Must be init |
| secret_key | string | Required | Your app's secret key (found in dashboard). Required for unencrypted requests; not needed for encrypted requests. |
{admin_id}/{app_id}&init — either as a query string or in the request body. The explicit action=init parameter is also supported.
Encrypted request format
For production use, the init request should be encrypted. The URL includes plaintext hints for fast key lookup, while the request body is AES-encrypted.
URL format
POST https://proudlyauthentication.com/api/v1/?aid=ADMIN_ID&appid=APP_IDThe ?aid= and &appid= query parameters are plaintext hints (only needed for encrypted requests) that allow the server to resolve the correct encryption key in O(1) instead of brute-forcing all keys. For unencrypted requests these are not needed since the admin ID and app ID are already visible in the body. In C++, these are typically wrapped with XOR() for compile-time string obfuscation.
Encrypted body
encrypt(adminID, enc_key, iv) / encrypt(appID, enc_key, iv) / encrypt("&action=init", enc_key, iv) &iv=IV| Component | Description |
|---|---|
| enc_key | Your app's encryption key (set in dashboard). Used only for init — after init, use the returned secret instead. |
| iv | A random string generated per-session. Client generates it, sends it in plaintext as &iv=, and uses it for all encrypt/decrypt in that session. |
| / | Slash separator between each encrypted segment. Each segment is individually encrypted and hex-encoded. |
Encryption details
| Property | Value |
|---|---|
| Algorithm | AES-256-CBC with PKCS7 padding |
| Key derivation | SHA256(enc_key).substring(0, 32) — first 32 hex chars as UTF-8 bytes |
| IV derivation | SHA256(iv).substring(0, 16) — first 16 hex chars as UTF-8 bytes |
| Output encoding | Lowercase hexadecimal |
After init
The init response returns a session_id and secret. All subsequent requests must:
- Use the
secretas the encryption key (replacesenc_key) - Use the same
ivfrom the init request - Include
sessionidin every encrypted request body - The response is also encrypted with the same
secret+iv— decrypt it to read the JSON
"?aid=" and "&appid=" are wrapped with XOR() — a compile-time string obfuscation macro that prevents static analysis from extracting URL patterns from your binary. The macro has no effect on the actual string value sent to the server.
Code examples
import requests
# Your credentials from the dashboard
ADMIN_ID = "your_admin_id"
APP_ID = "your_app_id"
SECRET_KEY = "your_secret_key"
BASE_URL = "https://proudlyauthentication.com"
# Initialize handshake
response = requests.post(
f"{BASE_URL}/api/v1",
data=f"{ADMIN_ID}/{APP_ID}&init&secret_key={SECRET_KEY}"
)
result = response.json()
if result["success"]:
session_id = result["session_id"]
print(f"Session: {session_id}")
# Use session_id in subsequent requests
response = requests.post(
f"{BASE_URL}/api/v1",
data=f"{ADMIN_ID}/{APP_ID}&getglobalvariable&sessionid={session_id}&varid=my_variable"
)
print(f"Value: {response.json()['value']}")
else:
print(f"Error: {result['message']}")const ADMIN_ID = "your_admin_id";
const APP_ID = "your_app_id";
const SECRET_KEY = "your_secret_key";
const BASE_URL = "https://proudlyauthentication.com";
// Initialize handshake
const response = await fetch(`${BASE_URL}/api/v1`, {
method: "POST",
headers: { "Content-Type": "text/plain" },
body: `${ADMIN_ID}/${APP_ID}&init&secret_key=${SECRET_KEY}`
});
const result = await response.json();
if (result.success) {
const { session_id } = result;
console.log(`Session: ${session_id}`);
// Use session_id in subsequent requests
const varRes = await fetch(`${BASE_URL}/api/v1`, {
method: "POST",
headers: { "Content-Type": "text/plain" },
body: `${ADMIN_ID}/${APP_ID}&getglobalvariable&sessionid=${session_id}&varid=my_variable`
});
const varData = await varRes.json();
console.log(`Value: ${varData.value}`);
} else {
console.error(`Error: ${result.message}`);
}using System.Net.Http;
using System.Text.Json;
var adminId = "your_admin_id";
var appId = "your_app_id";
var secretKey = "your_secret_key";
var baseUrl = "https://proudlyauthentication.com";
using var client = new HttpClient();
// Initialize handshake
var content = new StringContent(
$"{adminId}/{appId}&init&secret_key={secretKey}"
);
var response = await client.PostAsync(
$"{baseUrl}/api/v1", content
);
var json = await response.Content
.ReadAsStringAsync();
var result = JsonSerializer
.Deserialize<JsonElement>(json);
if (result.GetProperty("success").GetBoolean()) {
var sessionId = result
.GetProperty("session_id").GetString();
// Use session_id in subsequent requests
var varContent = new StringContent(
$"{adminId}/{appId}&getglobalvariable&sessionid={sessionId}&varid=my_variable"
);
var varResponse = await client.PostAsync(
$"{baseUrl}/api/v1", varContent
);
var varJson = await varResponse.Content
.ReadAsStringAsync();
var varResult = JsonSerializer
.Deserialize<JsonElement>(varJson);
}curl -X POST https://proudlyauthentication.com/api/v1 \
-d "your_admin_id/your_app_id&init&secret_key=your_secret_key"
# Use the session_id from init response
curl -X POST https://proudlyauthentication.com/api/v1 \
-d "your_admin_id/your_app_id&getglobalvariable&sessionid=SESSION_ID_FROM_INIT&varid=my_variable"Response
Success
// Encrypted init request → encrypted response with secret
{
"success": true,
"session_id": "aB3kL9mNqR2xT7wZ...",
"secret": "xK4pQ8rS1vU6yW..."
}
// Unencrypted init request → plain JSON, no secret
{
"success": true,
"session_id": "aB3kL9mNqR2xT7wZ..."
}| Field | Type | Description |
|---|---|---|
| success | boolean | Always true on success |
| session_id | string | Unique session identifier (URL-safe base64, 32 chars). Use this in subsequent requests. |
| secret | string | Per-session encryption key (URL-safe base64, 22 chars). Only returned when the init request itself is encrypted. Use as the AES key for all subsequent requests. |
Error responses
{
"success": false,
"message": "Missing or invalid route"
}Returned when admin_id, app_id, or action is missing or malformed.
{
"success": false,
"message": "Application not found"
}The specified app_id does not exist under the given admin_id.
{
"success": false,
"message": "This application is currently unavailable"
}The application owner's account is frozen or banned.
Security
secret returned by init is used to encrypt all subsequent API requests with AES-256-CBC. Never expose it in client-side code, logs, or version control.
- Handshake sessions are short-lived and automatically expire
- Each session gets a unique encryption key — no two sessions share the same secret
- Maximum request size is 10 KB
- All responses include security headers (HSTS, X-Frame-Options, CSP)
- Failed handshakes are tracked to prevent brute-force attempts
Login
Authenticate a customer using their credentials within an established handshake session. Supports HWID binding, subscription validation, and IP-based security.
action=login
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init handshake |
| username | string | Required | Customer username (supports hex encoding) |
| password | string | Required | Customer password (supports hex encoding) |
| hwid | string | Optional | Hardware ID for device binding |
| subscription | string | Optional | Required subscription hash to validate |
Success response
{
"success": true,
"subscriptions": [/* subscription hashes */],
"message": "login_success"
}Error responses
{
"success": false,
"message": "username, password, sessionid, and hwid are required"
}Missing required credentials. Also returned for invalid content type or empty HWID value.
{
"success": false,
"message": "Invalid username or password"
}Username not found or password does not match.
{
"success": false,
"message": "Your account has been banned."
}Account is banned (may include ban_note). Also returned for HWID mismatch, IP blacklisted, email verification required, no active subscriptions, or missing required subscription.
{
"success": false,
"message": "Session handshake not found"
}No handshake record found for the provided session ID.
{
"success": false,
"message": "Too many failed login attempts. Please try again later."
}Session locked or account locked due to repeated failed login attempts.
Register
Create a new customer account with license key redemption. Supports optional email verification.
action=register
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init handshake |
| username | string | Required | Desired username (supports hex encoding) |
| string | Required | Customer email address | |
| password | string | Required | Account password (supports hex encoding) |
| key | string | Required | License key to redeem |
| hwid | string | Optional | Hardware ID for device binding |
| location | string | Optional | User location (max 120 chars) |
Success response
{
"success": true,
"subscriptions": [/* subscription hashes */]
}"verification_required": true instead of subscriptions.Error responses
{
"success": false,
"message": "username is required"
}Missing required fields (username, email, password, key). Also returned for invalid email format, username/email already exists, invalid/used/upgrade-only registration key, or location exceeding 120 characters.
{
"success": false,
"message": "Access from your IP address has been blocked by the application owner."
}Client IP is on the application's blacklist. Also returned when the registration key is banned.
{
"success": false,
"message": "Session handshake not found"
}No handshake record found for the provided session ID.
{
"success": false,
"message": "Session locked"
}Handshake session is locked due to too many prior failures.
Session Checkup
Heartbeat endpoint that validates an authenticated session, checks IP binding, and extends its lifetime.
action=sessioncheckup
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init handshake |
Success response
{
"success": true,
"expires_at": "2026-02-15T16:00:00Z",
"expiry": 3600000,
"beats": 42
}Error responses
{
"success": false,
"message": "sessionid is required"
}No session ID was provided in the request.
{
"success": false,
"message": "Session invalidated for security reasons"
}IP binding mismatch — the client IP differs from the IP bound to the session.
{
"success": false,
"message": "Session not authenticated"
}The handshake is not in an authenticated state or has no active session.
{
"success": false,
"message": "Session handshake not found"
}No handshake or active session found for the provided session ID.
Upgrade
Redeem a license key to add or extend subscriptions for an authenticated user.
action=upgrade
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init handshake |
| key | string | Required | License key to redeem |
Success response
{
"success": true,
"message": "license_key_success",
"subscriptions": [/* updated subscriptions */],
"customer": { /* customer object */ }
}Error responses
{
"success": false,
"message": "A license key is required"
}Missing key parameter, key shorter than 8 characters, key already redeemed, or upgrade key incompatible with current subscriptions.
{
"success": false,
"message": "This key has been banned and cannot be redeemed."
}The license key has been banned by the application owner. Also returned when the session is not authenticated.
{
"success": false,
"message": "License key not recognized"
}The provided key does not exist in the application's key store. Also returned when the session or customer is not found.
Verify Email
Verify a customer's email address using the verification code sent during registration.
action=verify
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init handshake |
| code | string | Required | Verification code from email |
Success response
{
"success": true,
"message": "verification_success"
}Error responses
{
"success": false,
"message": "Invalid or expired verification code"
}Missing verification code, code has expired, code does not match (includes attempts_remaining), or no customer associated with the session.
{
"success": false,
"message": "Session handshake not found"
}No handshake found for the provided session ID.
{
"success": false,
"message": "Email is already verified"
}The customer's email address has already been verified.
{
"success": false,
"message": "Maximum attempts exceeded. Please request a new code."
}Too many incorrect verification attempts. A new code must be requested via the resend action.
Resend Verification
Resend the email verification code. Rate limited to 5 resends maximum.
action=resend
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init handshake |
Success response
{
"success": true,
"message": "resend_verification_success"
}Error responses
{
"success": false,
"message": "sessionid is required"
}No session ID was provided. Also returned when no customer is associated with the session.
{
"success": false,
"message": "Session handshake not found"
}No handshake found for the provided session ID.
{
"success": false,
"message": "A verification code is still active. Please wait before requesting a new one.",
"retry_after": 120
}The current verification code hasn't expired yet (includes retry_after in seconds). Also returned when the resend limit of 5 has been reached.
Reset Password — Request Code
Request a password-reset code by email for a user who forgot their password. Behaves exactly like the portal reset: same code length, expiry, attempt limits, and send-rate limits. The response is always generic and never reveals whether an account exists.
action=resetpassword
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init handshake |
| string | Either | Email of the account to reset | |
| username | string | Either | Username of the account to reset (provide email or username) |
Success response
{
"success": true,
"message": "password_reset_email_sent"
}Always returned whether or not the account exists (no user enumeration). A code email is only sent when the account is real. Limits (identical to portal): max 5 requests per 15 min per IP (30 min block), max 5 reset requests per account per 60 min, and a new code is blocked while the current one is still active (~20 min).
Error responses
{
"success": false,
"message": "Too many password reset requests. Please try again later.",
"error_code": "RESET_LIMIT_REACHED",
"retry_after_minutes": 42
}400 missing sessionid / no email or username (password_reset_identifier_required); 404 handshake not found; 429 per-IP throttle, account request limit (RESET_LIMIT_REACHED + retry_after_minutes), or a code still active (CODE_STILL_ACTIVE + retry_after).
Reset Password — Confirm
Submit the emailed code plus a new password to complete the reset. On success all of the user's existing sessions are revoked and they must log in again.
action=resetpasswordconfirm
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init handshake |
| code | string | Required | The reset code from the email |
| new_password | string | Required | The new password to set |
| string | Either | Email of the account (provide email or username) | |
| username | string | Either | Username of the account |
Success response
{
"success": true,
"message": "password_reset_success",
"sessions_revoked": 2
}Error responses
{
"success": false,
"message": "Invalid reset code.",
"error_code": "CODE_MISMATCH",
"attempts_remaining": 3
}Wrong code CODE_MISMATCH (+ attempts_remaining); expired RESET_CODE_EXPIRED (410); max 5 attempts RESET_ATTEMPTS_EXCEEDED (423); weak password INVALID_PASSWORD; same as old PASSWORD_UNCHANGED; no pending reset NO_RESET_PENDING. Per-IP throttle: max 10 confirms per 15 min (15 min block) → 429.
Validate Access Key
Validate an issued access key and return its policy's subscription bundle. Send this action through the same encrypted application-API envelope established by init.
action=accesskey| sessionid | string | Required | Session ID from init |
| key | string | Required | Issued access key |
| hwid | string | Policy-dependent | Hardware ID used for first-use bonding |
Keys are issued through an IP-verified site challenge, then bind to the first device that validates them in an application. Validation does not compare the application's IP with the issuance IP. Success returns subscription_hashes, expires_at, binding_mode, and hwid_bound. Failure status values include invalid, expired, revoked, hwid_required, hwid_mismatch, used, and banned.
Get Global Variable
Retrieve a global application variable by ID or name. Supports access control: public, authenticated, role-based, and subscription-based. Public variables are cached for 30 seconds.
action=getglobalvariable
| Parameter | Type | Required | Description |
|---|---|---|---|
| varid | string | Required | Variable ID or name |
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"value": "variable_value"
}Error responses
{
"success": false,
"message": "varID parameter is required"
}No variable identifier was provided in the request.
{
"success": false,
"message": "Authentication required"
}The variable requires authentication but no valid session was provided.
{
"success": false,
"message": "Access denied"
}The customer's role is not in the variable's allowed roles list, or they do not have an active required subscription.
{
"success": false,
"message": "Global variable not found"
}The specified variable ID or name does not exist.
Get Data Variable
Retrieve a data variable with access control support (public, authenticated, role-based, subscription-based).
action=getdatavariable
| Parameter | Type | Required | Description |
|---|---|---|---|
| varid | string | Required | Variable ID or name |
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"value": "variable_value"
}Error responses
{
"success": false,
"message": "varID parameter is required"
}No variable identifier was provided in the request.
{
"success": false,
"message": "Authentication required"
}The variable is not public and requires authentication, but no valid session was provided.
{
"success": false,
"message": "You do not have the required role to access this variable"
}The customer's role is not in the variable's allowed roles list. Also returned when the required subscription is missing.
{
"success": false,
"message": "Data variable not found"
}The specified variable ID or name does not exist in the application's data variables.
Get User Variable
Retrieve a user-specific variable value for the authenticated customer.
action=getuservariable
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
| varid | string | Required | Variable ID or name |
Success response
{
"success": true,
"value": "user_variable_value"
}Error responses
{
"success": false,
"message": "varID parameter is required"
}No variable identifier was provided. Also returned when sessionid is missing.
{
"success": false,
"message": "User variable definition not found"
}The variable definition was not found by ID or name. Also returned when the session or customer is not found.
Get Shared User Variable
Retrieve shared user variable values across users, if sharing is enabled for the variable.
action=getuservariableshared
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
| varid | string | Required | Variable ID or name |
| username | string | Optional | Filter by specific user |
Error responses
{
"success": false,
"message": "varID parameter is required"
}No variable identifier was provided. Also returned when session credentials are missing.
{
"success": false,
"message": "Variable is not share-enabled"
}The variable does not have sharing enabled. Also returned when the session is not authenticated.
{
"success": false,
"message": "User variable definition not found"
}The variable definition was not found. Also returned when the session or customer is not found.
Set User Variable
Set or update a user-specific variable value for the authenticated customer.
action=setuservariable
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
| varid | string | Required | Variable ID or name |
| value | any | Required | Value to set (string, number, object, array) |
Success response
{
"success": true,
"message": "variable_set_success"
}Error responses
{
"success": false,
"message": "varID parameter is required"
}No variable identifier was provided. Also returned when the value parameter is missing or session credentials are absent.
{
"success": false,
"message": "User variable definition not found"
}The variable definition was not found by ID or name. Also returned when the session or customer is not found.
Get Subscriptions
Returns active subscription hashes for the authenticated customer.
action=getsubscriptions
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"subscriptions": [/* subscription hashes */]
}Error responses
{
"success": false,
"message": "sessionid is required"
}No session ID was provided in the request.
{
"success": false,
"message": "Session not authenticated"
}The handshake is not in an authenticated state.
{
"success": false,
"message": "Customer session missing"
}Session handshake or customer record not found for the provided session ID.
Download File
Download a file from the application's proxies. Supports streaming, encoding options, multipart downloads, and byte ranges.
action=downloadfile
| Parameter | Type | Required | Description |
|---|---|---|---|
| fileid | string | Required | File ID or filename. Aliases: file_id, file, filename |
| sessionid | string | Required | Session ID from init |
| stream | boolean | Optional | Set true for raw binary stream (application/octet-stream). Much faster for large files. |
| encoding | string | Optional | hex (default) or base64. Only applies in JSON mode (non-streaming). |
| multi | integer | Optional | Number of chunks (1–100) for multipart parallel download |
| start | integer | Optional | Start byte for range request. Use with end. |
| end | integer | Optional | End byte for range request. Use with start. |
Success response (JSON mode)
{
"success": true,
"encoding": "hex",
"contents": "4a6f686e..."
}
// With base64 encoding
{
"success": true,
"encoding": "base64",
"contents": "Sm9obi4uLg=="
}
// Range request (206 Partial Content)
{
"success": true,
"encoding": "hex",
"contents": "4a6f...",
"range": "bytes 0-1023",
"partial_content": true
}When stream=true, the response is raw binary data with Content-Type: application/octet-stream instead of JSON.
Error responses
{
"success": false,
"message": "fileid parameter is required"
}No file identifier was provided. Also returned for invalid byte range, invalid multi parameter (must be 1–100), or invalid integer values for range/multi.
{
"success": false,
"message": "You do not have the required subscription to download this file."
}File requires a subscription or role the customer does not have.
{
"success": false,
"message": "File 'myfile' not found in proxies list."
}File ID does not match any proxy entry, or no file proxies are configured for the application. Also returned when the file entry has no URL or data content.
{
"success": false,
"message": "Requested range not satisfiable"
}The requested byte range exceeds the file's data size.
Get Download URL
Get the direct download URL for a file without streaming its content.
action=getdownloadurl
| Parameter | Type | Required | Description |
|---|---|---|---|
| fileid | string | Required | File ID or filename |
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"url": "https://example.com/file.zip",
"size": 12345
}Error responses
{
"success": false,
"message": "File 'myfile' contains inline data, not a URL. Use the downloadfile action instead."
}The file entry contains inline data rather than a URL. Also returned when the fileid parameter is missing.
{
"success": false,
"message": "You do not have the required subscription to access this file URL."
}File requires a subscription or role the customer does not have.
{
"success": false,
"message": "File 'myfile' not found in proxies list."
}File ID does not match any proxy entry, or no file proxies are configured for the application.
Get File Hash
Get the SHA256 hash of a file for integrity verification without downloading the full file content.
action=getfilehash
| Parameter | Type | Required | Description |
|---|---|---|---|
| fileid | string | Required | File ID, hash, or filename |
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 12345
}Error responses
{
"success": false,
"message": "You do not have the required subscription to access this file."
}File requires a subscription or role the customer does not have.
{
"success": false,
"message": "File 'myfile' not found in proxies list."
}File ID does not match any proxy entry, or no file proxies are configured for the application.
Get Chat Messages
Retrieve the last 100 chat messages from a specific channel.
action=chatget
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
| channel | string | Required | Channel/chatroom name |
Success response
{
"success": true,
"messages": [
{
"id": 41,
"message": "anyone got the new build?",
"username": "kenny",
"display_name": "Kenny",
"timestamp": "2026-06-19T10:00:00Z",
"customer_id": "101"
// normal message — no reply_to
},
{
"id": 42,
"message": "yeah it's in the portal",
"username": "alice",
"display_name": "Alice",
"timestamp": "2026-06-19T10:01:00Z",
"customer_id": "102",
"reply_to": { // present → this message is a reply
"id": 41,
"author": "Kenny",
"preview": "anyone got the new build?"
}
}
],
"channel": "general"
}Each message that is a reply carries a reply_to object with the parent id, author, and a short content preview (frozen when the reply was sent, so it still renders even if the parent was later deleted). Normal messages omit this field — detect a reply by checking whether reply_to is present.
Error responses
{
"success": false,
"message": "sessionid is required"
}No session ID was provided in the request.
{
"success": false,
"message": "Session not authenticated"
}The handshake is not in an authenticated state.
{
"success": false,
"message": "Chat room does not exist."
}Session handshake not found, or the specified chat room/channel does not exist.
Send Chat Message
Send a message to a chat channel. Content is sanitized and filtered automatically.
action=chatsend
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
| channel | string | Required | Channel/chatroom name |
| message | string | Required | Message content |
| reply_to | integer | Optional | ID of the message being replied to. Omit for a normal message. If the id is unknown or the parent was deleted, the reply link is ignored and the message posts normally. Aliases: parent_id, reply_id. |
Success response
{
"success": true,
"message": "message_sent_successfully",
"message_data": { /* message object */ }
}When reply_to is supplied and the parent exists, the returned message_data includes a reply_to object with the parent id, author, and a short content preview (frozen at reply time so it still renders if the parent is later deleted).
Error responses
{
"success": false,
"message": "Message parameter is required"
}Missing message content, message empty after sanitization, or message blocked by content filter (includes blocked: true and error_code: MESSAGE_BLOCKED).
{
"success": false,
"message": "Your account is banned from chat."
}Customer account is banned. Also returned when the session is not authenticated.
{
"success": false,
"message": "Chat room does not exist."
}Session handshake, customer, or chat room not found.
Get Online Users
Fetch all currently online users based on recent heartbeat activity. Cached for 15 seconds.
action=getonlineusers
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"online_users": [
{ "id": 123, "username": "user1" }
],
"count": 1
}App Status
Get application statistics including total users and online user count. Cached for 15 seconds.
action=getstatus
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"status": {
"numUsers": 1000,
"numOnlineUsers": 42
}
}Get Profile Picture
Get the authenticated customer's profile picture URL and username.
action=getprofilepicture
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"username": "user123",
"profile_picture": "https://...",
"has_profile_picture": true
}Error responses
{
"success": false,
"message": "sessionid is required"
}No session ID was provided in the request.
{
"success": false,
"message": "Session not authenticated"
}The handshake is not in an authenticated state.
{
"success": false,
"message": "Customer session missing"
}Session handshake or customer record not found.
Lookup Profile Picture
Look up another customer's profile picture by their username.
action=getprofilepicturebyusername
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
| target_username | string | Required | Username to look up |
Success response
{
"success": true,
"username": "target_user",
"profile_picture": "https://...",
"has_profile_picture": true,
"is_self": false
}Error responses
{
"success": false,
"message": "target_username parameter is required"
}Missing session ID or target username parameter.
{
"success": false,
"message": "Session not authenticated"
}The handshake is not in an authenticated state.
{
"success": false,
"message": "Requested user could not be found"
}Session handshake not found, or the target username does not match any customer.
Check Blacklist
Check if the authenticated user is banned or blacklisted.
action=checkblacklist
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
Success response
{
"success": true,
"blacklisted": false
}Error responses
{
"success": false,
"message": "sessionid is required"
}No session ID was provided in the request.
{
"success": false,
"message": "Session not authenticated"
}The handshake is not in an authenticated state or has no active session.
{
"success": false,
"message": "Customer session missing"
}Session handshake or customer record not found.
Ban
Bans the customer's account and all associated license keys. Intended to be triggered when anti-debug checks, integrity validation, or other detection mechanisms fail. This action is irreversible from the API.
action=ban
| Parameter | Type | Required | Description |
|---|---|---|---|
| sessionid | string | Required | Session ID from init |
| note | string | Optional | Ban reason/note |
Success response
{
"success": true,
"message": "account_banned_success",
"keys_banned": 3
}Error responses
{
"success": false,
"message": "sessionid is required"
}Missing sessionid parameter.
{
"success": false,
"message": "Session not authenticated"
}The handshake is not in an authenticated state.
{
"success": false,
"message": "Customer not found in customers list"
}Session handshake or customer record not found in the application's customer list.
Portal Login
Authenticate a customer and receive a session token for the portal API. Uses JSON request/response.
/api/portal/v1/{portal_key}/ — All portal endpoints require a valid portal key in the URL path. Auth endpoints use Bearer tokens in the Authorization header.| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username |
| password | string | Required | Customer password |
Success response
{
"success": true,
"customer": { /* customer object */ },
"session": {
"token": "hex_session_token",
"expires_at": "2026-02-15T16:00:00Z"
}
}Error responses
{
"success": false,
"message": "Username and password are required"
}Missing credentials or non-JSON request body.
{
"success": false,
"message": "Invalid username or password"
}Wrong credentials. Also returned with error_code: EMAIL_NOT_VERIFIED when email verification is required.
{
"success": false,
"message": "Access from your IP address has been blocked."
}IP blacklisted or account banned.
Portal Register
Create a new customer account with optional license key. Sends a verification email. Password must contain uppercase, lowercase, and digit (min 8 chars).
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | 3-32 chars, alphanumeric/underscore/hyphen |
| string | Required | Valid email, max 254 chars | |
| password | string | Required | Min 8 chars, must contain uppercase, lowercase, digit |
| key | string | Optional | License key to redeem on registration |
| location | string | Optional | User location, max 120 chars |
Success response
{
"success": true,
"message": "Verification code sent. Please confirm your email.",
"customer": { /* customer object */ },
"verification_required": true,
"verification_expires_at": "2026-02-15T16:10:00Z"
}Error responses
{
"success": false,
"message": "Missing required field: username"
}Missing fields, invalid username/email/password format, duplicate username/email, invalid/used/upgrade-only key, or location too long.
{
"success": false,
"message": "Access from your IP address has been blocked."
}IP blacklisted or registration key is banned.
Portal Verify Email
Verify email with a 6-digit code sent during registration.
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | string | Required | 6-digit verification code from email |
| string | Either | Customer email (provide email or username) | |
| username | string | Either | Customer username (provide email or username) |
Success response
{
"success": true,
"message": "Email verified successfully",
"verified": true
}Error responses
{
"success": false,
"message": "Invalid or expired verification code",
"error_code": "VERIFICATION_FAILED"
}Missing code/identifier, wrong code, expired code, or customer not found.
{
"success": false,
"message": "Email is already verified",
"error_code": "ALREADY_VERIFIED"
}Email has already been verified.
{
"success": false,
"message": "Maximum attempts exceeded. Please request a new code.",
"error_code": "ATTEMPTS_EXCEEDED"
}Too many incorrect verification attempts.
Portal Resend Code
Resend the email verification code. Returns generic success even if account not found (prevents enumeration).
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Either | Customer email | |
| username | string | Either | Customer username |
Success response
{
"success": true,
"message": "Verification email resent",
"resend_count": 1
}Error responses
{
"success": false,
"message": "A verification code is still active...",
"error_code": "CODE_STILL_ACTIVE"
}Existing code not yet expired, or resend limit reached (RESEND_LIMIT_REACHED).
Password Reset
Two-step password reset: request a reset code via email, then confirm with the code and new password.
Request reset code
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Either | Customer email | |
| username | string | Either | Customer username |
{
"success": true,
"message": "If an account exists, a reset email has been sent."
}Confirm reset
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | string | Required | Reset code from email |
| new_password | string | Required | New password (must meet complexity rules) |
| string | Either | Customer email | |
| username | string | Either | Customer username |
{
"success": true,
"message": "Password reset successfully",
"sessions_revoked": 2
}Session & Logout
Validate the current session or log out. Requires Authorization: Bearer {token} header.
Validate session
{
"success": true,
"valid": true,
"customer": { /* customer with hwid_state, subscriptions */ },
"has_password": true,
"session": { "expires_at": "..." }
}Logout
{
"success": true,
"revoked": true
}Error responses
{
"success": false,
"valid": false,
"message": "Session token required"
}Missing or invalid Bearer token, expired session, customer not found, account banned, or IP mismatch.
HWID Reset
Reset the customer's hardware ID. Requires authentication.
| Parameter | Type | Required | Description |
|---|---|---|---|
| note | string | Optional | Reason for reset |
Success response
{
"success": true,
"message": "HWID reset successful",
"state": { /* hwid state */ },
"policy": { /* hwid policy */ }
}Error responses
{
"success": false,
"message": "HWID resets are disabled for this application."
}HWID feature disabled or resets not available.
{
"success": false,
"message": "HWID reset is on cooldown."
}Cooldown period has not elapsed since last reset.
Redeem Key
Redeem an additional license key on an existing account. Requires authentication.
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Required | License key value (also accepts license_key) |
Success response
{
"success": true,
"message": "Key redeemed successfully",
"customer": { /* updated customer */ }
}Error responses
{
"success": false,
"message": "License key is required"
}Missing key, key too short, already redeemed, or upgrade key incompatible with current subscriptions.
{
"success": false,
"message": "License key not recognized"
}Key does not exist. Also returns 403 if the key is banned.
Set Password
Set a password for OAuth-only accounts that don't have one. Requires authentication.
| Parameter | Type | Required | Description |
|---|---|---|---|
| password | string | Required | New password (must meet complexity rules) |
Success response
{
"success": true,
"message": "Password set successfully"
}Error responses
{
"success": false,
"message": "Password is already set. Use change-password instead."
}Account already has a password, password field is empty, or password does not meet complexity requirements.
Issue an Access Key
Build the key-generation interface on your own site and call Proudly's portal API from its JavaScript. The site's exact HTTPS origin must match the integration selected by the policy.
Browser flow
- Fetch the policy metadata and read
integration_id. - Create a challenge with
{ "policy_slug": "your-slug", "integration_id": "..." }. - If
challenge.turnstile_requiredis true, render Turnstile with the returnedsite_key,action, andcdata. Otherwise continue immediately. - Issue the key with
{ "challenge_id": "...", "turnstile_token": "..." }. Omit or leave the token empty when Turnstile is disabled.
GitHub Pages and other static hosts are supported. The Turnstile public site key may be used in browser JavaScript, but its secret must remain in Proudly. Configure the integration hostname in Cloudflare's Turnstile hostname allowlist.
Challenge creation and issuance accept only the configured integration origin. Issuance also verifies the request IP, five-minute single-use challenge, policy status, cooldown and bans. When Turnstile is enabled it additionally verifies the token hostname, action, and cData.
OAuth Flow
OAuth authentication flow for third-party providers (Discord, etc). Supports login, register, and account linking.
List providers
{
"success": true,
"providers": [{ "name": "discord", "display_name": "Discord", "icon": "...", "color": "..." }]
}Authorize
| Parameter | Type | Required | Description |
|---|---|---|---|
| action | string | Optional | login, register, or link (default: login) |
| redirect_uri | string | Optional | Where to redirect after OAuth |
| session_token | string | Optional | Session token for link action |
Returns HTTP 302 redirect to the OAuth provider's authorization page.
Callback
Handles the OAuth provider callback. Redirects to frontend with hash params: oauth_session (success), oauth_error (failure), or oauth_link_required (needs password confirmation).
Link account
| Parameter | Type | Required | Description |
|---|---|---|---|
| link_token | string | Required | Token from OAuth callback |
| password | string | Required | Current account password |
Link status
Returns linked OAuth accounts, available providers, has_password, and can_unlink. Requires auth.
Unlink provider
| Parameter | Type | Required | Description |
|---|---|---|---|
| provider | string | Required | Provider name to unlink (e.g. discord) |
Error responses
{
"success": false,
"message": "Invalid provider"
}Unknown or disabled provider, missing fields, invalid/expired link token, or cannot unlink last OAuth provider without a password set.
Portal Info & Stats
Get portal information, community statistics, subscription types, and badge definitions.
Portal info
{
"success": true,
"portal": { "name": "Community", "banner": {}, "settings": {}, "oauth_providers": [] }
}Stats
{
"success": true,
"stats": { "total_members": 100, "online_members": 5, "total_threads": 50, "total_replies": 200 }
}Subscription types
{
"success": true,
"subscriptions": [{ "public_id": "sub_abc", "name": "Premium", "display_name": "Premium Plan" }]
}My subscriptions
Requires authentication. Returns the logged-in user's active subscriptions with id, expires_at, and granted_at.
Badges
{
"success": true,
"badges": [{ "id": "badge_1", "name": "First Post", "icon": "...", "color": "#...", "type": "achievement" }],
"badge_system_enabled": true
}Profile
Get and update customer profiles. View other users' public profiles.
Get own profile
Requires authentication. Returns the full profile payload.
Update profile
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Optional | 3-32 chars, alphanumeric/underscore/hyphen |
| location | string | Optional | Max 120 chars |
| status | string | Optional | Max 100 chars |
| bio | string | Optional | Max 500 chars |
| profile_picture | string | Optional | URL, max 2000 chars |
View user profile
Requires authentication. Returns profile and friendship_status.
Lookup by username
No auth required (enhanced with auth). Returns profile and friendship_status. Returns 404 if user not found.
Members
Get a paginated list of members with online status. Requires search query (min 2 chars) unless requesting online-only users.
| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | Optional | Search query, min 2 chars (required unless online=true) |
| online | string | Optional | Set true to show only online members |
| page | integer | Optional | Page number (default: 1) |
| limit | integer | Optional | Results per page (10-50, default: 20) |
Success response
{
"success": true,
"members": [{ "id": 1, "username": "...", "is_online": true }],
"total": 100,
"online_count": 5,
"has_more": true
}Search
Global search across threads, posts/replies, and users.
| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | Required | Search term, min 2 chars |
| type | string | Optional | threads, posts, users, or all (default: all) |
| limit | integer | Optional | Max results per type (default: 10, max: 50) |
| page | integer | Optional | Page number (default: 1, max: 100) |
Success response
{
"success": true,
"results": { "threads": [], "posts": [], "users": [] }
}Variables
Read global & data variables that have been flagged with Allow Portal Access in the dashboard. The variable's existing access control (public / authenticated / roles / subscriptions) is still enforced — pass a customer session as Authorization: Bearer <session_token> to read non-public variables.
List global variables
Returns every portal-enabled global variable. value is included only when the caller is allowed to read it; otherwise accessible is false and the value is omitted.
{
"success": true,
"variables": [
{ "id": 123, "name": "welcome_message", "access_type": "public", "accessible": true, "value": "Hello!" },
{ "id": 124, "name": "premium_config", "access_type": "subscriptions", "accessible": false }
]
}Get one global variable
{name} is the variable name or id. Returns 404 if the variable does not exist or is not portal-enabled; 401/403 if access control is not satisfied.
{
"success": true,
"value": "Hello!"
}List data variables
Same shape as global variables, plus a schema field when one is defined.
Get one data variable
Friends
Friend system — list friends, manage requests, remove friends. All endpoints require authentication.
List friends
Pending requests
Returns incoming and outgoing request arrays.
Send / Accept / Reject / Cancel
Actions: request, accept, reject, cancel. Body: {"user_id": 123}
Remove friend
Direct Messages
Private messaging between users. All endpoints require authentication.
List conversations
{
"success": true,
"conversations": [{ "conversation_id": "123_456", "participant": {}, "last_message": {}, "unread_count": 2 }]
}Unread count
Get conversation
Returns messages with sender info. Errors: 403 access denied, 404 not found.
Send message
| Parameter | Type | Required | Description |
|---|---|---|---|
| recipient_id | integer | Either | Recipient user ID |
| recipient_username | string | Either | Recipient username |
| content | string | Required | Message content, min 2 chars |
Mark as read
Notifications
User notification management. All endpoints require authentication.
List notifications
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | Optional | Max notifications (default: 20, max: 50) |
| unread_only | string | Optional | Set true for unread only |
Unread count
Mark as read
Mark all as read
User Blocking
Block and unblock users. Requires authentication.
Block / Unblock
| Parameter | Type | Required | Description |
|---|---|---|---|
| user_id | integer | Required | Target user ID |
| action | string | Required | block or unblock |
Blocked list
Forum Categories
Browse, create, update, reorder, and delete forum categories. Admin role required for write operations.
List categories
No auth required (enhanced with auth for permissions). Returns categories with user_role and permissions.
Create category
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Category name, max 100 chars |
| description | string | Optional | Category description |
| parent_id | integer | Optional | Parent category ID for subcategories |
Reorder
Admin only. Body: {"positions": [...]}
Update category
Delete category
Admin only. Cannot delete categories with subcategories or threads.
Forum Threads
Browse, create, view, edit, and delete forum threads. Supports polls and edit history.
List threads in category
Query: page, limit (max 50). Returns threads, category, pagination, can_post.
Create thread
| Parameter | Type | Required | Description |
|---|---|---|---|
| title | string | Required | Thread title, max 200 chars |
| content | string | Required | Thread body, max 50000 chars |
| poll | object | Optional | Poll: question, options (2-10), allow_multiple, ends_at |
View thread
Returns full thread with replies, likes, reactions, poll, and edit info.
Edit thread
Author or moderator. Body: title, content, edit_reason. Saves edit history.
Delete thread
Author or moderator. Deletes the thread and all its replies.
Recent activity
Query: limit (max 20), type (all/threads/replies/members/badges). Returns mixed activity feed.
Forum Replies
Post, edit, and delete replies on threads. Supports nested replies and edit history.
List replies
Query: page, limit (max 50). Returns replies and pagination.
Post reply
| Parameter | Type | Required | Description |
|---|---|---|---|
| content | string | Required | Reply content, 3-10000 chars |
| parent_reply_id | integer | Optional | Parent reply ID for nested replies |
Returns 403 if thread is locked. Creates notifications for thread author, parent reply author, and @mentions.
Edit reply
Delete reply
Edit history
Returns history array and edit_count. Requires authentication.
Likes, Reactions & Watch
Like, react to, and watch forum content. All require authentication.
Like thread / reply
Toggle like. Returns liked (boolean) and like_count.
React to thread / reply
| Parameter | Type | Required | Description |
|---|---|---|---|
| reaction_type | string | Required | like, love, helpful, insightful, funny |
One reaction per user. Same reaction again removes it. Returns user_reaction, reactions map, total_reactions.
Watch thread
Query: active_minutes (max 30, default 5). Returns watchers and count.
Start/stop watching a thread (heartbeat for "who's reading").
Forum Moderation
Moderator actions for forum threads. Requires moderator role.
Lock / Unlock
Toggles lock state. Returns is_locked.
Pin / Unpin
| Parameter | Type | Required | Description |
|---|---|---|---|
| pin_type | string | Optional | none, pinned, or announcement. Omit to toggle. |
| pinned_until | string | Optional | ISO datetime for auto-expiring pin |
Move thread
| Parameter | Type | Required | Description |
|---|---|---|---|
| category_id | integer | Required | Target category ID |
Polls
View and vote on thread polls.
Get poll
{
"success": true,
"poll": {
"question": "...",
"options": [{ "index": 0, "text": "...", "votes": 5, "percentage": 50.0 }],
"total_votes": 10,
"user_votes": [0],
"has_voted": true
}
}Vote
| Parameter | Type | Required | Description |
|---|---|---|---|
| options | array | Required | Array of option indices to vote for |
Portal Chat
Global chat messaging with moderation support. All endpoints require authentication.
Get messages
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | Optional | Max messages (default: 50, max: 100) |
| before_id | integer | Optional | Load messages before this ID (pagination) |
Success response
{
"success": true,
"messages": [
{
"id": 41,
"sender_id": 101,
"content": "anyone got the new build?",
"created_at": "2026-06-19T10:00:00Z",
"sender": { "display_name": "Kenny", "username": "kenny" }
// normal message — no reply_to
},
{
"id": 42,
"sender_id": 102,
"content": "yeah it's in the portal",
"created_at": "2026-06-19T10:01:00Z",
"sender": { "display_name": "Alice", "username": "alice" },
"reply_to": { // present → this message is a reply
"id": 41,
"author": "Kenny",
"preview": "anyone got the new build?"
}
}
],
"room": { "id": "global", "name": "Global Chat", "topic": "" }
}Each message that is a reply carries a reply_to object with the parent id, author, and a short content preview (frozen when the reply was sent, so it still renders even if the parent was later deleted). Normal messages omit this field — detect a reply by checking whether reply_to is present.
Send message
| Parameter | Type | Required | Description |
|---|---|---|---|
| content | string | Required | Message content, max 2000 chars |
| parent_id | integer | Optional | ID of the message being replied to. Omit for a normal message. Unknown or deleted parent ids are ignored and the message posts normally. Alias: reply_to. |
Delete message
Moderator only.
Mute / Unmute participant
Moderator only. POST accepts optional reason in body.
Muted list
Moderator only. Returns list of muted participants with details.
Support Tickets
Create, view, reply to, and manage support tickets. All endpoints require authentication. Staff can see all tickets and manage properties.
List tickets
Regular users see only their own tickets; staff sees all. Returns tickets, total, is_staff.
Create ticket
| Parameter | Type | Required | Description |
|---|---|---|---|
| subject | string | Required | Ticket subject |
| body | string | Required | Ticket message (also accepts message) |
| category | string | Optional | Ticket category |
| priority | string | Optional | Ticket priority |
View ticket
Returns full ticket with message history. Marks as read for the viewer.
Reply to ticket
| Parameter | Type | Required | Description |
|---|---|---|---|
| body | string | Required | Reply content (also accepts message) |
Close ticket
Update ticket (staff)
| Parameter | Type | Required | Description |
|---|---|---|---|
| priority | string | Optional | New priority |
| category | string | Optional | New category |
| status | string | Optional | open, waiting, closed, resolved |
| assigned_to | string | Optional | Assign to staff member |
Reopen ticket (staff)
Reports
Submit and manage content reports. Moderator role required for viewing/updating reports.
Submit report
| Parameter | Type | Required | Description |
|---|---|---|---|
| content_type | string | Required | thread, reply, chat, or user |
| content_id | string | Required | ID of reported content |
| reason | string | Required | spam, harassment, inappropriate, misinformation, other |
| details | string | Optional | Additional details, max 1000 chars |
List reports (moderator)
Query: status, limit (max 100), page.
Update report (moderator)
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | Required | pending, reviewed, resolved, dismissed |
| resolution_note | string | Optional | Resolution note, max 500 chars |
Verify License Key
Check whether a license key exists and whether it has been redeemed. Optionally returns detailed key info.
X-API-Key header set to your Admin Key (pa_...). Alternatively, use Authorization: Bearer pa_....| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Required | The license key to verify |
| info | boolean | Optional | If true, returns additional key details (redeemed_by, subscription types, etc.) |
Success response
{
"success": true,
"valid": true,
"redeemed": false,
"info": {
"id": 123456,
"notes": "",
"subscription_types": ["premium"],
"created_at": "2026-01-01T00:00:00Z",
"redeemed_at": null,
"redeemed_by": null,
"redeemed_by_username": null,
"grant_duration_unit": "days",
"grant_duration_value": 30
}
}Error responses
Missing required parameter: key.
Missing or invalid API key.
Generate License Keys
Generate one or more license keys for the application. Supports custom patterns, subscription types, and duration settings.
| Parameter | Type | Required | Description |
|---|---|---|---|
| count | integer | Optional | Number of keys to generate (1–100, default: 1) |
| subscription_types | array | Optional | List of subscription type names to grant on redemption |
| duration_type | string | Optional | days, hours, minutes, seconds, or lifetime (default: lifetime) |
| duration_value | number | Conditional | Required when duration_type is not lifetime |
| pattern | string | Optional | Key pattern with * as placeholders (e.g. KEY-****-****) |
| key_case | string | Optional | uppercase, lowercase, or mixed (default: mixed) |
| notes | string | Optional | Notes to attach to the generated keys |
Success response 201
{
"success": true,
"message": "5 keys generated successfully",
"keys": [
{
"id": 987654321,
"key": "KEY-A1B2-C3D4",
"subscription_types": ["premium"],
"grant_duration_unit": "days",
"grant_duration_value": 30,
"created_at": "2026-02-15T12:00:00Z"
}
]
}Get User Info
Retrieve detailed information about a customer by username, including subscription status, ban state, HWID, and variables.
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username (query parameter) |
Success response
{
"success": true,
"user": {
"id": 1,
"username": "johndoe",
"email": "[email protected]",
"created_at": "2026-01-01T00:00:00Z",
"last_login": "2026-02-15T10:00:00Z",
"status": "active",
"banned": false,
"subscription": { /* subscription object */ },
"hwid_state": { /* HWID details */ },
"variables": []
}
}Error responses
Customer not found.
Get User Keys
Retrieve all license keys redeemed by a specific customer.
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username (query parameter) |
Success response
{
"success": true,
"username": "johndoe",
"keys": [
{
"id": 1,
"key": "KEY-XXXX-XXXX",
"subscription_types": ["premium"],
"created_at": "2026-01-01T00:00:00Z",
"redeemed_at": "2026-01-02T00:00:00Z"
}
],
"total": 1
}Get All User Variables
Retrieve user variables for every customer in the application. Returns each customer with their effective variable values (customer-specific or default).
No parameters required beyond authentication.
Success response
{
"success": true,
"customers": [
{
"customer_id": 1,
"username": "johndoe",
"email": "[email protected]",
"variables": [
{ "id": "abc123", "name": "score", "value": "100", "default_value": "0" }
]
}
],
"total_customers": 1,
"variable_definitions_count": 1
}Set Global Variable
Replace the value of a global variable, identified by name or id.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Global variable name or id |
| value | string | Required | New value (objects/arrays are JSON-serialized) |
Success response
{
"success": true,
"message": "Variable updated successfully",
"variable": { "id": 123, "name": "welcome_message", "value": "Hi there", "updated_at": "2026-06-23T12:00:00" }
}Errors: 404 if the variable is not found.
Set User Variable
Directly set a specific customer's user variable value (admin override). The variable must already exist as a user-variable definition. If the customer has no value yet for that definition, one is created.
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username |
| name | string | Required | User-variable definition name or id |
| value | string | Required | New value (objects/arrays are JSON-serialized) |
Success response
{
"success": true,
"message": "User variable updated successfully",
"customer_id": 1,
"username": "johndoe",
"variable": { "id": "abc123", "name": "score", "value": "500" }
}Errors: 404 if the customer or the user-variable definition is not found.
Set Data Variable
Replace the entire value of a data variable, identified by name or id. Maximum value size is 10MB.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Data variable name or id |
| value | string | Required | New value (objects/arrays are JSON-serialized) |
Success response
{
"success": true,
"message": "Data variable updated successfully",
"variable": { "id": 456, "name": "download_maps", "value": "{...}", "updated_at": "2026-06-23T12:00:00" }
}Errors: 404 if the data variable is not found; 400 if the value exceeds 10MB.
Ban User
Ban a customer by username. Banned users cannot log in or use the application.
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username |
| reason | string | Optional | Ban reason |
Success response
{
"success": true,
"message": "User banned successfully.",
"customer": {
"id": 1,
"username": "johndoe",
"banned": true,
"ban_reason": "Violation of terms"
}
}Unban User
Remove a ban from a customer by username.
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username |
Success response
{
"success": true,
"message": "User unbanned successfully.",
"customer": {
"id": 1,
"username": "johndoe",
"banned": false
}
}Reset User Password
Reset a customer's password. The new password must be at least 6 characters.
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username |
| new_password | string | Required | New password (min 6 characters) |
Success response
{
"success": true,
"message": "Password reset successful.",
"customer": {
"id": 1,
"username": "johndoe"
}
}Reset User HWID
Reset the hardware ID (HWID) for a specific customer, allowing them to log in from a new device.
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username |
Success response
{
"success": true,
"message": "HWID reset successful.",
"customer": {
"id": 1,
"username": "johndoe"
},
"hwid_state": { /* updated HWID details */ }
}Reset All HWIDs
Reset hardware IDs for all customers in the application. Only resets customers who currently have an HWID set.
No parameters required beyond authentication.
Success response
{
"success": true,
"message": "HWID reset for 42 customers.",
"reset_count": 42,
"total_customers": 100
}Get Subscription Types
Retrieve all subscription types defined for the application.
No parameters required beyond authentication.
Success response
{
"success": true,
"subscriptions": [
{ "name": "premium", "id": 1 },
{ "name": "vip", "id": 2 }
],
"total": 2
}Extend User Subscription
Grant or extend a subscription for a customer. If the customer already has the subscription, the duration is added to the existing expiry.
| Parameter | Type | Required | Description |
|---|---|---|---|
| username | string | Required | Customer username |
| subscription | string | Required | Subscription name to grant/extend |
| duration_type | string | Optional | days, hours, minutes, seconds, or lifetime (default: days) |
| duration_value | number | Conditional | Required when duration_type is not lifetime |
Success response
{
"success": true,
"message": "Subscription extended successfully.",
"customer": {
"id": 1,
"username": "johndoe",
"subscription_types": ["premium"]
},
"grant": { /* grant record details */ }
}Validate Access Key
Validate an access key from a trusted server using X-API-Key and Idempotency-Key.
Body fields are key, trusted end-user ip, and hwid. Success returns the authorized subscription_hashes.