Skip to content

API Reference Batch 9 — Avatar, Preferences, System Config & Model Catalog

API Reference Batch 9 — Avatar, Preferences, System Config & Model Catalog

Section titled “API Reference Batch 9 — Avatar, Preferences, System Config & Model Catalog”

This batch documents four endpoint groups added or expanded in platform sprints 0050–0053: avatar management, user preferences, system configuration, and the model catalog administration API.

Unless otherwise noted, all endpoints require a valid Bearer token:

Authorization: Bearer <jwt>

Admin-only endpoints additionally require the authenticated user to have role: admin.


Avatar endpoints manage profile pictures for users. Images are resized to 256×256 PNG and stored in frontend/public/avatars/ as {user_id}.png.

Upload or replace the current user’s avatar.

PATCH /api/users/me/avatar
Authorization: Bearer <jwt>
Content-Type: multipart/form-data

Request body (multipart form):

FieldTypeRequiredDescription
filefileYesPNG or JPEG image; max 1 MB

Processing:

  1. Image is validated (PNG or JPEG only; > 1 MB rejected with 413).
  2. Pillow resizes and crops to 256×256 PNG.
  3. Saved to frontend/public/avatars/{user_id}.png (overwrites existing).
  4. avatar_url set to /avatars/{user_id}.png in profile_settings JSONB.

Response 200 OK:

{
"default_model_id": null,
"preferred_models": [],
"default_summarizer_model_id": null,
"theme": "system",
"avatar_url": "/avatars/a1b2c3d4-uuid.png"
}

Errors:

CodeReason
400File is not PNG or JPEG
413File exceeds 1 MB
401Not authenticated

Remove the current user’s avatar.

DELETE /api/users/me/avatar
Authorization: Bearer <jwt>

Deletes frontend/public/avatars/{user_id}.png from disk and clears avatar_url from profile_settings. If no avatar was set, this is a no-op.

Response 204 No Content


Set or replace the avatar for any user (admin only).

PATCH /api/admin/users/{user_id}/avatar
Authorization: Bearer <jwt>
Content-Type: multipart/form-data

Path parameters:

ParameterTypeDescription
user_idUUIDTarget user’s ID

Request body: Same multipart form as user self-upload (file field, PNG/JPEG, max 1 MB).

Same processing pipeline as user self-upload. Returns the updated UserPreferences for the target user.

Response 200 OK — same schema as upload response.

Errors:

CodeReason
404User not found
403Not admin
400 / 413Same as user upload

Preferences store per-user UI and model settings in the profile_settings JSONB column. Defaults are returned when the column is null or empty.

GET /api/users/me/preferences
Authorization: Bearer <jwt>

Response 200 OK:

{
"default_model_id": "claude-3-opus-20240229",
"preferred_models": ["claude-3-opus-20240229", "gpt-4o"],
"default_summarizer_model_id": null,
"theme": "dark",
"avatar_url": "/avatars/a1b2c3d4-uuid.png"
}
FieldTypeDefaultDescription
default_model_idstring | nullnullPre-selected model when opening new conversations
preferred_modelsstring[][]Ordered list of preferred model IDs for multi-model modes
default_summarizer_model_idstring | nullnullPre-selected synthesizer model for Summarize mode
theme"system" | "light" | "dark""system"UI color scheme preference
avatar_urlstring | nullnullPath to user’s avatar image

Merge new preference values into the current user’s settings. Only supplied fields are updated — omitted fields retain their current value.

PUT /api/users/me/preferences
Authorization: Bearer <jwt>
Content-Type: application/json

Request body (all fields optional):

{
"default_model_id": "gpt-4o",
"preferred_models": ["gpt-4o", "claude-3-opus-20240229"],
"default_summarizer_model_id": "claude-3-opus-20240229",
"theme": "dark"
}

Note: avatar_url cannot be set via this endpoint — use the avatar upload endpoint instead.

Response 200 OK — the full updated preferences object (same schema as GET).


The system config API manages platform-wide settings. Values resolve with priority: database override > environment variable > compiled default. All keys are admin-only.

GET /api/admin/config
Authorization: Bearer <jwt>

Returns all known configuration keys grouped by category.

Response 200 OK:

[
{
"key": "auto_title_enabled",
"category": "conversation",
"value": "true",
"default": "true",
"editable": true,
"description": "Enable automatic conversation title generation"
},
{
"key": "auto_title_model",
"category": "conversation",
"value": "anthropic/claude-haiku-3-20240307",
"default": "anthropic/claude-haiku-3-20240307",
"editable": true,
"description": "Model used for auto-title generation (format: provider/model_id)"
}
]
FieldTypeDescription
keystringConfig key name
categorystringLogical grouping (e.g., conversation, security, auth)
valuestringResolved value (DB override > env > default)
defaultstringCompiled-in default
editableboolWhether the key can be changed via the API
descriptionstringHuman-readable description

PUT /api/admin/config/{key}
Authorization: Bearer <jwt>
Content-Type: application/json

Path parameters:

ParameterTypeDescription
keystringConfig key to update (e.g., auto_title_enabled)

Request body:

{
"value": "false"
}

All values are stored as strings. Boolean values use "true" / "false".

Response 200 OK — the updated config entry (same schema as list item).

Errors:

CodeReason
404Key does not exist
422Key is not editable (editable: false)
403Not admin

Notable config keys:

KeyTypeDescription
auto_title_enabledboolEnable/disable auto-title generation for new conversations
auto_title_modelstringModel for auto-title (format: provider/model_id)
governance_enabledboolEnable human-in-loop governance prompts
mfa_requiredboolRequire MFA for all users in the organization
max_conversation_messagesintMaximum messages per conversation before hard limit

