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.
Developer API errors
All Developer API errors return a JSON object with an error code and a human-readable message:
{
"error": "not_found",
"message": "Resource not found"
}
| HTTP Status | Error Code | Description |
|---|
| 400 | bad_request | Invalid or missing request parameters. Check the request body and query parameters. |
| 401 | invalid_api_key | Missing, malformed, or revoked API key. Verify your Authorization: Bearer header. |
| 402 | no_credits | Insufficient credits to complete the operation. Purchase more credits in the dashboard. |
| 403 | forbidden | The target app or license doesn’t belong to your account. |
| 404 | not_found | The license key, webhook, or endpoint doesn’t exist. |
| 429 | rate_limited | Too many requests. Back off and retry with exponential backoff. Burst limit: 100 req/s, sustained: 50 req/s. |
| 500 | internal_error | Unexpected server error. Retry the request. If persistent, contact support. |
Public auth errors (SDK)
The public auth endpoints (/auth/validate, /auth/heartbeat, and /auth/selfban) return errors as real HTTP status codes with a JSON body of the form {"status": "failed", "error": "<code>"}:
{
"status": "failed",
"error": "invalid_key"
}
The HTTP status code matches the class of failure:
| HTTP Status | Error codes |
|---|
| 400 | bad_request, malformed_request |
| 401 | invalid_app, invalid_key, session_expired, replay_detected |
| 403 | blocked, app_disabled, hwid_mismatch, revoke_requires_session |
| 410 | expired, revoked |
| 429 | rate_limited, no_credits, app_burn_cap_reached |
| 500 | system_error |
Successful responses return 200 with status: "success".
Public auth requests are protected by two layers of throttling:
- API Gateway stage throttling: burst
200, sustained 100 req/s (default for every route on this API: /auth/validate, /auth/heartbeat, and /auth/selfban)
- Application-level per-minute limits on
/auth/validate only:
30/min per IP
5/min per license key
/auth/heartbeat is not IP rate-limited at the application layer. A flood of heartbeats only burns the victim’s credits, so a per-IP limit would add no security value and would interfere with legitimate high-frequency clients (e.g., 1 Hz server-side apps).
Additional application limits on /auth/selfban: 10/min per IP (all self-ban requests), and 3/min per license key for pre-session requests that send licenseKey + app credentials.
When a validate request exceeds either limit, the response is HTTP 429 with:
{
"status": "failed",
"error": "rate_limited"
}
Successful /auth/validate responses include the X-RateLimit-Remaining header (the lower of the remaining IP and per-license validate quotas). /auth/heartbeat success responses do not include this header: application-level rate buckets apply only where documented above. The official SDKs parse both the HTTP status code and the JSON error field, so 429 responses surface as rate_limited automatically; no special handling is needed in your integration.
Validate errors (/auth/validate)
| Error Code | Description | User-facing guidance |
|---|
invalid_app | The App ID or App Secret is incorrect, or the app has been deactivated. | This is a developer configuration error. Show “Authentication failed.” |
app_disabled | The app owner’s account is suspended. | Show “Authentication service temporarily unavailable.” |
invalid_key | The license key doesn’t exist or doesn’t belong to this app. | Show “Invalid license key. Please check and try again.” |
revoked | The license has been revoked. | Show “This license has been deactivated. Contact support.” |
expired | The license has passed its expiration date. | Show “Your license has expired.” with a renewal link if applicable. |
hwid_mismatch | All HWID slots are full and the current device’s HWID doesn’t match any bound device. | Show “This license is already in use on another device. Contact support to reset.” |
no_credits | The app developer’s account has insufficient credits. | Show “Authentication service temporarily unavailable. Please try again later.” Never expose this to end users. |
app_burn_cap_reached | The app hit its configured hourly/daily credit burn cap. | Show “Authentication service temporarily unavailable. Please try again later.” |
blocked | The device’s HWID or IP is blacklisted, or not on a required whitelist. | Show “Authentication failed. Contact support.” |
rate_limited | Too many requests from this IP or license key. Back off and retry after 60 seconds. | Retry with exponential backoff and jitter; wait at least 60 seconds before sustained retries. |
replay_detected | The nonce has already been used. Generate a fresh nonce for each request (SDKs do this automatically). | This is a developer integration error if you are not using an official SDK. |
bad_request | The JSON body parsed successfully but failed schema validation (missing fields, invalid types). Includes a details array. | This is a developer integration error. |
malformed_request | The request body is missing or is not valid JSON. | This is a developer integration error. |
Heartbeat errors (/auth/heartbeat)
| Error Code | Description | User-facing guidance |
|---|
session_expired | The session token is invalid or has expired. The SDK should re-authenticate. | Prompt the user to re-enter their license key or restart the app to log in again. |
revoked | The license has been revoked since the last heartbeat. | Show “This license has been deactivated. Contact support.” |
expired | The license has expired since the last heartbeat. | Show “Your license has expired.” with a renewal link if applicable. |
app_disabled | The app owner’s account has been suspended. | Show “Authentication service temporarily unavailable.” |
no_credits | The app developer’s account has insufficient credits. Returned on the billing milestone (every 10th successful heartbeat). | Show “Authentication service temporarily unavailable. Please try again later.” Never expose this to end users. |
app_burn_cap_reached | The app hit its configured hourly/daily credit burn cap during heartbeat billing. | Show “Authentication service temporarily unavailable. Please try again later.” |
bad_request | The JSON body parsed successfully but failed schema validation (missing fields, invalid types). | This is a developer integration error. |
malformed_request | The request body is missing or is not valid JSON. | This is a developer integration error. |
system_error | An unexpected failure occurred while processing the request (for example, credit deduction). | Show “Authentication service temporarily unavailable.” Retry with backoff. |
rate_limited and replay_detected are never returned from /auth/heartbeat. Heartbeats are not IP rate-limited and do not enforce nonce replay detection; replay protection is provided by the signed, short-lived session token.
Self-ban errors (/auth/selfban)
| Error Code | Description | User-facing guidance |
|---|
revoke_requires_session | A pre-session self-ban request attempted revokeLicense: true. License revoke is only allowed for session-authenticated self-ban requests. | This is a developer integration issue. Retry with revokeLicense: false, or call self-ban after login with sessionToken. |
invalid_app | appId or appSecret is invalid for pre-session self-ban. | Treat as integration/configuration error. |
invalid_key | The provided pre-session licenseKey does not belong to the app. | Treat as integration/configuration error. |
session_expired | sessionToken is missing/invalid/expired for post-session self-ban. | Re-authenticate before requesting self-ban. |
replay_detected | Pre-session nonce was reused. | Always send a fresh nonce (official SDKs do this automatically). |
rate_limited | Too many self-ban requests: 10/min per IP (all modes), 3/min per license key (pre-session requests with licenseKey only). | Back off and retry with jittered exponential backoff. |
expired | License is expired. | Prompt renewal/support workflow. |
revoked | License was already revoked. | Treat as already-locked state. |
app_disabled | App or app owner is suspended/paused. | Show generic service-unavailable messaging. |
system_error | Unexpected server error. | Retry with backoff; alert support if persistent. |
SDK client-side errors
In addition to server errors, the SDK may detect issues locally before or after a server response:
| Error | Description |
|---|
nonce_mismatch | The nonce in the server response doesn’t match the one sent in the request. Possible replay attack. |
signature_mismatch | The Ed25519 signature on the server response is invalid. Possible tampering. |
| HTTP/network errors | Connection timeout, DNS failure, or non-200 HTTP status. The SDK retries internally before failing. |
These errors trigger the onFailure callback with the "login_failed" or "heartbeat_failed" reason.
Best practices for error handling
- Never expose internal error codes to end users. Map them to user-friendly messages.
- Log the actual error code for debugging, but show generic messages to users.
- Retry on transient errors (
no_credits from your account, network errors) with exponential backoff.
- Don’t retry on permanent errors (
invalid_key, revoked, hwid_mismatch); prompt the user to take action instead.
- Handle
no_credits gracefully: this is a billing issue on your end, not the user’s problem.
See SDK Best Practices for detailed error handling guidance with code examples.