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. A mock_idp.py router 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 the langgraph agent implementation which is the core of the chatbot’s brain.
  • config.py: Centralized nested settings using pydantic-settings. Replaces the former variant_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 lifespan in api/main.py runs on startup in 5 phases:
    1. Data Infrastructure: Initializes DB schemas and PGVector store.
    2. Core Services: Loads contact directory, initializes chat orchestration, and warms up RAG engine.
    3. Background Workers: Starts MemoryLake workers for async persistence.
    4. System Integrity: Verifies formatter registration.
    5. 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 custom uvicorn server class, MMBackend, overrides shutdown to close all active SSE connections via sse_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 via UserAuthService. 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 AuthResponse containing a bearer_token (issued only if consent requirements are met), a profile object with consent_verification status, and optionally the user’s user_history.

  • PATCH /auth/profile: Updates user profile fields during onboarding — language, country, age_class, and has_consented. Returns a refreshed ProfileUpdateResponse with updated session scope.

  • POST /auth/request-authorization (teenager only): Creates a ParentalAuthorizationRequest voucher. Returns an authorization_url for the teenager to share with their parent.

  • GET /auth/authorization-request/{token}: Returns non-sensitive information about an authorization request — its status, is_valid flag, and the teenager’s language for UI localisation on the parent’s side.

  • POST /auth/authorize-teenager/{token} (parent only): Fulfills a pending authorization request. Grants parental consent, creates a UserConsent on the teenager’s behalf, adds the teenager to the parent’s primary collective, and marks the request as COMPLETED.

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.

Feature Flags

  • GET /features: Returns a dictionary of all frontend-visible feature flags resolved for the current variant and environment context. Accepts optional query parameters variant, environment, and language.

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 an owner or admin role.
  • 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. Accepts age_class, language, and an optional label. A usage_limit of 1 is automatically set for age-class-restricted invitations. Returns the invitation_id and a ready-to-share invite_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 the parental_consent_granted flag on the collective itself.
  • POST /collectives/authorize-teenager: Authorizes a teenager by their teenager_id and 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 optional resume_conversation_id in the request body (InitChatRequest). User profile information (language, user type, origin) is automatically retrieved from the AppSession via the auth token. Returns a ChatResponse containing the initial message and session state.
  • POST /chat: Sends a user message and receives the agent’s response. Supports attachments (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 in chat.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 the I18nManager. 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 optional urgent_only boolean query parameter.

File Management

  • POST /files/upload: Uploads one or more files. Returns file paths for use in chat attachments.
  • 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 the public directory.

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 in IDPLogin table. Creates a new User record on first login.
  • Session initialisation: Creates an AppSession linked to the user, populates the initial LangGraph state, and returns a UserHistory of past conversations.
  • Conversation retrieval: Eager-loads the User → AppSession → Conversation relationship chain to avoid N+1 queries.
  • Collective management: Creates and retrieves Collective records, manages CollectiveMembership relationships, and processes Invitation records for group-based access.
  • Consent management: Tracks UserConsent (TOS acceptance) and ParentalConsent (parent-to-teenager authorization) via dedicated repositories.
  • Dynamic i18n management: LocalizedContentService provides methods to upsert and retrieve localized content overrides from the database. It is a standalone service independent of AppDataService.

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 internal User via IDPLogin.
  • Registration: Creates new User + IDPLogin records 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 OWNER membership.
  • Consent gating: Before issuing a bearer token, verifies UserConsent and ParentalConsent. Subject to the enforce_consent_policy feature flag.
  • Scope assignment: Determines the AppSession.scope based on consent status (ONBOARDINGPENDING_CONSENTFULL).
  • Authorization requests: Creates and fulfills ParentalAuthorizationRequest vouchers 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 via GEOIP_PROVIDER=ipapi and GEOIP_IPAPI_API_KEY.
  • NullCountryResolver: No-op resolver for local development — always returns None.

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=1 or disabled with NO_COLOR=1.
  • Log level: Controlled by settings.logging.level (default INFO).

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).