AgentSkillsCN

api

解释该项目的 API 结构、端点与模式

SKILL.md
--- frontmatter
name: api
description: Explains this project's API structure, endpoints, and patterns

API Guide

Architecture

REST API built with FastAPI (Python). All routes prefixed with /api/v1.

Key Files

  • /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/main.py - FastAPI app entry point, middleware setup
  • /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/app.py - Route registration
  • /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/api/routes/ - Route handlers
  • /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/api/schemas.py - Pydantic request/response models
  • /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/core/dependencies.py - Dependency injection
  • /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/core/config.py - Settings from env vars

Layered Architecture

code
Routes (app/api/routes/) -> Services (app/services/) -> Repositories (app/adapters/repositories/)
                                                     -> Domain Models (app/domain/models/)

Endpoints

Auth (/api/v1/auth)

MethodPathDescription
GET/googleInitiate Google OAuth
GET/google/callbackOAuth callback
POST/registerEmail/password registration
POST/loginEmail/password login
POST/refreshRefresh access token
POST/logoutLogout (revoke refresh token)
GET/meGet current user
POST/test-loginE2E test login (only when E2E_TEST_MODE=true)

Transactions (/api/v1/transactions)

MethodPathDescription
POST``Create transaction
GET``List transactions (paginated, filterable)
GET/exportExport to CSV
GET/category-totalsAggregated totals by category
GET/category-time-seriesTime series data
GET/recurring-patternsDetect recurring expenses
POST/preview-enhancementPreview rule enhancement
PUT/bulk-update-categoryBulk update by description
GET/count-similarCount similar transactions
GET/count-by-categoryCount by category
PUT/bulk-replace-categoryReplace category in bulk
GET/{transaction_id}Get single transaction
PUT/{transaction_id}Update transaction
DELETE/{transaction_id}Delete transaction
PUT/{transaction_id}/categorizeSet category
PUT/{transaction_id}/mark-failureMark categorisation failed
GET/categorization-jobs/{job_id}/statusBackground job status

Statements (/api/v1/statements)

MethodPathDescription
POST/analyzeUpload and analyse statement file
POST/{uploaded_file_id}/preview-statisticsPreview with filters
POST/uploadProcess and save statement
GET``List all statements
DELETE/{statement_id}Delete statement and transactions

Categories (/api/v1/categories)

MethodPathDescription
POST``Create category
GET``List all categories
GET/rootList root categories only
GET/exportExport to CSV
GET/{category_id}Get category
GET/{category_id}/subcategoriesGet subcategories
PUT/{category_id}Update category
DELETE/{category_id}Delete category
POST/uploadUpload categories CSV
POST/ai/generate-suggestionsAI suggest new categories
POST/ai/create-selectedCreate AI-suggested categories

Accounts (/api/v1/accounts)

MethodPathDescription
POST``Create account
GET``List all accounts
GET/exportExport to CSV
GET/{account_id}Get account
PUT/{account_id}Update account
DELETE/{account_id}Delete account
PUT/{account_id}/initial-balanceSet initial balance
DELETE/{account_id}/initial-balanceDelete initial balance
POST/uploadUpload accounts CSV

Enhancement Rules (/api/v1/enhancement-rules)

MethodPathDescription
GET``List rules (paginated, filterable)
GET/statsRule statistics
POST/cleanup-unusedDelete unused rules
GET/{rule_id}/matching-transactions/countCount matching transactions
POST/preview/matching-transactions/countPreview rule match count
GET/{rule_id}Get rule
POST``Create rule
PUT/{rule_id}Update rule
DELETE/{rule_id}Delete rule
POST/ai/suggest-categoriesAI suggest categories for rules
POST/ai/suggest-counterpartiesAI suggest counterparties
POST/{rule_id}/ai-suggestion/applyApply AI suggestion
POST/{rule_id}/ai-suggestion/rejectReject AI suggestion

Other Endpoints

  • Description Groups (/api/v1/description-groups) - Group similar descriptions
  • Saved Filters (/api/v1/saved-filters) - Temporary saved transaction selections
  • Filter Presets (/api/v1/filter-presets) - Named filter configurations

Authentication

Method

Cookie-based JWT authentication with refresh tokens.

Flow

  1. User authenticates via OAuth or email/password
  2. Server sets access_token (15 min) and refresh_token (7 days) cookies
  3. Cookies are httponly, secure (if HTTPS), samesite=lax|none
  4. Access token decoded from cookie on each request

Protecting Routes

python
from app.api.routes.auth import require_current_user

@router.get("/protected")
def protected_endpoint(
    current_user: User = Depends(require_current_user),
):
    # current_user guaranteed to be authenticated

Key Functions (/Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/api/routes/auth.py)

  • require_current_user - Dependency, raises 401 if not authenticated
  • get_current_user_from_cookie - Returns User or None
  • _set_auth_cookies / _clear_auth_cookies - Cookie management

