App Developer Docs
API Reference
All routes are prefixed with /api. Auth routes live at /api/auth/*. The chat route is at /api/chat.
Requests and responses use application/json. Cookies are sent automatically by the browser (withCredentials: true in the axios client).
Auth routes
POST /api/auth/request-magic-link
Request a magic sign-in link. Creates the user if the email is new.
Request body:
{ "email": "you@example.com" }
Response 200:
{ "ok": true }
Always returns 200 — even if the user doesn’t exist. This prevents email enumeration. In development, the link is printed to the server terminal instead of sent by email.
Errors: 400 if email is missing.
GET /api/auth/magic?token=<token>
Validates a magic link token and redirects.
- New user (
needs_password_setup = 1): redirects to/#/setup-password?token=<token> - Returning user: burns token, creates session, sets cookie, redirects to
/#/chat - Invalid/expired token: redirects to
/#/login?error=expired - Missing token param: redirects to
/#/login?error=missing-token
POST /api/auth/setup-password
Complete first-time password setup. Requires a valid magic token.
Request body:
{
"token": "abc123...",
"password": "atleast8chars"
}
Response 200:
{
"id": 1,
"email": "you@example.com",
"display_name": "you@example.com",
"needs_password_setup": false,
"theme": "dark"
}
Sets session_token cookie. Burns the magic token.
Errors: 400 for invalid token, password shorter than 8 characters, or password already set.
POST /api/auth/login
Sign in with email and password.
Request body:
{ "email": "you@example.com", "password": "yourpassword" }
Response 200:
{
"id": 1,
"email": "you@example.com",
"display_name": "you@example.com",
"needs_password_setup": false,
"theme": "dark"
}
Sets session_token cookie.
Errors: 401 for wrong password or unknown email. 400 if fields are missing.
POST /api/auth/logout
End the current session.
Response 200:
{ "ok": true }
Deletes the session row and clears the cookie. Safe to call even if there’s no active session.
GET /api/auth/me
Return the currently authenticated user.
Response 200:
{
"id": 1,
"email": "you@example.com",
"display_name": "you@example.com",
"needs_password_setup": false,
"theme": "dark"
}
Errors: 401 if not authenticated or session expired.
Chat route
POST /api/chat
Send a message to the AI. Requires authentication.
Request body:
{
"messages": [
{ "role": "user", "content": "I feel like I should clean but I'm not sure where to start." }
]
}
Send the full conversation history on every request — the server is stateless; it prepends the system prompt and forwards all messages to OpenAI.
Response 200:
{ "reply": "Given where you are right now…" }
Errors: 401 if not authenticated. 400 if messages is empty. 500 if the OpenAI call fails (check server logs — usually a bad API key or wrong model name).
Conversation format
The frontend accumulates messages in component state (not persisted). Each send() call sends the full history. The backend always adds the system prompt at position 0 before forwarding to OpenAI:
[system prompt] + [all user/assistant messages]
The model and system prompt are defined in backend/src/routes/chat.js. The system prompt describes the 8 dimensions and instructs the model to use state-check context when present.