The model catalog stores the set of available AI models. Admins can manage catalog entries directly (add, update, disable) and trigger provider discovery to auto-populate from connected providers.

Router prefix: /api/admin/models/catalog


GET /api/admin/models/catalog/
Authorization: Bearer <jwt>

Query parameters:

ParameterTypeDefaultDescription
providerstringFilter by provider name (e.g., openai)
is_activeboolFilter by active/inactive status
pageint1Page number
per_pageint50Items per page

Response 200 OK:

{
"items": [
{
"id": "uuid",
"provider": "anthropic",
"model_id": "claude-3-opus-20240229",
"display_name": "Claude 3 Opus",
"is_active": true,
"context_window": 200000,
"supports_vision": false,
"max_output_tokens": 4096,
"created_at": "2026-01-15T10:00:00Z"
}
],
"total": 42,
"page": 1,
"per_page": 50
}

POST /api/admin/models/catalog/
Authorization: Bearer <jwt>
Content-Type: application/json

Request body:

{
"provider": "openai",
"model_id": "gpt-4o-2024-11-20",
"display_name": "GPT-4o (Nov 2024)",
"is_active": true,
"context_window": 128000,
"supports_vision": true,
"max_output_tokens": 16384
}
FieldRequiredDescription
providerYesProvider name (must match a registered provider)
model_idYesProvider-specific model identifier
display_nameNoHuman-readable name shown in the UI
is_activeNoWhether the model appears in the model selector (default: true)
context_windowNoMaximum context window in tokens
supports_visionNoWhether the model accepts image inputs
max_output_tokensNoMaximum output tokens per request

Response 201 Created — the created ModelConfig record.

Errors:

CodeReason
409A catalog entry with the same provider + model_id already exists

GET /api/admin/models/catalog/{model_config_id}
Authorization: Bearer <jwt>

Path parameters:

ParameterTypeDescription
model_config_idUUIDCatalog entry primary key

Response 200 OK — the ModelConfig record.

Errors: 404 if not found.


Partial update — only supplied fields are changed.

PUT /api/admin/models/catalog/{model_config_id}
Authorization: Bearer <jwt>
Content-Type: application/json

Request body (all fields optional):

{
"display_name": "GPT-4o Latest",
"is_active": false,
"context_window": 128000
}

Response 200 OK — the updated ModelConfig record.

Errors: 404 if not found.


DELETE /api/admin/models/catalog/{model_config_id}
Authorization: Bearer <jwt>

Permanently deletes the catalog entry. Models in use by existing conversations retain their model_config_snapshot but can no longer be selected for new requests.

Response 204 No Content

Errors: 404 if not found.


Query all connected providers for their available models and return discovered model metadata without persisting to the catalog.

GET /api/admin/models/catalog/discover
Authorization: Bearer <jwt>

Response 200 OK:

[
{
"provider": "openai",
"model_id": "gpt-4o-2024-11-20",
"display_name": "GPT-4o",
"context_window": 128000,
"supports_vision": true,
"already_in_catalog": false
}
]

already_in_catalog: true means a matching provider+model_id pair exists in the catalog. Use this to identify new models available for import.


Automatically add discovered models to the catalog. Models already in the catalog (by provider+model_id) are skipped; new models are created with is_active: true.

POST /api/admin/models/catalog/sync
Authorization: Bearer <jwt>

Response 200 OK:

{
"added": 3,
"skipped": 12,
"added_models": [
{ "provider": "openai", "model_id": "gpt-4o-2024-11-20" }
]
}

Enable or disable multiple catalog entries in a single request.

PATCH /api/admin/models/catalog/bulk
Authorization: Bearer <jwt>
Content-Type: application/json

Request body:

{
"model_ids": ["uuid-1", "uuid-2", "uuid-3"],
"is_active": false
}
FieldRequiredDescription
model_idsYesList of ModelConfig UUIDs to update
is_activeYesNew active state for all listed models

IDs that do not match any catalog entry are silently ignored.

Response 200 OK — list of updated ModelConfig records.


The share link v2 API provides per-token management for conversation sharing, replacing the single-token share/unshare pattern. Each share link has its own UUID and can be independently revoked.

Router prefix: /api/conversations/{conversation_id}/share_links

POST /api/conversations/{conversation_id}/share_links
Authorization: Bearer <jwt>

Generates a new unguessable UUID4 share token. Multiple share links can exist for the same conversation.

Response 201 Created:

{
"id": "link-uuid",
"token": "a3f82bc1-...",
"conversation_id": "conv-uuid",
"created_at": "2026-03-12T10:00:00Z",
"revoked": false,
"url": "https://your-instance.com/shared/a3f82bc1-..."
}

GET /api/conversations/{conversation_id}/share_links
Authorization: Bearer <jwt>

Returns all share links for the conversation — both active and revoked.

Response 200 OK — array of share link objects (same schema as create response).


DELETE /api/conversations/{conversation_id}/share_links/{token_id}
Authorization: Bearer <jwt>

Marks the share link as revoked. The token can no longer be used to access the conversation. The link record is retained for audit purposes.

Response 204 No Content

Errors: 404 if conversation or token not found, or not owned by current user.


Section titled “Access Shared Conversation (via share link)”
GET /api/share_links/{token}

No authentication required. Returns the conversation and all its messages in read-only mode using the share link token.

Response 200 OK — conversation object with messages array.

Errors: 404 if token is not found or has been revoked.