# Pydantic Schemas Used in Securo API for Validation: Complete Reference Guide

> Explore over 20 Pydantic schemas in the Securo API for robust validation and serialization. Learn how Securo uses Pydantic for user management, transactions, and financial data.

- Repository: [securo-finance/securo](https://github.com/securo-finance/securo)
- Tags: api-reference
- Published: 2026-08-28

---

**The Securo API uses over 20 purpose-built Pydantic schemas located in `backend/app/schemas/` to validate requests and serialize responses across user management, transactions, workspaces, assets, and financial data domains.**

Securo Finance's backend architecture relies extensively on **Pydantic** for runtime data validation and API documentation generation. Every incoming request body passes through a strictly typed schema before reaching business logic, ensuring type safety and automatic OpenAPI schema generation. This article maps every validation schema in the codebase, tracing their file locations, dependencies, and usage patterns in FastAPI routes.

---

## Core Schema Architecture

The Securo project organizes Pydantic models hierarchically under `backend/app/schemas/`. Each domain receives its own module, following **single-responsibility principles** for maintainable validation logic.

```

backend/app/schemas/
├── __init__.py
├── workspace.py
├── user.py
├── two_factor.py
├── passkey.py
├── account.py
├── transaction.py
├── transaction_split.py
├── transaction_calendar.py
├── recurring_transaction.py
├── transaction_group_settlement.py
├── payee.py
├── category.py
├── category_group.py
├── budget.py
├── goal.py
├── asset.py
├── asset_group.py
├── asset_import.py
├── bank_connection.py
├── attachment.py
├── export.py
├── import_log.py
├── admin.py
├── rule.py
├── report.py
├── dashboard.py
└── collection.py

```

All schemas inherit from `pydantic.BaseModel` and leverage Pydantic v2 features including `field_validator`, `model_validator`, and `ConfigDict` for strict type enforcement.

---

## User Authentication & Security Schemas

Securo handles sensitive financial data, requiring robust validation for authentication flows, multi-factor authentication, and modern passkey credentials.

### User Schema ([`backend/app/schemas/user.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/user.py))

The foundational identity model validates user registration, profile updates, and public user representations. It typically includes:

- `email`: `EmailStr` with DNS validation
- `password`: `SecretStr` for write operations, excluded from responses
- `full_name`: constrained `str` with length limits
- `is_active`, `is_verified`: boolean flags for account state

```python

# backend/app/schemas/user.py

from pydantic import BaseModel, EmailStr, Field, SecretStr

class UserBase(BaseModel):
    email: EmailStr
    full_name: str = Field(..., min_length=1, max_length=128)

class UserCreate(UserBase):
    password: SecretStr = Field(..., min_length=12)

class User(UserBase):
    id: int
    is_active: bool
    is_verified: bool

    model_config = {"from_attributes": True}

```

### Two-Factor Authentication ([`backend/app/schemas/two_factor.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/two_factor.py))

Validates TOTP enrollment, backup code generation, and verification requests. Critical fields include `totp_secret`, `backup_codes`, and `verified_at` timestamps.

### Passkey Schema ([`backend/app/schemas/passkey.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/passkey.py))

Supports WebAuthn credential registration and authentication with fields for `credential_id`, `public_key`, `sign_count`, and device attestation metadata.

---

## Workspace & Account Management Schemas

Financial applications require strict isolation between organizational boundaries. Securo implements this through workspace-scoped validation.

### Workspace Schema ([`backend/app/schemas/workspace.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/workspace.py))

Validates organizational creation, billing tier selection, and member invitation flows. Key fields:

- `name`: unique workspace identifier
- `slug`: URL-safe normalized name
- `billing_tier`: enum-constrained subscription level
- `fiscal_year_start`: month-day pair for reporting cycles

### Account Schema ([`backend/app/schemas/account.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/account.py))

Represents financial accounts within a workspace, validating:

- `institution_name`: connected bank or manual entry
- `account_type`: checking, savings, credit, investment enum
- `currency_code`: ISO 4217 three-letter code with custom validator
- `initial_balance`: `Decimal` precision for monetary values
- `connection_id`: optional foreign key to `BankConnection`

---

## Transaction Validation Schemas

The transaction domain contains the most complex validation logic, with six specialized schemas handling different financial record types.

### Base Transaction Schema ([`backend/app/schemas/transaction.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/transaction.py))

Core model for single financial transactions with extensive custom validators:

```python

# backend/app/schemas/transaction.py

from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel, field_validator, Field

class Transaction(BaseModel):
    amount: Decimal = Field(..., decimal_places=2, max_digits=15)
    currency: str = Field(..., min_length=3, max_length=3)
    description: str = Field(..., max_length=512)
    posted_at: datetime
    category_id: int | None = None
    payee_id: int | None = None

    @field_validator("currency")
    @classmethod
    def validate_supported_currency(cls, v: str) -> str:
        supported = {"USD", "BRL", "EUR", "GBP", "JPY", "CAD"}
        if v.upper() not in supported:
            raise ValueError(f"Currency {v} not supported. Use: {', '.join(sorted(supported))}")
        return v.upper()

    @field_validator("amount")
    @classmethod
    def validate_non_zero(cls, v: Decimal) -> Decimal:
        if v == 0:
            raise ValueError("Transaction amount cannot be zero")
        return v

```

### Transaction Split Schema ([`backend/app/schemas/transaction_split.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/transaction_split.py))

Validates division of single transactions across multiple categories or payees. Enforces that split percentages sum to 100% or split amounts sum to parent transaction total via `model_validator`.

### Transaction Calendar Schema ([`backend/app/schemas/transaction_calendar.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/transaction_calendar.py))

Schedules future or recurring transactions with fields for `frequency` (daily, weekly, monthly, yearly), `interval` multiplier, `occurrences` limit, and `end_date` termination.

### Recurring Transaction Schema ([`backend/app/schemas/recurring_transaction.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/recurring_transaction.py))

Similar to calendar schema but for automatically generated transactions, including `next_occurrence` prediction and `last_generated` tracking.

### Transaction Group Settlement ([`backend/app/schemas/transaction_group_settlement.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/transaction_group_settlement.py))

Validates multi-party settlement workflows for shared expenses, tracking `payer_splits`, `ower_splits`, and settlement status.

---

## Classification & Budgeting Schemas

Financial organization requires structured categorization and goal tracking.

### Category & CategoryGroup Schemas ([`backend/app/schemas/category.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/category.py), [`backend/app/schemas/category_group.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/category_group.py))

- **Category**: Individual classification with `name`, `color`, `icon`, `parent_id` for hierarchies, and `is_system` flag for protected categories
- **CategoryGroup**: Container for related categories with `sort_order` and budget rollup configuration

### Budget Schema ([`backend/app/schemas/budget.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/budget.py))

Validates spending limits with period-aware validation:

- `amount`: periodic spending ceiling
- `period`: enum of `weekly`, `monthly`, `quarterly`, `yearly`
- `category_ids` or `category_group_ids`: scope specification
- `rollover_enabled`: boolean for unused amount carryover

### Goal Schema ([`backend/app/schemas/goal.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/goal.py))

Tracks financial targets with `target_amount`, `current_amount`, `deadline`, and `goal_type` (savings, debt reduction, purchase target). Includes progress calculation validators.

---

## Asset Management Schemas

Investment and physical asset tracking uses nested validation for complex portfolio structures.

### Asset Schema ([`backend/app/schemas/asset.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/asset.py))

Validates individual holdings:

- `asset_class`: equity, fixed_income, real_estate, commodity, crypto enum
- `symbol`: ticker or identifier with format validation per asset class
- `quantity`: `Decimal` with precision appropriate to asset type
- `cost_basis`: purchase price tracking for gain/loss calculation
- `current_price`: optional live price with `last_price_update` timestamp

### AssetGroup Schema ([`backend/app/schemas/asset_group.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/asset_group.py))

Portfolio-level validation with `allocation_targets` dictionary mapping asset classes to target percentages, enforced by `model_validator` summing to 100%.

### AssetImport Schema ([`backend/app/schemas/asset_import.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/asset_import.py))

Validates bulk import operations from CSV brokerage files, including column mapping validation and duplicate detection rules.

---

## Financial Connection Schemas

Third-party integrations require specialized validation for credentials and connection state.

### BankConnection Schema ([`backend/app/schemas/bank_connection.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/bank_connection.py))

Validates open banking and screen-scraping connections:

- `provider_id`: supported institution identifier
- `connection_type`: `oauth`, `credentials`, `api_key` enum
- `credentials`: `SecretStr` fields conditionally required by type
- `sync_frequency`: `timedelta` with minimum bounds
- `last_sync_at`, `last_sync_status`: operational metadata

---

## Auxiliary Schemas

Supporting functionality validates file attachments, data exports, and administrative operations.

### Attachment Schema ([`backend/app/schemas/attachment.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/attachment.py))

Validates file metadata with **content-type restriction** and size limits:

- `filename`: sanitized string with extension validation
- `content_type`: allowlist of `image/*`, `application/pdf`, `text/csv`
- `size_bytes`: maximum 10MB default
- `checksum`: SHA-256 for integrity verification

### Export Schema ([`backend/app/schemas/export.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/export.py))

Validates data export requests with `format` (CSV, JSON, OFX, QIF), `date_range` bounds, `included_accounts`, and `include_attachments` flag.

### ImportLog Schema ([`backend/app/schemas/import_log.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/import_log.py))

Tracks asynchronous import job status with `source_type`, `file_checksum`, `records_processed`, `records_failed`, and `error_details` array.

### Admin Schema ([`backend/app/schemas/admin.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/admin.py))

Validates privileged operations with enhanced secret handling. Uses `SecretStr` for sensitive configuration and includes `audit_reason` fields for compliance logging.

### Rule Schema ([`backend/app/schemas/rule.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/rule.py))

Validates automated transaction categorization with `conditions` (field, operator, value triples) and `actions` (set_category, set_payee, flag_for_review).

### Report, Dashboard, Collection Schemas

- **Report**: Scheduled or on-demand analytics with `metric_selections`, `grouping_dimensions`, `filter_criteria`
- **Dashboard**: Widget layout validation with position coordinates and refresh intervals
- **Collection**: Saved filter combinations for quick data access

---

## Integration with FastAPI Routes

Pydantic schemas integrate directly into route definitions for automatic validation and documentation:

```python

# backend/app/api/payees.py

from fastapi import APIRouter, Depends
from app.schemas.payee import Payee, PayeeCreate
from app.api.dependencies import get_current_user, get_db_session

router = APIRouter(prefix="/payees", tags=["payees"])

@router.post("/", response_model=Payee, status_code=201)
async def create_payee(
    payee: PayeeCreate,  # ← Pydantic validation applied automatically

    session=Depends(get_db_session),
    current_user=Depends(get_current_user)
):
    """Create a new payee with validated tax ID and source information."""
    return await payee_service.create(session, current_user.workspace_id, payee)

```

The `PayeeCreate` schema validates incoming JSON, returning **422 Unprocessable Entity** for type mismatches or validation failures before the service layer executes.

---

## Configuration via Pydantic-Settings

Beyond request validation, Securo uses `pydantic-settings` for environment-based configuration in [`backend/app/core/config.py`](https://github.com/securo-finance/securo/blob/main/backend/app/core/config.py):

```python

# backend/app/core/config.py

from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import SecretStr, PostgresDsn, RedisDsn

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", extra="ignore")
    
    DATABASE_URL: PostgresDsn
    REDIS_URL: RedisDsn
    SECRET_KEY: SecretStr
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
    SUPPORTED_CURRENCIES: list[str] = ["USD", "BRL", "EUR"]
    
    @property
    def async_database_url(self) -> str:
        return str(self.DATABASE_URL).replace("postgresql://", "postgresql+asyncpg://")

settings = Settings()

```

This pattern separates **runtime configuration** (secrets, URLs) from **request validation schemas** while maintaining Pydantic's validation guarantees.

---

## Summary

- **20+ specialized schemas** in `backend/app/schemas/` cover all API validation needs
- **Hierarchical organization** by domain (user, transaction, asset, etc.) enables maintainable code
- **Pydantic v2 features** employed: `field_validator`, `model_validator`, `ConfigDict`, `SecretStr`
- **Custom validators** enforce business rules like currency support, split totaling, and allocation balancing
- **FastAPI integration** provides automatic request validation, serialization, and OpenAPI documentation
- **Pydantic-Settings** handles environment configuration with identical validation rigor

---

## Frequently Asked Questions

### How does Securo ensure currency values are validated correctly?

The base `Transaction` schema in [`backend/app/schemas/transaction.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/transaction.py) implements a `@field_validator("currency")` that checks against a `supported` set of ISO 4217 codes. Invalid currencies trigger a descriptive error listing all supported options. Monetary amounts use `Decimal` with explicit `decimal_places=2` to prevent floating-point errors.

### What prevents split transactions from having invalid totals?

The `TransactionSplit` schema in [`backend/app/schemas/transaction_split.py`](https://github.com/securo-finance/securo/blob/main/backend/app/schemas/transaction_split.py) uses a `@model_validator(mode="after")` that executes after individual field validation. This validator sums all split amounts or percentages and compares against the parent transaction total, raising `ValueError` if the difference exceeds a configurable epsilon.

### Are password hashes exposed in API responses?

No. The `User` schema hierarchy separates `UserCreate` (input, contains `SecretStr` password) from `User` (response model). The response model never includes password fields. Additionally, `SecretStr` automatically masks values in logs and serialization, and `model_config = {"from_attributes": True}` ensures proper ORM mapping without accidental field leakage.