Base URL
/v1/.
Authentication
Every request must include your API key in theAuthorization header as a Bearer token:
af_live_ for identification.
Each key is scoped to your account and can manage licenses across all of your applications; the target app is specified per-request via the appId field or URL parameter.
Rate limits
If you exceed a rate limit, you’ll receive
HTTP 429 with the Developer API error shape (error + message) and a Retry-After header. Back off and retry with exponential backoff.
Public SDK endpoints on auth.authforge.cc (/auth/validate, /auth/heartbeat, /auth/selfban, /auth/offline/activate) use different limits and a different JSON body on failure: { "status": "failed", "error": "rate_limited" }. /auth/validate has extra per-IP and per-license application limits; /auth/heartbeat has a per-license limit (6/min, applied per device for unlimited-seat licenses) but no per-IP limit; /auth/offline/activate allows 10/min per IP and 3/min per license key. See Error Codes Reference.
Credits
License management calls (create, list, update, delete, variables, webhooks, security) do not consume credits. Two things do: SDK authentication (1 credit per successful validate, 1 per 10 heartbeats) and minting an offline license file viaPOST /v1/licenses/{licenseKey}/offline-files (1 credit per successful mint, debited from the app owner’s balance; rejected mints are free). Self-serve offline activation costs 2 credits per new machine (one validation plus one mint), charged to the app owner when the end user’s app calls POST /auth/offline/activate; activating the same machine again and every refusal are free. A refund or chargeback revokes the license online but cannot reach files already issued. For a lifetime file, that means the buyer keeps the app. See Managing Credits for details.
Response format
All responses return JSON. Successful responses include the requested data directly:error code and a human-readable message:
Pagination
List endpoints use cursor-based pagination. If more results are available, the response includes acursor value. Pass it as a query parameter in the next request:
cursor is null, you’ve reached the last page.