API & Authentication
Browsers authenticate with JWTs; machines authenticate with scoped API keys. Plugins declare their own scopes and guard their endpoints.
Routes
All routes live under /api/v1/: /auth/ (register, login, logout, refresh), /user/, /tarif-plans/, /invoices/ and /admin/ (admin-role gated). A plugin mounts its blueprint under its own prefix via get_url_prefix().
JWT auth
POST /api/v1/auth/login
{ "email": "…", "password": "…" } -> { "token": "<jwt>", "user": {…} }
# then send it on every call
Authorization: Bearer <jwt>API keys
Machine clients use a key from the api_key table, sent as X-API-Key. Each key carries a set of scopes; an endpoint is guarded by the scope it requires. Test a key with GET /api/v1/api-keys/health.
A plugin exposing its own scope
# 1. declare the scope on the plugin object
api_scopes = [
{"key": "loopai:posts:create",
"label": "Create CMS posts via LoopAI"},
]
# 2. guard the endpoint with the core decorator
@require_api_key("loopai:posts:create")
def create_post(): ...The key is hashed at rest; the scope set is the only authority over what a key can do.