3.5 KiB
User Profile & Settings
User identity, preferences, and credentials.
All endpoints require: Auth() middleware (JWT bearer token).
Get Profile
GET /profile
Auth: Authenticated user
Returns the current user's profile.
{
"id": "uuid",
"username": "jdoe",
"email": "jdoe@example.com",
"display_name": "Jane Doe",
"role": "user",
"avatar": "data:image/png;base64,...",
"settings": { "theme": "dark" },
"created_at": "2025-06-15T14:30:00Z",
"last_login_at": "2025-06-15T14:30:00Z"
}
| Field | Type | Notes |
|---|---|---|
id |
string | UUIDv4 |
username |
string | Unique login name |
email |
string | Unique, lowercased |
display_name |
string | null | Optional friendly name |
role |
"user" | "admin" |
Platform role |
avatar |
string | null | Data URI (PNG, 128×128). Omitted if unset. |
settings |
object | User preferences (theme, keybindings, etc.) |
created_at |
string | ISO 8601 timestamp |
last_login_at |
string | null | ISO 8601 timestamp. Null if never logged in. |
Note: The profile endpoint returns
avatar(notavatar_url) as a curated response shape. TheUsermodel usesavatar_urlinternally.
Update Profile
PUT /profile
Auth: Authenticated user
{
"display_name": "New Name",
"email": "new@example.com"
}
Both fields are optional (partial update). Returns the full profile
(same shape as GET /profile).
Errors:
409— email already taken
Upload Avatar
POST /profile/avatar
Auth: Authenticated user
Content-Type: application/json
{
"image": "data:image/png;base64,..."
}
Accepts a base64 data URI or raw base64 string. The server decodes, resizes to 128×128 PNG, and stores as a data URI. Max input: 2 MB.
Response:
{
"avatar": "data:image/png;base64,..."
}
Errors:
400— missingimagefield, invalid base64, unsupported format, too large
Delete Avatar
DELETE /profile/avatar
Auth: Authenticated user
Response:
{
"message": "avatar removed"
}
Change Password
POST /profile/password
Auth: Authenticated user (builtin auth only)
{
"current_password": "old-pass",
"new_password": "new-pass-min-8"
}
Validates current_password against stored bcrypt hash. new_password
must be 8–128 characters.
On success, the UEK (User Encryption Key) is re-wrapped with the new password so BYOK-encrypted provider keys remain accessible.
Response:
{
"message": "password updated"
}
Errors:
400— missing fields, password too short/long401— current password incorrect
Get Settings
GET /settings
Auth: Authenticated user
Returns user-level preferences wrapped in a settings key.
{
"settings": {
"theme": "dark",
"editor_keybindings": "vim",
"default_model": "claude-sonnet-4-5"
}
}
This is a composite response (named key wrapping a single object),
not a bare object. The settings key is always present; its value is
{} if the user has never saved preferences.
Update Settings
PUT /settings
Auth: Authenticated user
{
"theme": "light",
"new_key": "new_value"
}
Shallow merge: incoming keys overwrite existing, unmentioned keys are
preserved. Handles edge cases: SQL NULL, JSON null, and corrupted
array values are all normalized to {} before merge.
Returns the updated settings (same shape as GET /settings).