Errors

AIUS returns standard HTTP status codes. Error bodies use FastAPI’s detail envelope. There are two shapes you’ll encounter.

Structured errors

Account-side endpoints (auth, 2FA, clients, projects) return a structured detail object with a stable code and a human message:
{
  "detail": {
    "code": "INVALID_CREDENTIALS",
    "message": "Invalid email or password"
  }
}
Always branch on detail.code (stable), not detail.message (may change).
r = httpx.post("https://aius.co/api/v1/login", json=creds)
if r.status_code != 200:
    err = r.json().get("detail", {})
    code = err.get("code") if isinstance(err, dict) else None
    raise RuntimeError(f"login failed: {code}{r.status_code}")

String errors

Some gateway-level checks (bearer validation, token minting, upstream availability) return detail as a plain string:
{ "detail": "invalid token" }
A robust client should handle both:
detail = r.json().get("detail")
code = detail.get("code") if isinstance(detail, dict) else None
message = detail.get("message") if isinstance(detail, dict) else detail

HTTP status codes

CodeMeaning
200Success
400Bad request — missing/invalid field
401Unauthorized — missing/invalid credential or session
403Forbidden — authenticated but no access to the resource
404Not found
409Conflict — e.g. email already exists, 2FA already enabled
429Rate limited — back off and retry
500Internal error
503Upstream/account service unavailable

Common codes

Authentication & registration

CodeHTTPMeaning
MISSING_NAME / MISSING_EMAIL / MISSING_PASSWORD400A required field was empty
INVALID_EMAIL400Malformed email
PASSWORD_TOO_WEAK400Min 8 chars, with a letter, number, and special character
EMAIL_ALREADY_EXISTS409An account with this email already exists
INVALID_CREDENTIALS401Wrong email or password
REGISTRATION_FAILED500Unexpected failure during registration

Two-factor

CodeHTTPMeaning
INVALID_CHALLENGE401Expired/invalid 2FA challenge token
INVALID_CODE400 / 401Wrong TOTP or recovery code
2FA_ALREADY_ENABLED4092FA is already on for this account
2FA_NOT_STARTED400Call /v1/2fa/setup before /v1/2fa/enable
2FA_NOT_ENABLED4002FA is not enabled for this account

Bearer / tokens

Detail (string)HTTPMeaning
missing bearer token401No Authorization header
invalid authorization header401Header isn’t Bearer <token>
invalid bearer token401Empty token
invalid token401Unknown or revoked aius_… token
token_name is required400POST /v1/tokens needs token_name
no session cookie401POST /v1/tokens needs the __Host-aius_session cookie

Clients & projects

CodeHTTPMeaning
NO_ACCESS403You don’t belong to that client
NOT_FOUND404Project/resource doesn’t exist
USER_NOT_FOUND404Identity not resolvable
MISSING_NAME400Project/client name required

Rate limiting

429 responses indicate you’ve exceeded a per-IP limit (see Overview › Rate limits). Use exponential backoff:
import time, httpx

def with_retry(send, attempts=4):
    for i in range(attempts):
        r = send()
        if r.status_code != 429:
            return r
        time.sleep(2 ** i)
    return r

Service availability

503 with detail like Failed to reach … service means an upstream account service is unreachable. Retry after a short delay.