# How API Endpoints Are Organized in Securo: A FastAPI Router Architecture Guide

> Discover how Securo organizes API endpoints using FastAPI's APIRouter pattern. Learn about domain-specific modules and central registration for a clean architecture. Read the guide now!

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

---

**Securo organizes its REST API endpoints using FastAPI's `APIRouter` pattern, with domain-specific modules in `backend/app/api/` each defining their own URL prefix and tags, then centrally registered in [`backend/app/main.py`](https://github.com/securo-finance/securo/blob/main/backend/app/main.py).**

Securo's backend follows a clean, domain-driven design that makes its API structure predictable and maintainable. Built on **FastAPI**, the codebase splits every logical group of HTTP endpoints into dedicated router modules. This article breaks down exactly how the securo-finance/securo repository structures its API layer, with specific file paths and wiring patterns you can apply to your own FastAPI projects.

## Router Module Structure in backend/app/api/

Each business domain gets its own Python file under `backend/app/api/`. These files create `APIRouter` instances with distinct **prefixes** and **tags**, then export the router for central registration.

| Domain | Router Prefix | Source File |
|--------|---------------|-------------|
| Workspaces | `/api/workspaces` | [[`workspaces.py`](https://github.com/securo-finance/securo/blob/main/workspaces.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/workspaces.py) |
| Users / Lookup | `/api/users` | [[`user_lookup.py`](https://github.com/securo-finance/securo/blob/main/user_lookup.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/user_lookup.py) |
| Accounts | `/api/accounts` | [[`accounts.py`](https://github.com/securo-finance/securo/blob/main/accounts.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/accounts.py) |
| Transactions | `/api/transactions` | [[`transactions.py`](https://github.com/securo-finance/securo/blob/main/transactions.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/transactions.py) |
| Import Transactions | `/api/transactions` (tags = "import") | [[`import_transactions.py`](https://github.com/securo-finance/securo/blob/main/import_transactions.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/import_transactions.py) |
| Import Logs | `/api/import-logs` | [[`import_logs.py`](https://github.com/securo-finance/securo/blob/main/import_logs.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/import_logs.py) |
| Recurring Transactions | `/api/recurring-transactions` | [[`recurring_transactions.py`](https://github.com/securo-finance/securo/blob/main/recurring_transactions.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/recurring_transactions.py) |
| Assets | `/api/assets` | [[`assets.py`](https://github.com/securo-finance/securo/blob/main/assets.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/assets.py) |
| Asset Groups | `/api/asset-groups` | [[`asset_groups.py`](https://github.com/securo-finance/securo/blob/main/asset_groups.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/asset_groups.py) |
| Budgets | `/api/budgets` | [[`budgets.py`](https://github.com/securo-finance/securo/blob/main/budgets.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/budgets.py) |
| Goals | `/api/goals` | [[`goals.py`](https://github.com/securo-finance/securo/blob/main/goals.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/goals.py) |
| Categories | `/api/categories` | [[`categories.py`](https://github.com/securo-finance/securo/blob/main/categories.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/categories.py) |
| Category Groups | `/api/category-groups` | [[`category_groups.py`](https://github.com/securo-finance/securo/blob/main/category_groups.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/category_groups.py) |
| Payees | `/api/payees` | [[`payees.py`](https://github.com/securo-finance/securo/blob/main/payees.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/payees.py) |
| Groups | `/api/groups` | [[`groups.py`](https://github.com/securo-finance/securo/blob/main/groups.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/groups.py) |
| Rules | `/api/rules` | [[`rules.py`](https://github.com/securo-finance/securo/blob/main/rules.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/rules.py) |
| Settings | `/api/settings` | [[`settings.py`](https://github.com/securo-finance/securo/blob/main/settings.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/settings.py) |
| Dashboard | `/api/dashboard` | [[`dashboard.py`](https://github.com/securo-finance/securo/blob/main/dashboard.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/dashboard.py) |
| Reports | `/api/reports` | [[`reports.py`](https://github.com/securo-finance/securo/blob/main/reports.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/reports.py) |
| Search | `/api/search` | [[`search.py`](https://github.com/securo-finance/securo/blob/main/search.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/search.py) |
| Export | `/api/export` | [[`export.py`](https://github.com/securo-finance/securo/blob/main/export.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/export.py) |
| Attachments | `/api/attachments` | [[`attachments.py`](https://github.com/securo-finance/securo/blob/main/attachments.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/attachments.py) |
| Collections | `/api/collections` | [[`collections.py`](https://github.com/securo-finance/securo/blob/main/collections.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/collections.py) |
| Connections | `/api/connections` | [[`connections.py`](https://github.com/securo-finance/securo/blob/main/connections.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/connections.py) |
| Currencies | `/api/currencies` | [[`currencies.py`](https://github.com/securo-finance/securo/blob/main/currencies.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/currencies.py) |
| FX Rates | `/api/fx-rates` | [[`fx_rates.py`](https://github.com/securo-finance/securo/blob/main/fx_rates.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/fx_rates.py) |
| Fiscal | `/api/fiscal` | [[`fiscal.py`](https://github.com/securo-finance/securo/blob/main/fiscal.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/fiscal.py) |
| Admin | `/api/admin` | [[`admin.py`](https://github.com/securo-finance/securo/blob/main/admin.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/admin.py) |
| Two-Factor Auth | `/api/2fa` (router without prefix) | [[`two_factor.py`](https://github.com/securo-finance/securo/blob/main/two_factor.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/two_factor.py) |
| Passkeys | `/api/passkeys` (router without prefix) | [[`passkeys.py`](https://github.com/securo-finance/securo/blob/main/passkeys.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/passkeys.py) |
| OIDC Auth | `/api/auth/oidc` | [[`oidc_auth.py`](https://github.com/securo-finance/securo/blob/main/oidc_auth.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/oidc_auth.py) |
| Info (general) | `/api` | [[`info.py`](https://github.com/securo-finance/securo/blob/main/info.py)](https://github.com/securo-finance/securo/blob/main/backend/app/api/info.py) |

### Special Case: Routers Without Prefixes

Some authentication modules define routes with explicit path prefixes baked into their endpoint decorators rather than the `APIRouter` prefix parameter. This pattern appears in:

- [`two_factor.py`](https://github.com/securo-finance/securo/blob/main/two_factor.py) — routes explicitly start with `/api/2fa`
- [`passkeys.py`](https://github.com/securo-finance/securo/blob/main/passkeys.py) — routes explicitly start with `/api/passkeys`

## Central Router Registration in main.py

The FastAPI application instance lives in [`backend/app/main.py`](https://github.com/securo-finance/securo/blob/main/backend/app/main.py). This file imports all router modules and mounts them with `app.include_router()`.

```python

# Simplified structure as implemented in securo-finance/securo

from fastapi import FastAPI
from app.api import (
    workspaces,
    user_lookup,
    transactions,
    assets,
    budgets,
    categories,
    # ... all other router modules

)

app = FastAPI(title="Securo API")

app.include_router(workspaces.router)
app.include_router(user_lookup.router)
app.include_router(transactions.router)
app.include_router(assets.router)
app.include_router(budgets.router)

# ... remaining routers

```

This central registration pattern ensures:

- **No circular imports** — routers depend on [`main.py`](https://github.com/securo-finance/securo/blob/main/main.py), not each other
- **Consistent middleware stack** — all routes inherit the same CORS, authentication, and logging layers
- **Single source of truth** — one file shows the complete API surface

## Agent-Specific API Layer

Securo extends its API organization for AI agent functionality through a separate subpackage. Agent-related endpoints reside in `backend/app/agents/api/` and follow the same router pattern.

| Domain | Router Prefix | Source File |
|--------|---------------|-------------|
| Agents (various sub-domains) | `/api/agents/*` | [[`agents.py`](https://github.com/securo-finance/securo/blob/main/agents.py)](https://github.com/securo-finance/securo/blob/main/backend/app/agents/api/agents.py) |
| Knowledge | `/api/agents/knowledge` | [[`knowledge.py`](https://github.com/securo-finance/securo/blob/main/knowledge.py)](https://github.com/securo-finance/securo/blob/main/backend/app/agents/api/knowledge.py) |
| Conversations | `/api/agents/conversations` | [[`conversations.py`](https://github.com/securo-finance/securo/blob/main/conversations.py)](https://github.com/securo-finance/securo/blob/main/backend/app/agents/api/conversations.py) |

This separation keeps agent logic isolated from core financial operations while maintaining architectural consistency.

## Design Principles Behind Securo's API Organization

### URL Prefixes Mirror Resource Hierarchy

Every prefix nests under `/api/` and uses kebab-case plural nouns. This creates intuitive URLs:

```

/api/workspaces/{id}/accounts
/api/transactions?workspace_id={id}
/api/assets/{id}/prices

```

### Tags Enable OpenAPI Documentation Grouping

Each router module assigns relevant **tags** to its endpoints. These appear in the auto-generated Swagger UI at `/docs`, grouping related operations visually.

```python

# Typical router initialization pattern

from fastapi import APIRouter

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

```

### Module Size Stays Bounded

No single file accumulates unrelated endpoints. Even closely related domains like `transactions` and `import_transactions` split into separate modules based on operation type (CRUD vs. data ingestion).

## Practical API Examples

### Creating a Workspace

```python
import httpx

async def create_workspace(name: str):
    async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
        response = await client.post(
            "/api/workspaces",
            json={"name": name}
        )
        return response.json()

# → {"id": "ws_abc123", "name": "Personal Finance", "created_at": "2024-01-15T..."}

```

### Listing Assets with Filters

```python
async def list_stocks(workspace_id: str):
    async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
        response = await client.get(
            "/api/assets",
            params={"workspace_id": workspace_id, "type": "stock"}
        )
        return response.json()

# → [{"id": "ast_001", "type": "stock", "symbol": "AAPL", "quantity": 50.0}, ...]

```

### Batch Import Transactions

```python
async def import_csv(workspace_id: str, file_content: bytes):
    async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
        response = await client.post(
            "/api/transactions/import",
            params={"workspace_id": workspace_id},
            files={"file": ("transactions.csv", file_content, "text/csv")}
        )
        return response.json()

# → {"import_id": "imp_789", "rows_processed": 142, "errors": []}

```

## Key Files for Understanding Securo API Organization

| File Path | Purpose |
|-----------|---------|
| [`backend/app/main.py`](https://github.com/securo-finance/securo/blob/main/backend/app/main.py) | FastAPI app factory; central router registration |
| [`backend/app/api/__init__.py`](https://github.com/securo-finance/securo/blob/main/backend/app/api/__init__.py) | Package exports (if present) |
| `backend/app/api/{domain}.py` | Individual router modules (30+ files) |
| `backend/app/agents/api/` | Agent-specific router subpackage |
| [`backend/app/core/config.py`](https://github.com/securo-finance/securo/blob/main/backend/app/core/config.py) | Settings including API versioning |
| `backend/app/dependencies/` | Shared FastAPI dependencies for auth, DB sessions |

## Summary

- **Domain-driven modules**: Each business concept lives in its own file under `backend/app/api/`
- **Explicit prefixes**: Every `APIRouter` declares its `/api/{resource}` prefix at initialization
- **Central registration**: [`backend/app/main.py`](https://github.com/securo-finance/securo/blob/main/backend/app/main.py) imports and mounts all routers with `include_router()`
- **Tags for documentation**: Logical grouping in OpenAPI/Swagger through consistent tag usage
- **Agent separation**: Extended API surface in `backend/app/agents/api/` follows identical patterns

## Frequently Asked Questions

### What pattern does Securo use for FastAPI routing?

Securo uses **FastAPI's `APIRouter` pattern** with domain-specific modules. Each file in `backend/app/api/` creates a router with a dedicated prefix and tags, then exports it for central registration in [`main.py`](https://github.com/securo-finance/securo/blob/main/main.py). This is the recommended FastAPI pattern for applications with more than a handful of endpoints.

### How does Securo handle authentication-related API routes?

Authentication routes split across multiple specialized modules. **OIDC flows** live in [`oidc_auth.py`](https://github.com/securo-finance/securo/blob/main/oidc_auth.py) with prefix `/api/auth/oidc`. **Two-factor authentication** and **passkeys** each have dedicated modules ([`two_factor.py`](https://github.com/securo-finance/securo/blob/main/two_factor.py), [`passkeys.py`](https://github.com/securo-finance/securo/blob/main/passkeys.py)) that define explicit paths rather than router prefixes. This allows flexible grouping of related but distinct security mechanisms.

### Where are agent-specific endpoints located?

Agent API endpoints reside in `backend/app/agents/api/` rather than the main `backend/app/api/` directory. This subpackage contains [`agents.py`](https://github.com/securo-finance/securo/blob/main/agents.py), [`knowledge.py`](https://github.com/securo-finance/securo/blob/main/knowledge.py), [`conversations.py`](https://github.com/securo-finance/securo/blob/main/conversations.py), and related files, all using the same `APIRouter` pattern with prefixes under `/api/agents/`. The separation prevents agent logic from polluting core financial domain code.

### Can I add new endpoints without modifying main.py?

In Securo's architecture, **yes, but with a required registration step**. You create a new module in `backend/app/api/` with a router, then explicitly import and include it in [`backend/app/main.py`](https://github.com/securo-finance/securo/blob/main/backend/app/main.py). There is no auto-discovery mechanism — explicit registration ensures intentional API surface changes and prevents accidental exposure of development or test endpoints.