Lumen Docs

API overview

Hono on Bun, base URL https://lumen-api.zenmail.my.id. Auth is JWT in the Authorization header. Responses are JSON. Chat is SSE.

Auth header

All protected endpoints require:

Authorization: Bearer <access-token>

Access tokens last 15 minutes. The web client auto-refreshes on any 401 using the refresh token (7-day lifetime). If the refresh fails, the client clears tokens and redirects to /login.

Response shapes

Success

{
  "users": [...],          // resource-named key
  "total": 42
}

or

{
  "user": { ... },
  "accessToken": "...",
  "refreshToken": "..."
}

Error

Two shapes depending on source:

Handler-thrown (400/401/403/404/409/500):

{ "error": "<code>", "message": "<human text>" }

Zod validation failure (400):

{
  "success": false,
  "error": {
    "issues": [
      { "path": ["email"], "message": "Invalid email", "code": "invalid_string" }
    ],
    "name": "ZodError"
  }
}

The web client's api.ts extracts a readable string for both shapes:

  • Prefer message field
  • Fall back to error if it's a string
  • For Zod errors, format as "<path>: <message>" (e.g., email: Invalid email)

Never surface [object Object] to the user — that's a bug in the error extractor.

Core endpoint groups

| Prefix | Purpose | Auth | |---|---|---| | /auth/* | Login, refresh, logout, me | Public (login/refresh) or JWT | | /bootstrap/* | First-run wizard | Public | | /users/* | Users CRUD | admin/superadmin | | /departments/* | Departments + members + grants | admin/superadmin + CEO/manager for own dept | | /groups/* | Groups under department | admin/superadmin + dept manager | | /projects/* | Projects CRUD + grants + members | resolver-based | | /projects/:id/grants | Create/update/delete grant | full tier on project | | /documents/* | Upload, list, delete | resolver-based (tier varies) | | /chat/* | SSE chat, conversations, messages | resolver-based (use tier) | | /share-requests/* | Request flow | admin/superadmin | | /share/:token | Guest share page | Public | | /audit-log | Admin audit query | admin/superadmin | | /providers/* | LLM provider config | engineer/superadmin (write), any auth (read /models) | | /memories/* | Project memory | resolver-based |

Rate limits

Not enforced currently. Dev phase, single tenant. Production will need:

  • /auth/login — 10 req/min per IP
  • /bootstrap/init — 3 req/min per IP
  • /chat/* SSE — connection cap per user
  • /documents/* POST — 50MB per file, 100 files/hour per user

CORS

Web origin (https://ai-kb.zenmail.my.id) is whitelisted. Preflight OPTIONS is auto-handled by Hono.

OpenAPI

Not auto-generated. This doc is the source of truth. When you add an endpoint:

  1. Add it to apps/api/src/routes/<area>.ts
  2. Register it in apps/api/src/index.ts if it's a new route file
  3. Document it in the appropriate /engineering/api/* page
  4. Add integration test if the endpoint has auth/permission gates

Observability

  • Access log: docker logs lumen-api --follow
  • Slow queries: Prisma $queryRaw has console.log wrappers for the vector search only
  • Errors: console.error with stack — piped through Dokploy log retention

No structured logging / tracing yet. When we outgrow single-tenant dev, add OpenTelemetry via Hono middleware.