Febasidocs
Reference

Errors

The error envelope, every error code Febasi Auth returns, and what each one means in practice.

Every Auth response follows the same envelope:

{ "success": true,  "data":  { "..." : "..." } }
{ "success": false, "error": "Human-readable message", "code": "MACHINE_CODE" }

Validation failures (Zod) include a details array describing each invalid field:

{
  "success": false,
  "error": "Validation failed",
  "code": "VALIDATION_ERROR",
  "details": [
    { "path": ["password"], "message": "String must contain at least 6 character(s)" }
  ]
}

The HTTP status code is meaningful — match on code for fine-grained behavior, on the status code for coarse routing.

Auth & sessions (401)

CodeWhen
UNAUTHORIZEDNo Authorization header / no Client Key on a protected route.
INVALID_CREDENTIALSWrong password, missing user, or INACTIVE user (deliberately ambiguous).
INVALID_TOKENToken signature invalid, malformed, or revoked.
TOKEN_EXPIREDAccess token's exp is in the past.
MISSING_API_KEYA dual-auth route was hit with neither a JWT nor an X-API-Key.
INVALID_API_KEYThe provided X-API-Key is unknown, revoked, or expired.
MISSING_TENANT_CONTEXTThe handler could not resolve a tenant — usually a Client Key call missing the X-Tenant-Code header.

Authorization (403)

CodeWhen
FORBIDDENJWT principal lacks the required permission.
INSUFFICIENT_SCOPEClient Key principal lacks the required scope, or scope-vs-permission mismatch on dual-auth routes.
TENANT_ACCESS_DENIEDA Client Key tried to operate against a tenant it is not authorized for (per tenantAccessLevel / allowedTenantIds).
IP_NOT_ALLOWEDSource IP is not in the configured tenant or per-key allowlist. Carries the failing IP in the message. See IP allowlist.
HIERARCHY_VIOLATIONTrying to manage a role/user at or above the actor's level.
PROTECTED_RESOURCETrying to modify a system role (e.g. super_admin).
CROSS_TENANT_ACCESSThe resource belongs to a different tenant than the caller.

HIERARCHY_VIOLATION includes both actorLevel and targetLevel in details — useful for UI messages.

Resource not found (404)

CodeWhen
TENANT_NOT_FOUNDThe tenantCode (or tenant id) does not resolve.
USER_NOT_FOUNDThe user id does not exist in the caller's tenant.
ROLE_NOT_FOUNDThe role id does not exist.
PERMISSION_NOT_FOUNDThe permission id does not exist.
NOT_FOUNDGeneric "no such resource" — used by token revoke and a few other endpoints when the target id is unknown.

Validation & conflict (400 / 409)

CodeWhen
VALIDATION_ERRORBody / query / params failed schema validation. For IP allowlist updates, details.invalidEntries lists the malformed entries.
IP_LOCKOUT_PREVENTEDA tenant config or Client Key update would leave the caller's current IP outside the proposed allowlist. Pass ?force=true to override (audit-logged). Platform admin:* callers bypass the check.
PASSWORD_POLICY_VIOLATIONPassword fails the tenant's policy on register or update. Includes violations[].
USER_CONTEXT_REQUIREDPOST /permissions/check was called by a Client Key with no user context. The endpoint needs a JWT-bound user to check.
CONFLICTDuplicate identifier (email, username, CPF/CNPJ already taken in this tenant).

Rate / capacity (429)

CodeWhen
RATE_LIMIT_EXCEEDEDThe route's per-IP rate limit is exhausted.
SESSION_LIMIT_EXCEEDEDThe user hit sessionLimits.maxConcurrentSessions on a tenant configured with enforceLogout: false. Includes currentSessions and maxSessions.

Both share status 429 but mean different things. RATE_LIMIT_EXCEEDED is recoverable by waiting Retry-After; SESSION_LIMIT_EXCEEDED requires ending another session first — retrying without that will fail again regardless of Retry-After.

Server-side (500)

CodeWhen
INTERNAL_ERRORCatch-all for unexpected exceptions. The response body never leaks stack traces in production.

If you see INTERNAL_ERROR, capture the timestamp and tenant code; the service writes a structured Pino log line for every one of them and they're correlated by request id.

Treat code, not message

Messages are intentionally human-friendly and may change. The code field is the stable contract.

401 → refresh, not relogin

On TOKEN_EXPIRED, refresh first; on INVALID_TOKEN or INVALID_CREDENTIALS, force a relogin.

403 → check the code

FORBIDDEN means "you don't have the permission". HIERARCHY_VIOLATION means "the action would violate the level rule" — the user might still fix it by escalating, instead of giving up.

409 is recoverable

CONFLICT is the user-friendly path: surface "already taken" to the user instead of a generic error.

On this page