Skip to main content

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;
}

Pagination: iterate all licenses

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"]

Go

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