Skip to main content
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