API Reference · Auth
API Reference

Auth: three routes, one token

Accounts are email + password. Registering or logging in returns a JWT valid for 7 days; send it as a Bearer token to every account-surface route. Passwords are hashed with bcrypt (10 rounds).

POST/auth/registerNo auth

Creates an account and signs it in immediately. Email is trimmed and lowercased.

FieldTypeDescription
email*stringValid email shape, max 320 chars.
password*string8–128 characters. No strength rules beyond length.
name*stringDisplay name, 1–120 characters.
curl
curl -X POST http://localhost/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"dev@example.com","password":"s3cure-passphrase","name":"Dev"}'
201 Created
{
  "user": {
    "id": "cml4k9x2b0000jq8f3h5a1d2e",
    "email": "dev@example.com",
    "name": "Dev",
    "createdAt": "2026-08-06T10:15:30.000Z"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"
}
FieldTypeDescription
400ApiErrorBody failed schema validation (missing field, bad email, password out of range, unknown field).
409ApiError“An account with this email already exists.”
POST/auth/loginNo auth

Exchanges credentials for a fresh JWT. Email matching is case-insensitive.

FieldTypeDescription
email*stringRegistered email.
password*stringAccount password.
curl
curl -X POST http://localhost/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"dev@example.com","password":"s3cure-passphrase"}'
200 OK — same shape as register
{
  "user": { "id": "cml4k9x2b0000jq8f3h5a1d2e", "email": "dev@example.com", … },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"
}
FieldTypeDescription
401ApiError“Invalid email or password.” — deliberately identical for unknown emails and wrong passwords.
GET/auth/meJWT Bearer

Returns the account behind a token. Useful as a session check.

curl
curl http://localhost/api/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"
200 OK
{
  "user": {
    "id": "cml4k9x2b0000jq8f3h5a1d2e",
    "email": "dev@example.com",
    "name": "Dev",
    "createdAt": "2026-08-06T10:15:30.000Z"
  }
}
FieldTypeDescription
401ApiErrorMissing, malformed, or expired token — or the account no longer exists.

The JWT contract

Tokens are signed with the api's JWT_SECRET and expire 7 days after issue. The payload is exactly { "sub": "<user id>", "email": "<email>" } plus the standard iat/exp claims. There is no refresh-token flow yet — when a token expires, log in again. The web app stores the token in localStorage; your client should treat it as a bearer secret and never log it.

Continue to template endpoints