Documentation Index
Fetch the complete documentation index at: https://docs.authforge.cc/llms.txt
Use this file to discover all available pages before exploring further.
The Developer API lets you integrate AuthForge with any payment provider, CRM, or internal tool. This guide covers the patterns for building your own integration.
Prerequisites
- An AuthForge account with an app created
- A Developer API key
- A backend that can make HTTP requests
Authentication
All API requests require your API key in the Authorization header:
Authorization: Bearer af_live_your_key_here
Common patterns
Generate a license after payment
The most common integration: create a license when a customer pays, deliver the key.
async function generateLicense({ appId, email, plan, orderId }) {
// Create the license
const response = await fetch("https://api.authforge.cc/v1/licenses", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTHFORGE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
appId,
quantity: 1,
maxHwidSlots: plan === "enterprise" ? 5 : 1,
label: orderId,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`AuthForge API error: ${error.error}; ${error.message}`);
}
const { licenses } = await response.json();
const licenseKey = licenses[0].licenseKey;
// Optionally set license variables for the plan tier
if (plan !== "basic") {
await setLicenseVariables(licenseKey, plan);
}
// Deliver to customer
await sendEmail(email, licenseKey);
return licenseKey;
}
Bulk license generation
Generate multiple keys at once (up to 100 per request):
async function generateBulkLicenses(appId, quantity, options = {}) {
const response = await fetch("https://api.authforge.cc/v1/licenses", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTHFORGE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
appId,
quantity,
expiresAt: options.expiresAt || null,
maxHwidSlots: options.maxHwidSlots || 1,
label: options.label,
}),
});
const { licenses } = await response.json();
return licenses.map((l) => l.licenseKey);
}
// Generate 50 keys for a reseller
const keys = await generateBulkLicenses("your-app-id", 50, {
expiresAt: "2027-01-01T00:00:00Z",
label: "Reseller batch #42",
});
License lookup and status check
Check a license’s status before taking action:
async function checkLicense(licenseKey) {
const response = await fetch(
`https://api.authforge.cc/v1/licenses/${licenseKey}`,
{
headers: {
Authorization: `Bearer ${process.env.AUTHFORGE_API_KEY}`,
},
}
);
if (!response.ok) {
if (response.status === 404) return null;
throw new Error("API error");
}
return await response.json();
}
// Usage
const license = await checkLicense("A3K9-BFWX-7NP2-QHDT");
if (license) {
console.log(`Status: ${license.status}`);
console.log(`Expires: ${license.expiresAt || "never"}`);
console.log(`Devices: ${license.hwidList.length}/${license.maxHwidSlots}`);
}
Customer support: HWID reset
When a customer gets a new machine:
async function resetHwid(licenseKey) {
const response = await fetch(
`https://api.authforge.cc/v1/licenses/${licenseKey}`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.AUTHFORGE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ action: "reset-hwid" }),
}
);
return response.ok;
}
async function getAllLicenses(appId) {
const allLicenses = [];
let cursor = null;
do {
const url = new URL("https://api.authforge.cc/v1/licenses");
url.searchParams.set("appId", appId);
url.searchParams.set("limit", "200");
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url.toString(), {
headers: {
Authorization: `Bearer ${process.env.AUTHFORGE_API_KEY}`,
},
});
const data = await response.json();
allLicenses.push(...data.licenses);
cursor = data.cursor;
} while (cursor);
return allLicenses;
}
Error handling
Always handle API errors gracefully:
async function safeApiCall(url, options) {
const response = await fetch(url, options);
if (!response.ok) {
const body = await response.json().catch(() => ({}));
switch (body.error) {
case "no_credits":
console.error("Out of credits; purchase more in the dashboard");
break;
case "rate_limited":
// Retry with exponential backoff
await sleep(1000);
return safeApiCall(url, options);
case "forbidden":
console.error("App doesn't belong to this account");
break;
default:
console.error(`API error: ${body.error}; ${body.message}`);
}
throw new Error(body.error || "unknown_error");
}
return response.json();
}
Webhooks for event-driven workflows
Instead of polling, use webhooks to react to license events:
app.post("/webhooks/authforge", express.raw({ type: "application/json" }), (req, res) => {
// Verify signature (see webhooks docs)
const event = JSON.parse(req.body);
switch (event.event) {
case "license.validated":
updateLastSeen(event.data.licenseKey);
break;
case "license.hwid_bound":
logNewDevice(event.data.licenseKey, event.data.hwid);
break;
}
res.sendStatus(200);
});
Language examples
Python
import requests
API_KEY = os.environ["AUTHFORGE_API_KEY"]
BASE_URL = "https://api.authforge.cc/v1"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
def create_license(app_id, **kwargs):
response = requests.post(
f"{BASE_URL}/licenses",
headers=HEADERS,
json={"appId": app_id, **kwargs},
)
response.raise_for_status()
return response.json()["licenses"]
func createLicense(appID string, quantity int) ([]License, error) {
body, _ := json.Marshal(map[string]interface{}{
"appId": appID,
"quantity": quantity,
})
req, _ := http.NewRequest("POST", "https://api.authforge.cc/v1/licenses", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("AUTHFORGE_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result struct {
Licenses []License `json:"licenses"`
}
json.NewDecoder(resp.Body).Decode(&result)
return result.Licenses, nil
}
Next steps