Quickstart
Integrate AstraAuth into your software in five minutes. You only need a URL and a few HTTP calls.
1. Get your product
Sign in to the dashboard and open Products. Each product has a name you will use as an identifier. The default product is called Default App.
2. Configure the API URL
API_URL = "http://localhost:3000" # your AstraAuth server
PRODUCT = "Default App" # the exact product name
VERSION = "1.0.0" # your software version3. Login / activate
Send key + hwid to POST /api/activate. On the first call the key is bound to that hardware.
4. Check on every start
Every time your software opens, call POST /api/check with the same key and hwid. If the key is banned, expired, or linked to another PC, you will receive success: false.
API reference
Three public endpoints for your software. All accept JSON and respond with JSON.
Endpoints
| Method | Path | Description |
|---|---|---|
POST | /api/init | Product info and optional key status |
POST | /api/activate | Activates the key and binds the HWID |
POST | /api/check | Verifies the license on every start |
POST | /api/use | Registers a custom event (optional) |
Request body
{
"key": "ASTR-9F3K-77L2-QZX1", // the license key
"hwid": "sha256...", // hardware hash (see HWID guide)
"product": "Default App", // product name
"version": "1.0.0" // client version
}Response shape
{
"success": true,
"key": "ASTR-9F3K-77L2-QZX1",
"tier": "premium", // standard | premium | lifetime
"premium": true, // premium active?
"expires_at": "2026-12-01T00:00:00.000Z",
"max_activations": 3,
"variables": { "skin": "gold", "level": 100 }, // key variables
"status": "active"
}Error codes
| Error | Meaning |
|---|---|
Invalid license key. | The key does not exist |
This key has been banned. | Key banned by an admin |
This license key has expired. | The key passed its expiration date |
License not activated yet. | The key was not activated yet (use /activate) |
Key is linked to another computer. | HWID mismatch or activations exhausted |
Rate limit exceeded. | Too many requests per minute |
Python
Uses requests. Full code in clients/example.py.
Login / activate
import requests, hashlib, platform
API = "http://localhost:3000"
PRODUCT = "Default App"
def get_hwid() -> str:
raw = platform.node() + "|" + platform.machine() + "|" + platform.system() + platform.release()
return hashlib.sha256(raw.encode()).hexdigest()
def activate(key: str) -> dict:
r = requests.post(f"{API}/api/activate", json={
"key": key, "hwid": get_hwid(),
"product": PRODUCT, "version": "1.0.0"
}, timeout=10)
return r.json()Check
def check(key: str) -> dict:
r = requests.post(f"{API}/api/check", json={
"key": key, "hwid": get_hwid(), "product": PRODUCT
}, timeout=10)
data = r.json()
if data.get("success"):
if data.get("premium"):
print("[+] premium active")
if data.get("variables"):
print("[+] variables:", data["variables"])
else:
print("[!] access denied:", data.get("error"))
exit(1)
return dataJavaScript
Uses fetch (Node 18+ or browser). Full code in clients/example.js.
Login / activate
const API = "http://localhost:3000";
const PRODUCT = "Default App";
const crypto = require("crypto");
function getHwid() {
return crypto.createHash("sha256")
.update(process.env.COMPUTERNAME + process.arch)
.digest("hex");
}
async function activate(key) {
const res = await fetch(API + "/api/activate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key, hwid: getHwid(), product: PRODUCT, version: "1.0.0" })
});
return res.json();
}Check
async function check(key) {
const res = await fetch(API + "/api/check", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key, hwid: getHwid(), product: PRODUCT })
});
const data = await res.json();
if (!data.success) {
console.error("access denied:", data.error);
process.exit(1);
}
console.log("tier:", data.tier, "| premium:", data.premium, "| vars:", data.variables);
return data;
}TypeScript
Same as JavaScript but with types. Full code in clients/example.ts.
Types + activate
interface License {
success: boolean;
tier: "standard" | "premium" | "lifetime";
premium: boolean;
expires_at: string | null;
variables: Record<string, string>;
error?: string;
}
const API = "http://localhost:3000";
const PRODUCT = "Default App";
async function activate(key: string, hwid: string): Promise<License> {
const res = await fetch(API + "/api/activate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key, hwid, product: PRODUCT, version: "1.0.0" })
});
return res.json() as Promise<License>;
}Lua
Uses socket.http and dkjson. Full code in clients/example.lua.
Login / activate
local http = require("socket.http")
local json = require("dkjson")
local API = "http://localhost:3000"
local PRODUCT = "Default App"
local function hwid()
-- replace with a stable hash of your machine
return "machine-hwid-hash"
end
local function activate(key)
local body = json.encode({
key = key, hwid = hwid(), product = PRODUCT, version = "1.0.0"
})
local resp, status = http.request(API .. "/api/activate", body)
if not resp then return { success = false, error = "network" } end
return json.decode(resp)
endCheck + premium
local function check(key)
local body = json.encode({ key = key, hwid = hwid(), product = PRODUCT })
local resp = http.request(API .. "/api/check", body)
local data = json.decode(resp)
if not data.success then
print("access denied: " .. data.error)
os.exit(1)
end
if data.premium then print("premium active") end
return data
endC#
Uses HttpClient and System.Text.Json. Full code in clients/example.cs.
Login / activate
using System.Net.Http;
using System.Text;
using System.Text.Json;
var client = new HttpClient();
const string API = "http://localhost:3000";
var body = JsonSerializer.Serialize(new {
key = key, hwid = GetHwid(), product = "Default App", version = "1.0.0"
});
var res = await client.PostAsync(API + "/api/activate",
new StringContent(body, Encoding.UTF8, "application/json"));
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
var root = doc.RootElement;
bool ok = root.GetProperty("success").GetBoolean();
string tier = ok ? root.GetProperty("tier").GetString() : "";C++
Uses cpp-httplib. Full code in clients/example.cpp.
Login / activate
#include <httplib.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
std::string activate(const std::string& key, const std::string& hwid) {
httplib::Client cli("http://localhost:3000");
json payload = {
{"key", key}, {"hwid", hwid},
{"product", "Default App"}, {"version", "1.0.0"}
};
auto res = cli.Post("/api/activate", payload.dump(), "application/json");
if (!res || res->status != 200) return "{}";
return res->body;
}Rust
Uses reqwest and serde. Full code in clients/example.rs.
Login / activate
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
struct Login<'a> {
key: &'a str, hwid: &'a str, product: &'a str, version: &'a str,
}
#[derive(Deserialize)]
struct License {
success: bool,
tier: Option<String>,
premium: Option<bool>,
variables: Option<serde_json::Value>,
error: Option<String>,
}
async fn activate(key: &str, hwid: &str) -> anyhow::Result<License> {
let res = reqwest::Client::new()
.post("http://localhost:3000/api/activate")
.json(&Login { key, hwid, product: "Default App", version: "1.0.0" })
.send().await?
.json::<License>().await?;
Ok(res)
}Java
Uses java.net.http (Java 11+). Full code in clients/example.java.
Login / activate
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
String json = "{\"key\":\"" + key + "\",\"hwid\":\"" + hwid +
"\",\"product\":\"Default App\",\"version\":\"1.0.0\"}";
HttpRequest req = HttpRequest.newBuilder(URI.create("http://localhost:3000/api/activate"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body()); // json with success/tier/premiumGo
Uses net/http and encoding/json. Full code in clients/example.go.
Login / activate
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type License struct {
Success bool `json:"success"`
Tier string `json:"tier"`
Premium bool `json:"premium"`
Variables map[string]string `json:"variables"`
Error string `json:"error"`
}
func Activate(key, hwid string) (*License, error) {
body, _ := json.Marshal(map[string]string{
"key": key, "hwid": hwid, "product": "Default App", "version": "1.0.0",
})
resp, err := http.Post("http://localhost:3000/api/activate",
"application/json", bytes.NewReader(body))
if err != nil { return nil, err }
defer resp.Body.Close()
var lic License
if err := json.NewDecoder(resp.Body).Decode(&lic); err != nil { return nil, err }
return &lic, nil
}HWID generation
The HWID is a unique per-machine hash that prevents key sharing. It must be stable across reboots but different between machines.
Recommendations
- Combine multiple sources: main interface MAC, machine name, CPU ID, disk serial.
- Compute a SHA-256 of the concatenation.
- Never send raw hardware data, only the hash.
- If the software runs on WSL/VM, consider including the hypervisor in the hash.
Example (Python)
import hashlib, uuid, platform, subprocess
def get_hwid() -> str:
parts = []
parts.append(str(uuid.UUID(int=uuid.getnode()))) # MAC
parts.append(platform.processor() or platform.machine())
parts.append(platform.system() + platform.release())
if platform.system() == "Windows":
out = subprocess.check_output("vol C:", shell=True).decode()
parts.append(out.strip().split()[-1]) # disk serial
raw = "|".join(filter(None, parts))
return hashlib.sha256(raw.encode()).hexdigest()Logs & webhooks
AstraAuth records every event and can send it to Discord as an embed.
How logs work
Every activation, check, ban, reset and login is stored with: IP, HWID, Discord ID (if present), software version and reason. You see them in the dashboard in real time.
Configuration
- Webhook: add the URL in
Settings → Log webhook URLin the dashboard, or with/logs webhook <url>. - Bot channel: use
/logs set <#channel>so the bot sends events to a channel. - Test:
/logs testor the Send test embed button in the dashboard.
What each embed records
| Event | Embed fields |
|---|---|
| Key created | Key, tier, expiration, activations, variables, who created it |
| Key activation | Key, tier, result, reason, HWID, IP, Discord owner |
| Key banned / unbanned | Key, reason, who did it |
| Premium granted | Key, duration, deadline, who granted it |
| User login | Username, Discord ID, email, IP, server auto-join |
| User banned | Discord ID, IP, reason, who banned it |