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

config
API_URL = "http://localhost:3000"        # your AstraAuth server
PRODUCT = "Default App"                  # the exact product name
VERSION = "1.0.0"                        # your software version

3. 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.

Nothing to install. Your client only needs to make HTTP requests. The examples in each language use only the standard library or minimal dependencies.

API reference

Three public endpoints for your software. All accept JSON and respond with JSON.

Endpoints

MethodPathDescription
POST/api/initProduct info and optional key status
POST/api/activateActivates the key and binds the HWID
POST/api/checkVerifies the license on every start
POST/api/useRegisters a custom event (optional)

Request body

json
{
  "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

json
{
  "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

ErrorMeaning
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

python
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

python
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 data

JavaScript

Uses fetch (Node 18+ or browser). Full code in clients/example.js.

Login / activate

javascript
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

javascript
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

typescript
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

lua
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)
end

Check + premium

lua
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
end

C#

Uses HttpClient and System.Text.Json. Full code in clients/example.cs.

Login / activate

csharp
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

cpp
#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

rust
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

java
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/premium

Go

Uses net/http and encoding/json. Full code in clients/example.go.

Login / activate

go
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)

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()
The HWID is not tamper-proof by itself. Combine it with obfuscation and, if needed, an anti-tamper service in your software.

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 URL in the dashboard, or with /logs webhook <url>.
  • Bot channel: use /logs set <#channel> so the bot sends events to a channel.
  • Test: /logs test or the Send test embed button in the dashboard.

What each embed records

EventEmbed fields
Key createdKey, tier, expiration, activations, variables, who created it
Key activationKey, tier, result, reason, HWID, IP, Discord owner
Key banned / unbannedKey, reason, who did it
Premium grantedKey, duration, deadline, who granted it
User loginUsername, Discord ID, email, IP, server auto-join
User bannedDiscord ID, IP, reason, who banned it