# FinSight Backend API Documentation **Generated:** February 9, 2026 **Version:** 1.0 **Framework:** Django 6.0.2 with Django REST Framework --- ## Table of Contents 1. [Overview](#overview) 2. [API Routes & Endpoints](#api-routes--endpoints) 3. [Models](#models) 4. [Views & Endpoints Detail](#views--endpoints-detail) 5. [Serializers](#serializers) 6. [Utility Functions](#utility-functions) 7. [Configuration](#configuration) 8. [Authentication](#authentication) --- ## Overview FinSight is a Django-based REST API backend that provides user authentication, email verification via OTP, and JWT-based token management. The backend is organized into modular Django apps: - **`core`**: Main project configuration and routing - **`accounts`**: User authentication, registration, and OTP verification - **`api`**: Placeholder app for future API endpoints --- ## API Routes & Endpoints ### Base URL Structure All authentication routes are prefixed with `/api/auth/` ### Complete Endpoint List | HTTP Method | Endpoint Path | View Class | Authentication Required | Description | | ----------- | -------------------------- | ------------------ | ----------------------- | -------------------------------------------- | | **POST** | `/api/auth/register/` | `RegisterView` | No | Register a new user account | | **POST** | `/api/auth/login/` | `LoginView` | No | Login with username/email and password | | **GET** | `/api/auth/me/` | `MeView` | Yes (JWT) | Get current authenticated user profile | | **POST** | `/api/auth/send-otp/` | `SendOTPView` | No | Send OTP to user's email for verification | | **POST** | `/api/auth/verify-otp/` | `VerifyOTPView` | No | Verify OTP to activate email verification | | **POST** | `/api/auth/logout/` | `LogoutView` | Yes (JWT) | Logout and blacklist refresh token | | **POST** | `/api/auth/token/refresh/` | `TokenRefreshView` | No | Refresh JWT access token using refresh token | | **POST** | `/api/auth/google/` | `GoogleAuthView` | No | Authenticate with Google OAuth token | --- ## Models ### `User` Model **Location:** `backend/accounts/models.py` Custom user model extending Django's `AbstractBaseUser` and `PermissionsMixin`. #### Fields | Field Name | Type | Properties | Description | | ------------------- | ------------- | ----------------------------------- | -------------------------------- | | `name` | CharField | max_length=100 | User's full name | | `username` | CharField | max_length=50, unique=True | Unique username for login | | `email` | EmailField | - | User's email address | | `is_email_verified` | BooleanField | default=False | Email verification status | | `otp` | CharField | max_length=6, blank=True, null=True | Current OTP for verification | | `otp_created_at` | DateTimeField | blank=True, null=True | Timestamp when OTP was generated | | `is_active` | BooleanField | default=True | Account active status | | `is_staff` | BooleanField | default=False | Staff/admin status | | `created_at` | DateTimeField | auto_now_add=True | Account creation timestamp | #### Configuration - **USERNAME_FIELD:** `username` - **REQUIRED_FIELDS:** `["email"]` - **Manager:** `UserManager` #### Methods - `__str__(self)` → Returns the username as string representation ### `UserManager` Class **Location:** `backend/accounts/models.py` Custom manager for the User model. #### Methods ##### `create_user(username, email, password=None, **extra_fields)` **Purpose:** Creates and saves a regular user with hashed password **Parameters:** - `username` (str, required): Unique username - `email` (str, required): User's email address - `password` (str, optional): Plain text password (will be hashed) - `**extra_fields`: Additional user fields **Returns:** User instance **Raises:** - `ValueError`: If username or email is not provided ##### `create_superuser(username, email, password=None, **extra_fields)` **Purpose:** Creates and saves a superuser with staff and superuser privileges **Parameters:** - `username` (str, required): Unique username - `email` (str, required): User's email address - `password` (str, optional): Plain text password (will be hashed) - `**extra_fields`: Additional user fields **Returns:** User instance with `is_staff=True` and `is_superuser=True` --- ## Views & Endpoints Detail ### `RegisterView` **Location:** `backend/accounts/views.py` **Base Class:** `APIView` **Permission:** `AllowAny` (public access) #### POST `/api/auth/register/` **Purpose:** Register a new user account **Request Body:** ```json { "name": "string (required)", "username": "string (required, unique)", "email": "string (required)", "password": "string (required)" } ``` **Success Response (201 CREATED):** ```json { "success": true, "message": "User registered successfully" } ``` **Error Response (400 BAD REQUEST):** ```json { "field_name": ["error message"] } ``` **Implementation Details:** - Uses `RegisterSerializer` for validation and user creation - Password is automatically hashed during creation - Email verification is NOT required at registration --- ### `LoginView` **Location:** `backend/accounts/views.py` **Base Class:** `APIView` **Permission:** `AllowAny` (public access) #### POST `/api/auth/login/` **Purpose:** Authenticate user and return JWT tokens **Request Body:** ```json { "identifier": "string (username or email)", "password": "string" } ``` **Success Response (200 OK):** ```json { "access": "eyJhbGc...", "refresh": "eyJhbGc..." } ``` **Error Responses:** **400 BAD REQUEST:** ```json { "error": "Identifier and password are required" } ``` **401 UNAUTHORIZED:** ```json { "error": "Invalid username/email or password" } ``` **403 FORBIDDEN:** ```json { "error": "Email not verified" } ``` **Implementation Details:** - Accepts either username or email as identifier - Checks password using Django's `check_password()` method - Validates email verification status before issuing tokens - Returns both access and refresh JWT tokens --- ### `MeView` **Location:** `backend/accounts/views.py` **Base Class:** `APIView` **Permission:** `IsAuthenticated` (requires JWT token) #### GET `/api/auth/me/` **Purpose:** Retrieve current authenticated user's profile information **Request Headers:** ``` Authorization: Bearer ``` **Success Response (200 OK):** ```json { "id": 1, "name": "John Doe", "username": "johndoe", "email": "john@example.com" } ``` **Error Response (401 UNAUTHORIZED):** ```json { "detail": "Authentication credentials were not provided." } ``` **Implementation Details:** - Extracts user from `request.user` (populated by JWT authentication) - Returns user ID, name, username, and email - Does not expose sensitive fields like password or OTP --- ### `SendOTPView` **Location:** `backend/accounts/views.py` **Base Class:** `APIView` **Permission:** `AllowAny` (public access) #### POST `/api/auth/send-otp/` **Purpose:** Generate and send a 6-digit OTP to user's email for verification **Request Body:** ```json { "email": "string (required)" } ``` **Success Response (200 OK):** ```json { "success": true, "message": "OTP sent successfully" } ``` **Error Responses:** **400 BAD REQUEST:** ```json { "error": "Email is required" } ``` **404 NOT FOUND:** ```json { "error": "User not found" } ``` **Implementation Details:** - Generates a 6-digit random OTP using `generate_otp()` utility - Stores OTP and creation timestamp in user record - Sends email via Django's `send_mail()` with: - Subject: "FinSight AI - Email Verification OTP" - Message: "Your OTP is {otp}. It is valid for 5 minutes." - From: "no-reply@finsight.ai" - OTP is valid for 5 minutes --- ### `VerifyOTPView` **Location:** `backend/accounts/views.py` **Base Class:** `APIView` **Permission:** `AllowAny` (public access) #### POST `/api/auth/verify-otp/` **Purpose:** Verify user's email using the OTP sent via email **Request Body:** ```json { "email": "string (required)", "otp": "string (required, 6 digits)" } ``` **Success Response (200 OK):** ```json { "success": true, "message": "Email verified successfully" } ``` **Error Responses:** **400 BAD REQUEST (missing fields):** ```json { "error": "Email and OTP are required" } ``` **400 BAD REQUEST (invalid OTP):** ```json { "error": "Invalid OTP" } ``` **400 BAD REQUEST (expired OTP):** ```json { "error": "OTP expired" } ``` **Implementation Details:** - Validates OTP matches the one stored in user record - Checks OTP expiration (5 minutes from creation) - Sets `is_email_verified = True` on success - Clears OTP and OTP creation timestamp after verification - Users can login only after email is verified --- ### `LogoutView` **Location:** `backend/accounts/views.py` **Base Class:** `APIView` **Permission:** `IsAuthenticated` (requires JWT token) #### POST `/api/auth/logout/` **Purpose:** Logout user by blacklisting their refresh token **Request Headers:** ``` Authorization: Bearer ``` **Request Body:** ```json { "refresh": "string (required, refresh token)" } ``` **Success Response (200 OK):** ```json { "success": true, "message": "Logged out successfully" } ``` **Error Responses:** **400 BAD REQUEST (missing token):** ```json { "error": "Refresh token is required" } ``` **400 BAD REQUEST (invalid token):** ```json { "error": "Invalid or expired refresh token" } ``` **Implementation Details:** - Uses `rest_framework_simplejwt.token_blacklist` to blacklist tokens - Prevents reuse of the refresh token after logout - Requires both access token (in header) and refresh token (in body) --- ### `TokenRefreshView` **Location:** `rest_framework_simplejwt.views` (third-party) **Base Class:** `TokenRefreshView` **Permission:** `AllowAny` #### POST `/api/auth/token/refresh/` **Purpose:** Obtain a new access token using a valid refresh token **Request Body:** ```json { "refresh": "string (required, refresh token)" } ``` **Success Response (200 OK):** ```json { "access": "eyJhbGc..." } ``` **Error Response (401 UNAUTHORIZED):** ```json { "detail": "Token is invalid or expired", "code": "token_not_valid" } ``` **Implementation Details:** - Provided by `djangorestframework-simplejwt` library - Validates refresh token and issues new access token - Does not issue a new refresh token (single refresh token lifecycle) --- ### `GoogleAuthView` **Location:** `backend/accounts/views.py` **Base Class:** `APIView` **Permission:** `AllowAny` (public access) #### POST `/api/auth/google/` **Purpose:** Authenticate user using Google OAuth token and return JWT tokens **Request Body:** ```json { "token": "string (required, Google OAuth ID token)" } ``` **Success Response (200 OK):** ```json { "access": "eyJhbGc...", "refresh": "eyJhbGc..." } ``` **Error Responses:** **400 BAD REQUEST:** ```json { "error": "Token is required" } ``` **401 UNAUTHORIZED:** ```json { "error": "Invalid token" } ``` **Implementation Details:** - Verifies Google OAuth ID token using `google.oauth2.id_token` - Extracts user information (email, name) from verified token - Creates new user if email doesn't exist, or retrieves existing user - Automatically sets `is_email_verified=True` for Google-authenticated users - Returns both access and refresh JWT tokens - Requires valid Google OAuth Client ID configured in settings --- ## Serializers ### `RegisterSerializer` **Location:** `backend/accounts/serializers.py` **Base Class:** `serializers.ModelSerializer` **Purpose:** Validate and create new user accounts **Model:** `User` **Fields:** - `name` (CharField) - `username` (CharField) - `email` (EmailField) - `password` (CharField, write-only) **Extra Configuration:** ```python extra_kwargs = { "password": {"write_only": True} } ``` #### Methods ##### `create(validated_data)` **Purpose:** Create a new user with hashed password **Parameters:** - `validated_data` (dict): Validated user data **Returns:** User instance **Implementation:** - Calls `User.objects.create_user()` to ensure password is hashed - Extracts username, email, password, and name from validated data --- ## Utility Functions ### `generate_otp()` **Location:** `backend/accounts/utils.py` **Purpose:** Generate a random 6-digit OTP for email verification **Parameters:** None **Returns:** `str` - 6-digit numeric string (e.g., "123456") **Implementation:** ```python return str(random.randint(100000, 999999)) ``` **Usage:** Called by `SendOTPView` to create verification codes --- ## Configuration ### Django Settings **Location:** `backend/core/settings.py` #### Installed Apps - `django.contrib.admin` - `django.contrib.auth` - `django.contrib.contenttypes` - `django.contrib.sessions` - `django.contrib.messages` - `django.contrib.staticfiles` - `rest_framework` - `corsheaders` - `rest_framework_simplejwt.token_blacklist` - `accounts` #### Database - **Engine:** SQLite3 - **Location:** `backend/db.sqlite3` #### Email Configuration - **Backend:** `django.core.mail.backends.smtp.EmailBackend` - **Host:** `smtp.gmail.com` - **Port:** `465` - **SSL:** `True` - **TLS:** `False` - **From Email:** `FinSight AI ` #### CORS Settings - **CORS_ALLOW_ALL_ORIGINS:** `True` #### Custom User Model - **AUTH_USER_MODEL:** `accounts.User` --- ## Authentication ### JWT Configuration **Location:** `backend/core/settings.py` #### REST Framework Settings ```python REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ( "rest_framework_simplejwt.authentication.JWTAuthentication", ), } ``` #### JWT Token Settings ```python SIMPLE_JWT = { "ACCESS_TOKEN_LIFETIME": timedelta(minutes=15), "REFRESH_TOKEN_LIFETIME": timedelta(days=1), "AUTH_HEADER_TYPES": ("Bearer",), } ``` **Token Lifetime:** - **Access Token:** 15 minutes - **Refresh Token:** 1 day **Header Format:** ``` Authorization: Bearer ``` ### Authentication Flow 1. **Registration:** - User registers with name, username, email, and password - Account created but email NOT verified (`is_email_verified=False`) 2. **Email Verification:** - User requests OTP via `/api/auth/send-otp/` - OTP sent to registered email (valid for 5 minutes) - User submits OTP via `/api/auth/verify-otp/` - Email verified (`is_email_verified=True`) 3. **Login:** - User submits username/email and password - System checks credentials and email verification status - Returns access and refresh tokens if successful 4. **Authenticated Requests:** - Client includes access token in `Authorization` header - Access token valid for 15 minutes 5. **Token Refresh:** - Client submits refresh token to `/api/auth/token/refresh/` - Receives new access token (refresh token valid for 1 day) 6. **Logout:** - Client submits refresh token to `/api/auth/logout/` - Refresh token blacklisted and cannot be reused --- ## Application Configuration Files ### WSGI Application **Location:** `backend/core/wsgi.py` **Purpose:** WSGI callable for production deployment **Exposed Variable:** `application` (WSGI callable) **Environment Variable:** `DJANGO_SETTINGS_MODULE=core.settings` --- ### ASGI Application **Location:** `backend/core/asgi.py` **Purpose:** ASGI callable for asynchronous deployment **Exposed Variable:** `application` (ASGI callable) **Environment Variable:** `DJANGO_SETTINGS_MODULE=core.settings` --- ## App Configurations ### Accounts App **Location:** `backend/accounts/apps.py` **Class:** `AccountsConfig` **Name:** `accounts` --- ### API App **Location:** `backend/api/apps.py` **Class:** `ApiConfig` **Name:** `api` **Status:** Placeholder app with no models or views currently defined --- ## URL Routing Structure ### Root URLs **Location:** `backend/core/urls.py` ```python urlpatterns = [ path("api/auth/", include("accounts.urls")), ] ``` **Note:** Django admin is not currently exposed in URL configuration --- ### Accounts URLs **Location:** `backend/accounts/urls.py` All routes under `/api/auth/` prefix: ```python urlpatterns = [ path("register/", RegisterView.as_view(), name="register"), path("login/", LoginView.as_view(), name="login"), path("me/", MeView.as_view(), name="me"), path("send-otp/", SendOTPView.as_view()), path("verify-otp/", VerifyOTPView.as_view()), path("logout/", LogoutView.as_view(), name="logout"), path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), path("google/", GoogleAuthView.as_view(), name="google-auth"), ] ``` --- ## Dependencies **Location:** `backend/requirements.txt` - `django` - `djangorestframework` - `django-cors-headers` - `djangorestframework-simplejwt` - `python-decouple` - `requests` - `google-auth` - `google-auth-oauthlib` --- ## Notes and Considerations 1. **Email Verification Required:** Users cannot login until their email is verified via OTP 2. **OTP Expiration:** OTPs are valid for 5 minutes only 3. **Token Expiration:** Access tokens expire after 15 minutes; refresh tokens after 1 day 4. **Security:** Passwords are hashed using Django's default password hasher 5. **CORS:** Currently allows all origins (should be restricted in production) 6. **Database:** Using SQLite for development (consider PostgreSQL for production) 7. **Email:** Using Gmail SMTP (credentials exposed in settings.py - move to environment variables) 8. **Admin Panel:** Not currently configured in URLs --- **End of Documentation**