Request/Response Patterns

Pydantic Schemas

All request/response models in /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/api/schemas.py.

python
class CategoryCreate(BaseModel):
    name: str
    parent_id: Optional[UUID] = None
    color: Optional[str] = None

class CategoryResponse(BaseModel):
    id: UUID
    name: str
    color: Optional[str] = None
    parent_id: Optional[UUID] = None

    model_config = ConfigDict(from_attributes=True)

Naming Conventions

  • *Create - POST request body
  • *Update - PUT request body
  • *Response - Response body
  • *ListResponse - Paginated list response

Pagination Pattern

python
class TransactionListResponse(BaseModel):
    transactions: List[TransactionResponse]
    total: int
    page: int
    page_size: int
    total_pages: int

Query parameters: page (1-based), page_size (default 20, max 100)

List Responses

Non-paginated lists use:

python
class CategoryListResponse(BaseModel):
    categories: Sequence[CategoryResponse]
    total: int

Error Handling

HTTP Exceptions

python
from fastapi import HTTPException, status

raise HTTPException(
    status_code=status.HTTP_404_NOT_FOUND,
    detail=f"Category with ID {category_id} not found",
)

Standard Error Codes

CodeUsage
400Bad request, validation error
401Not authenticated
403Forbidden (e.g., test endpoint in prod)
404Resource not found
409Conflict (e.g., duplicate name)
422Validation error (automatic from Pydantic)
500Server error

Pattern

python
@router.post("")
def create_resource(data: CreateRequest, ...):
    try:
        # business logic
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error: {str(e)}")

Adding New Endpoints

1. Create/Update Schemas

Add Pydantic models to /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/api/schemas.py:

python
class NewResourceCreate(BaseModel):
    name: str
    ...

class NewResourceResponse(BaseModel):
    id: UUID
    name: str
    ...
    model_config = ConfigDict(from_attributes=True)

2. Create Route File

Create /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/api/routes/new_resource.py:

python
from typing import Callable, Iterator
from uuid import UUID

from fastapi import APIRouter, Depends, FastAPI, HTTPException, status

from app.api.routes.auth import require_current_user
from app.api.schemas import NewResourceCreate, NewResourceResponse
from app.core.config import settings
from app.core.dependencies import InternalDependencies
from app.domain.models.user import User


def register_new_resource_routes(
    app: FastAPI,
    provide_dependencies: Callable[[], Iterator[InternalDependencies]],
):
    router = APIRouter(prefix="/new-resources", tags=["new-resources"])

    @router.post(
        "",
        response_model=NewResourceResponse,
        status_code=status.HTTP_201_CREATED,
    )
    def create_new_resource(
        data: NewResourceCreate,
        internal: InternalDependencies = Depends(provide_dependencies),
        current_user: User = Depends(require_current_user),
    ):
        resource = internal.new_resource_service.create(
            user_id=current_user.id,
            name=data.name,
        )
        return resource

    @router.get("", response_model=NewResourceListResponse)
    def list_new_resources(
        internal: InternalDependencies = Depends(provide_dependencies),
        current_user: User = Depends(require_current_user),
    ):
        resources = internal.new_resource_service.get_all(current_user.id)
        return NewResourceListResponse(resources=resources, total=len(resources))

    app.include_router(router, prefix=settings.API_V1_STR)

3. Register Routes

Update /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/app.py:

python
from app.api.routes.new_resource import register_new_resource_routes

def register_app_routes(app, provide_dependencies):
    # ... existing routes ...
    register_new_resource_routes(app, provide_dependencies)

4. Add Dependencies (if needed)

Update /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/core/dependencies.py:

  • Add repository/service to InternalDependencies class
  • Instantiate in build_internal_dependencies()

Middleware

Configured in /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/main.py:

  1. Request timing - Logs request duration
  2. SessionMiddleware - For OAuth state
  3. CORSMiddleware - Cross-origin requests from frontend

Background Jobs

For long-running operations (e.g., AI categorisation):

  1. Create job via BackgroundJobService
  2. Return job ID to client
  3. Client polls /transactions/categorization-jobs/{job_id}/status
  4. Job processor runs async via FastAPI BackgroundTasks

Environment Variables

Key settings in /Users/pedrosousa/Work/Personal/Code/statements-ai-v7/bank-statements-api/app/core/config.py:

  • DATABASE_URL - PostgreSQL connection
  • API_BASE_URL / WEB_BASE_URL - For OAuth redirects
  • JWT_SECRET_KEY - Token signing
  • GOOGLE_OAUTH_CLIENT_ID/SECRET - OAuth credentials
  • GEMINI_API_KEY / GROQ_API_KEY - LLM providers
  • E2E_TEST_MODE - Enables test login endpoint