Backend API
Last updated: 2026-05-08
Backend API
This document provides an overview of the backend API architecture, covering its structure, configuration, routing, and key components. The API is built using the FastAPI framework.
Project Structure
The core backend logic is contained within the /api directory:
main.py: The main entrypoint for the FastAPI application. It initializes the app, includes routers, configures middleware, and manages the application lifespan./routers: Contains the different API endpoint groups:chat.py,auth.py,features.py,contacts.py,feedback.py,files.py,health.py. Amock_idp.pyrouter provides a mock Identity Provider for local development./services: Holds the business logic the routers delegate to. Organised into sub-modules:app_data/(user/session/conversation persistence),rag/(retrieval), and top-level services for chat, feedback, analytics, and SSE./agents: Contains thelanggraphagent implementation which is the core of the chatbot’s brain.config.py: Centralized nested settings usingpydantic-settings. Replaces the formervariant_config.py, which has been removed./database: Database module containing the async engine, connection pool management, multi-schema support, ORM models, and Alembic migration infrastructure./schemas: Pydantic request/response models split by domain:schemas/__init__.py(chat, feedback, health),schemas/auth.py,schemas/app_data.py,schemas/resources/contacts.py./core: Cross-cutting concerns —dependencies.py(singleton accessors),logging_config.py(hierarchical color-coded logger).
Configuration & Secrets
config.py — Nested Settings
All application settings are defined in api/config.py using pydantic-settings with nested BaseModel groups. This replaces the old flat settings object and the removed variant_config.py.
| Group | Key settings |
|---|---|
DatabaseSettings |
Pool size, max overflow, recycle period, max attempts |
StorageSettings |
Upload dir, max file/audio size, allowed types |
RAGSettings |
Embeddings model, number of queries, n_chunks, search type |
LLMSettings |
Primary model, temperature, max output tokens, transcription/translation models |
I18nSettings |
Supported languages, default language, user types (teenager / parent) |
LoggingSettings |
Log level, FORCE_COLOR / NO_COLOR overrides |
ServerSettings |
Host, port, reload mode |
LangfuseSettings |
Enabled, host, keys |
CorsSettings |
Allowed origins |
The variant (formerly CHATBOT_VARIANT) and debug mode are still read from the environment but are accessed through settings.variant and settings.debug. Valid values for variant are teenager and parent.
Secrets Management
Sensitive values (database URL, API keys) are fetched from Google Cloud Secret Manager via utils.secret.get_secret(), with environment variable override for local development.
Application Lifecycle
- Lifespan Manager: The
@asynccontextmanager lifespaninapi/main.pyruns on startup in 5 phases:- Data Infrastructure: Initializes DB schemas and PGVector store.
- Core Services: Loads contact directory, initializes chat orchestration, and warms up RAG engine.
- Background Workers: Starts
MemoryLakeworkers for async persistence. - System Integrity: Verifies formatter registration.
- Feature Flags: Initializes OpenFeature provider and seeds default flags.
- Graceful Shutdown: On shutdown, it drains the
MemoryLake, closes database pools, and cleans up the chat service. A customuvicornserver class,MMBackend, overridesshutdownto close all active SSE connections viasse_manager._cleanup().
API Endpoints
All API endpoints are prefixed with /api/v1. Interactive documentation is available at /docs (Swagger UI) and /redoc when the application is running.
Authentication
Endpoints in routers/auth.py support the consent-aware IDP-based authentication flow.
POST /auth/login: Receives an IDP token from the client, validates it against the IDP’s public key, and initialises an application session viaUserAuthService. The request body (AuthIDPRequest) now includes:has_consented(bool) — whether the user accepted the Terms of Service during onboarding.invitation_id(UUID, optional) — an invitation token for joining a collective.age_class(enum, optional) — the user’s self-declared age group (child,preteen,teenager,adult).
Returns an
AuthResponsecontaining abearer_token(issued only if consent requirements are met), aprofileobject withconsent_verificationstatus, and optionally the user’suser_history.PATCH /auth/profile: Updates user profile fields during onboarding —language,country,age_class, andhas_consented. Returns a refreshedProfileUpdateResponsewith updated session scope.POST /auth/request-authorization(teenager only): Creates aParentalAuthorizationRequestvoucher. Returns anauthorization_urlfor the teenager to share with their parent.GET /auth/authorization-request/{token}: Returns non-sensitive information about an authorization request — itsstatus,is_validflag, and the teenager’slanguagefor UI localisation on the parent’s side.POST /auth/authorize-teenager/{token}(parent only): Fulfills a pending authorization request. Grants parental consent, creates aUserConsenton the teenager’s behalf, adds the teenager to the parent’s primary collective, and marks the request asCOMPLETED.
A mock IDP (routers/mock_idp.py) is registered in development mode. It issues signed tokens for testing and exposes POST /mock-idp/authenticate and GET /mock-idp/.well-known/jwks.json.
Consent-Based Session Scoping
Every AppSession carries a scope field (SessionScope enum) that reflects the user’s progress through the consent pipeline:
| Scope | Condition | Chat access |
|---|---|---|
ONBOARDING |
No TOS consent yet | Profile settings only |
PENDING_CONSENT |
TOS accepted, but minor lacks parental consent | Restricted — consent gate shown |
FULL |
All consent requirements satisfied | Unrestricted chat access |
Two feature flags control consent enforcement: - enforce_consent_policy (backend): When true, the UserAuthService withholds the bearer token entirely if consent is incomplete (identity gate). - enforce_consent_gate (frontend): When true, the ConsentGate component blocks access to chat until parental consent is granted (UI gate).
Both flags default to false in development for easier testing.
Feature Flags
GET /features: Returns a dictionary of all frontend-visible feature flags resolved for the currentvariantandenvironmentcontext. Accepts optional query parametersvariant,environment, andlanguage.
Collectives
Group management endpoints in routers/collectives.py. Access is restricted to users who are owners or admins of the target collective.
GET /collectives: Lists all collectives where the authenticated user holds anowneroradminrole.GET /collectives/{id}/members: Returns all members of a collective with their consent status (active,pending_tos,pending_consent).GET /collectives/{id}/invitations: Lists active and pending invitations for a collective.POST /collectives/{id}/invitations: Creates a new invitation. Acceptsage_class,language, and an optionallabel. Ausage_limitof 1 is automatically set for age-class-restricted invitations. Returns theinvitation_idand a ready-to-shareinvite_url.DELETE /collectives/{id}/invitations/{invitation_id}: Revokes an invitation.PATCH /collectives/{id}/members/{member_id}/consent: Grants or revokes parental consent for a specific member (admin-only).PATCH /collectives/{id}/parental-consent: Toggles theparental_consent_grantedflag on the collective itself.POST /collectives/authorize-teenager: Authorizes a teenager by theirteenager_idand adds them to the parent’s primary collective (parent only).
Health Check
GET /health: Reports status of the application, database connection, connection pool, and chat service.
Chat
Core endpoints for chatbot interaction, defined in routers/chat.py.
POST /chat/init: Initialises a new chat session. Accepts an optionalresume_conversation_idin the request body (InitChatRequest). User profile information (language, user type, origin) is automatically retrieved from theAppSessionvia the auth token. Returns aChatResponsecontaining the initial message and session state.POST /chat: Sends a user message and receives the agent’s response. Supportsattachments(paths to previously uploaded files).POST /chat/emotion: Submits an emotion selection during context collection.POST /chat/dropdown: Submits a dropdown (multiple-choice) selection.GET /chat/history: Returns the conversation history for the current user (UserHistory), listing past sessions and conversations.POST /chat/delete: Deletes the user’s conversation summary from the persistent store.POST /chat/settings/change: Updates session settings (language, monster) on the fly.GET /chat/{session_id}/events/listen: SSE endpoint for real-time event streaming.POST /chat/{session_id}/events/push: Allows the backend to push custom events to a session (for debugging).POST /audio/transcribe: Accepts an audio file and returns the transcribed text. (Defined inchat.py).
Internationalization (i18n)
New endpoints in routers/i18n.py manage the dynamic content cache.
POST /i18n/reset-cache: Forces a full reload of the database override cache in theI18nManager. This is useful after manual database updates or a sync operation.GET /i18n/status: Returns the current status of the i18n system, including supported languages and cache metadata.
Contacts
GET /contacts/: Returns a list of all country codes that have available contact information.GET /contacts/{country_code}: Returns contact resources for a specific country. Supports an optionalurgent_onlyboolean query parameter.
File Management
POST /files/upload: Uploads one or more files. Returns file paths for use in chatattachments.GET /files/{filename}: Retrieves an uploaded file.DELETE /files/{filename}: Deletes a file.
Feedback
POST /feedback/{session_id}: Submits user feedback for a session.GET /feedback/{session_id}: Retrieves all feedback for a session.DELETE /feedback/{session_id}: Deletes feedback for a session.
Static Files
/static: Serves static files from thepublicdirectory.
Core Services
AppDataService (services/app_data/app_data_service.py)
The central data-management service, built on a repository pattern. Provides methods for:
- User registration & lookup: Identifies users via
(idp, login_id)composite key inIDPLogintable. Creates a newUserrecord on first login. - Session initialisation: Creates an
AppSessionlinked to the user, populates the initial LangGraph state, and returns aUserHistoryof past conversations. - Conversation retrieval: Eager-loads the
User → AppSession → Conversationrelationship chain to avoid N+1 queries. - Collective management: Creates and retrieves
Collectiverecords, managesCollectiveMembershiprelationships, and processesInvitationrecords for group-based access. - Consent management: Tracks
UserConsent(TOS acceptance) andParentalConsent(parent-to-teenager authorization) via dedicated repositories. - Dynamic i18n management:
LocalizedContentServiceprovides methods to upsert and retrieve localized content overrides from the database. It is a standalone service independent ofAppDataService.
Repositories (UserRepository, IDPLoginRepository, AppSessionRepository, ConversationRepository, ChatSessionRepository, CollectiveRepository, CollectiveMembershipRepository, InvitationRepository, UserConsentRepository, ParentalConsentRepository) are accessed via self.<name> and share a BaseDataService with common CRUD helpers. LocalizedContentRepository is managed separately by its own service.
UserAuthService (services/user_auth_service.py)
The entry gate for authentication. Handles identity mapping, consent verification, invitation processing, and session issuance — all in a single atomic transaction (authenticate()). Key responsibilities:
- Identity mapping: Resolves
(idp, login_id)to an internalUserviaIDPLogin. - Registration: Creates new
User+IDPLoginrecords atomically. During registration, automatically resolves the user’s country via Geo-IP (if the client supports it) and processes any provided invitation. - Auto-collective creation: On first login, parent users automatically get a “Family” collective with an
OWNERmembership. - Consent gating: Before issuing a bearer token, verifies
UserConsentandParentalConsent. Subject to theenforce_consent_policyfeature flag. - Scope assignment: Determines the
AppSession.scopebased on consent status (ONBOARDING→PENDING_CONSENT→FULL). - Authorization requests: Creates and fulfills
ParentalAuthorizationRequestvouchers for teenagers to request parental consent.
The db_session_router pattern is used throughout — methods accept an optional db_session and will create their own if none is provided, enabling both standalone use and transactional reuse within authenticate().
GeoService (services/geo_service.py)
Provides CountryResolver — a swappable abstraction for IP-to-country-code resolution. Current implementations:
IpApiCountryResolver: Uses the ipapi.co free tier (1,000 requests/day). Skips resolution for private/local IPs. Configurable viaGEOIP_PROVIDER=ipapiandGEOIP_IPAPI_API_KEY.NullCountryResolver: No-op resolver for local development — always returnsNone.
The resolver is accessed via get_country_resolver() singleton and is called during user registration when the client declares supports_geoip capability. Detected country codes are stored on the User.country field and pre-filled in the frontend SettingsPanel.
FeatureFlagService (services/app_data/feature_flag_service.py)
Implements AbstractProvider from the OpenFeature SDK. Backed by a FeatureFlag database table and a TTL-based in-memory cache (60 s TTL, 1 024 entries). On startup, seeds default flags and primes the cache.
Flags are resolved against an EvaluationContext carrying environment and variant dimensions. The service is the backend of the GET /features endpoint and can also be called directly inside agent nodes.
| Flag name | Default | Visibility | Variant | Description | |
|---|---|---|---|---|---|
enable_dynamic_question_generation |
true |
backend | all | LLM generates extra context questions | |
collective_management |
true |
frontend | parent | Collective admin dashboard | |
enable_simple_qr_invitation |
true |
frontend | parent | Simplified QR code modal for non-expiring invitations | |
enable_login_screen |
true |
frontend | all | Manual login screen instead of auto-login | |
enable_monster_flip_animation |
true |
frontend | all | 3D coin-flip animation for monster avatar | |
enable_simple_resume_prompt |
true |
frontend | all | Floating “resume” UI instead of full history modal | |
enable_settings_panel |
true |
frontend | all | Settings/consent panel replacing language selector | |
enforce_consent_policy |
false |
backend | all | Withholds bearer token if consent is incomplete (identity gate) | |
enforce_consent_gate |
false |
frontend | all | Shows blocking consent gate when parental consent is missing (UI gate) |
ChatService (services/chat_service.py)
Manages chat sessions, interacts with the LangGraph agent, and orchestrates the overall chat flow. Updated to consume AppDataService for session context and MemoryLake for asynchronous summary persistence.
RAGService (services/rag/rag_service.py)
Multi-tenant retrieval service. Manages separate PGVectorStore collections per user type (docs_youth / docs_adult). Integrates a PostgresRAGCache for semantic query caching and CacheBackedEmbeddings for embedding-level caching. See RAG Pipeline for details.
MemoryService & MemoryLake
MemoryService wraps the LangGraph PostgresStore for summary persistence. MemoryLake (agents/service1/memory/lake.py) provides a non-blocking async queue in front of MemoryService. See Agentic framework for details.
Other Services
FeedbackService: Stores and retrieves user feedback.SSEManager: Manages all Server-Sent Event connections.AudioTranscriptionService: Converts audio files to text using the Gemini transcription model.ContactService: Serves structured contact resources from YAML files, grouped by country.AnalyticsService: Extracts and categorises conversation analytics using a secondary LLM pass.
Logging
Application logging is configured in api/core/logging_config.py. All application code uses the chatbot logger hierarchy (e.g., logging.getLogger("chatbot.rag")), which is independent of Uvicorn’s internal loggers.
- Color-coded output: Each logger in the
chatbot.*tree is displayed in salmon; Uvicorn loggers use distinct pastel colors. - Fixed-width alignment: Level (8 chars) and logger-name (15 chars) columns ensure readable output.
- TTY detection: Colors are enabled automatically for interactive terminals and can be forced via
FORCE_COLOR=1or disabled withNO_COLOR=1. - Log level: Controlled by
settings.logging.level(defaultINFO).
Data Models (Schemas)
Schemas are split across multiple files under api/schemas/:
schemas/__init__.py: Chat domain —ChatRequest,ChatResponse,ChatSessionResponse,InitChatRequest,ChangeSessionSettingsRequest,EmotionSelection,DropdownSelection,FeedbackCreate,FeedbackResponse,FileUploadResponse,HealthResponse.schemas/auth.py: Authentication —AuthIDPRequest,AuthResponse,UserAppSessionInit,ProfileUpdateRequest,ProfileUpdateResponse,ConsentVerificationAuthResponse,AuthorizationRequestResponse,AuthorizationRequestInfo.schemas/app_data.py: Data layer read models —UserRecord,AppSessionRecord,ConversationRecord,ConsentVerificationRecord,UserHistory.schemas/resources/contacts.py: Contact resources —ContactResource,CountryContacts.schemas/collectives.py: Collective management —CollectiveRecord,MemberRecord,CollectiveMemberResponse,InvitationCreateRequest,InvitationResponse,CollectiveInvitationRecord,MemberConsentUpdateRequest,ParentalConsentGrantedRequest,AuthorizeTeenagerRequest.
Configuration — Updated Groups
The following groups have been added or changed since the last major update:
| Group | Key additions/changes |
|---|---|
GeoIPSettings |
provider (ipapi/null/maxmind), ipapi_api_key |
ClientConfig |
supports_sse, supports_geoip — negotiated per client type |
ServerSettings |
frontend_teenager_url, frontend_parent_url — split services for consent flows |
CorsSettings |
Now includes both frontend origins |
Known client configurations: WebReactConfig (sse + geoip), DiscordConfig (neither), DevTerminalConfig (neither). Used during capability negotiation in the auth flow.
Lifespan — Updated Startup Sequence
The startup sequence now includes in Phase 1 an automatic alembic upgrade head run, ensuring the database schema is up-to-date before any application code executes. This replaces the previous init_db() call and runs migrations inside the application container on every deployment (see Deployment).