Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/joaomonteir0/printheritage/llms.txt

Use this file to discover all available pages before exploring further.

PrintHeritage uses the OAuth2 password flow for authentication. You exchange a user’s email address and password for a short-lived JWT access token, then attach that token to every subsequent request. This page explains how to obtain a token, how to use it, when it expires, and how to register new users and manage passwords through the API.

How Authentication Works

1

Submit credentials to POST /login

Send the user’s email and password as application/x-www-form-urlencoded form fields. The API validates the credentials against the bcrypt-hashed password stored in the database.
2

Receive a Bearer token

On success, the API returns a JSON object containing an access_token string and token_type: "bearer". Store this token in your application’s memory or a secure storage mechanism.
3

Attach the token to every request

Include the token in the Authorization header as Bearer <access_token> for all protected endpoints.
4

Re-authenticate after expiry

Tokens are valid for 60 minutes. After expiry the API returns 401 Unauthorized. Prompt the user to log in again to obtain a fresh token.

POST /login — Obtain a Token

The login endpoint follows the OAuth2 password grant. It expects application/x-www-form-urlencodednot JSON — because it uses FastAPI’s OAuth2PasswordRequestForm.

Request Fields

username
string
required
The user’s email address. Despite the field name username (required by the OAuth2 spec), the API validates this value as an email and looks it up in the users table by the email column.
password
string
required
The user’s plain-text password. The API verifies it against the bcrypt hash stored in the database using bcrypt.checkpw.

Response

On success the API returns HTTP 200 with:
access_token
string
required
A signed HS256 JWT. The payload contains sub (the user’s email) and exp (a UTC timestamp 60 minutes in the future).
token_type
string
required
Always "bearer".

Curl Example

curl -X POST http://localhost:8001/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=user@example.com&password=yourpassword"
If the email is not found or the password does not match, the API returns 401 Unauthorized with no additional detail. There is intentionally no distinction between “user not found” and “wrong password” to prevent user enumeration.

Using the Token

Include the access token in the Authorization header on every protected request:
Authorization: Bearer <access_token>
The API decodes the JWT, extracts the sub claim (email), and loads the corresponding user record from the database. If the token is missing, malformed, expired, or the user no longer exists, the API returns 401 Unauthorized.

JWT Payload Structure

{
  "sub": "user@example.com",
  "exp": 1720000000
}
ClaimDescription
subThe authenticated user’s email address
expUnix timestamp after which the token is invalid (60 min from issue)

GET /me — Current User Profile

Returns the full profile of the user identified by the Bearer token. Useful for bootstrapping a session after login.
curl http://localhost:8001/me \
  -H "Authorization: Bearer <access_token>"

UserResponse Schema

id
UUID
required
The user’s unique identifier (UUID v4).
email
EmailStr
required
The user’s email address. Used as the login credential and as the JWT sub claim.
global_role
GlobalRole
required
The user’s platform-wide role. One of: SUPER_ADMIN, GENERAL_ADMIN, PROJECT_ADMIN, or VISUALIZER. Determines access to admin-only endpoints and implicit project permissions.
full_name
string | null
The user’s display name. Optional; may be null if not set during registration or profile update.
birth_date
datetime | null
ISO 8601 datetime. Optional; may be null.
profile_pic_url
string | null
URL to the user’s profile picture. Optional; may be null.
is_public
boolean
required
Whether the user’s profile is publicly visible to other platform members. Defaults to true.

POST /register — Create a New Account

Registers a new user. This endpoint does not require authentication, so it can be called before any token is obtained. If the email is already in use, the API returns 400 Bad Request.

Request Fields

email
string
required
A valid email address. Must be unique across the platform. Used as the login credential.
password
string
required
The desired password. The API hashes it with bcrypt before storing it — the plain-text password is never persisted.
global_role
string
required
The role to assign to the new user. Must be one of: SUPER_ADMIN, GENERAL_ADMIN, PROJECT_ADMIN, VISUALIZER.
full_name
string
Optional display name for the user.
birth_date
string
Optional birth date in ISO 8601 format (e.g. "1990-06-15T00:00:00").
profile_pic_url
string
Optional URL to a profile picture.
is_public
boolean
Whether the profile is publicly visible. Defaults to true.

Curl Example

curl -X POST http://localhost:8001/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newuser@example.com",
    "password": "securepassword",
    "global_role": "VISUALIZER",
    "full_name": "João Monteiro"
  }'
Registration is logged to the audit trail as a USER_CREATED action. The new user’s UUID and email are recorded in the audit_logs table.

POST /change-password — Update Password

Allows an authenticated user to change their own password. The request must include the current password for verification before the new password is accepted.

Request Fields

current_password
string
required
The user’s existing password. Verified against the stored bcrypt hash using bcrypt.checkpw. Returns 400 Bad Request if it does not match.
new_password
string
required
The replacement password. Immediately hashed with bcrypt and stored; the old hash is discarded.

Curl Example

curl -X POST http://localhost:8001/change-password \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "current_password": "oldpassword",
    "new_password": "newstrongpassword"
  }'

Error Reference

HTTP StatusTrigger
400 Bad RequestEmail already registered (POST /register), or wrong current_password (POST /change-password)
401 UnauthorizedInvalid credentials (POST /login), missing, malformed, or expired Bearer token on any protected endpoint
403 ForbiddenValid token but insufficient role for the requested operation
A 401 response on a protected endpoint always means the token is absent, expired, or invalid — not that the user’s account credentials are wrong. Re-authenticate via POST /login to obtain a new token.

Build docs developers (and LLMs) love