Central REST API gateway for the GrantFlow.AI platform, providing Firebase authentication, multi-tenant organization management, and Pub/Sub orchestration of the document processing pipeline.
For prerequisites, environment setup, and general development workflow, see the Contributing Guide.
This README covers backend service-specific architecture and development details.
The backend service is the primary API gateway that coordinates all platform operations:
- Authentication & Authorization: Firebase JWT validation with organization-level claims
- Multi-tenant Architecture: Organization-based data isolation with role-based access control
- API Gateway: RESTful endpoints for all platform entities (organizations, projects, grant applications, templates)
- Real-time Notifications: WebSocket connections for RAG job progress updates
- Service Orchestration: Pub/Sub message publishing to indexer, crawler, and RAG services
- Webhook Handlers: OIDC-authenticated endpoints for email notifications, grant matching, entity cleanup
services/backend/src/
├── api/
│ ├── middleware.py # AuthMiddleware, TraceIdMiddleware
│ ├── routes/ # 15 REST API route modules
│ │ ├── auth.py # User authentication, login
│ │ ├── organizations.py # Organization CRUD
│ │ ├── organizations_members.py
│ │ ├── organization_invitations.py
│ │ ├── projects.py # Project management
│ │ ├── grant_applications.py # Grant application lifecycle
│ │ ├── grant_templates.py # Template generation
│ │ ├── granting_institutions.py
│ │ ├── grants.py # Public grant search
│ │ ├── sources.py # RAG source management
│ │ ├── rag_jobs.py # RAG job monitoring
│ │ ├── files.py # GCS signed URLs
│ │ ├── notifications.py # Notification history
│ │ └── user.py # User profile
│ ├── sockets/
│ │ └── grant_applications.py # WebSocket for RAG progress
│ └── webhooks/
│ ├── email_sending.py # /webhooks/pubsub/email-notifications
│ ├── grant_matcher.py # /webhooks/scheduler/grant-matcher
│ └── entity_cleanup.py # /webhooks/scheduler/entity-cleanup
├── utils/
│ ├── jwt.py # Firebase JWT verification
│ ├── oidc_auth.py # Webhook OIDC token validation
│ ├── email.py # Email sending utilities
│ ├── docx.py # Document export
│ └── pdf.py # PDF generation
└── common_types.py # Shared TypedDict definitions
sequenceDiagram
participant Client
participant AuthMiddleware
participant RouteHandler
participant Database
participant PubSub
participant RAGService
Note over Client,RAGService: REST API Request Flow
Client->>AuthMiddleware: POST /organizations/{id}/projects/{id}/applications
AuthMiddleware->>AuthMiddleware: Verify Firebase JWT
AuthMiddleware->>Database: Check organization membership & role
AuthMiddleware->>RouteHandler: Forward with firebase_uid
RouteHandler->>Database: Create/Update entities
RouteHandler->>PubSub: Publish rag-processing message
RouteHandler->>Client: Return response
Note over Client,RAGService: WebSocket Connection Flow
Client->>RouteHandler: WS /api/socket/grant-applications/{id}
RouteHandler->>Database: Poll GenerationNotification table
Database-->>RouteHandler: New notifications
RouteHandler-->>Client: Stream progress updates
RAGService->>Database: Insert notifications
RouteHandler-->>Client: Updated application state
Note over Client,RAGService: Webhook Flow
PubSub->>AuthMiddleware: POST /webhooks/pubsub/email-notifications
AuthMiddleware->>AuthMiddleware: Verify OIDC token
AuthMiddleware->>RouteHandler: Forward webhook event
RouteHandler->>Database: Fetch application & users
RouteHandler->>RouteHandler: Send email notifications
RouteHandler->>PubSub: Return 200 OK
All routes require Firebase JWT authentication unless noted as public.
Authentication & Users
/auth- User login, token refresh/user- User profile management
Organization Management
/organizations- Organization CRUD (multi-tenant isolation)/organizations/{id}/members- Member management (OWNER/ADMIN/COLLABORATOR roles)/organizations/{id}/invitations- Invitation system
Project & Application Management
/organizations/{id}/projects- Project CRUD within organization context/organizations/{id}/projects/{id}/applications- Grant application lifecycle/organizations/{id}/projects/{id}/applications/{id}/sources- RAG source attachment/organizations/{id}/rag-jobs- RAG job status monitoring
Grant Discovery & Templates
/grants- Public grant search (no auth required)/granting-institutions- Institution management (backoffice admin)/organizations/{id}/templates- Grant template generation
File & Notification Management
/organizations/{id}/files- GCS signed URL generation/organizations/{id}/notifications- Notification history
/api/socket/grant-applications/{application_id}- Real-time RAG job progress updates
OIDC-authenticated endpoints for async service communication:
/webhooks/pubsub/email-notifications- Email notifications when RAG jobs complete/webhooks/scheduler/grant-matcher- Periodic grant opportunity matching/webhooks/scheduler/entity-cleanup- Soft-delete cleanup tasks
-
rag-processing - Triggers RAG service for content generation
- Payload:
{parent_type, parent_id, trace_id} - Published from: grant_applications.py, grant_templates.py, sources.py
- Payload:
-
autofill-requests - Triggers RAG autofill for specific fields
- Payload:
{parent_id, autofill_type, field_name, context, trace_id} - Published from: grant_applications.py
- Payload:
- JWT Claims: All authenticated requests include
firebase_uid - Organization Context: Routes extract
organization_idfrom URL path - Role Verification: AuthMiddleware validates
allowed_rolesdecorator parameter - Project Access: COLLABORATOR role checks ProjectAccess table for project-level permissions
- Signed URLs: Generated via
/filesendpoint for client-side uploads - Document Storage: PDFs, DOCX exports stored in organization-specific buckets
- Async Sessions: All database operations use SQLAlchemy 2.0 async session factory
- Soft Deletes: Uses
select_active()helpers from packages.db.src.query_helpers - Vector Search: pgvector extension for RAG similarity search (via RAG service)
All endpoints enforce multi-tenant isolation through:
- URL-based Organization Context:
organization_idrequired in path parameters - Middleware Role Validation:
@post('/path', allowed_roles=[UserRoleEnum.COLLABORATOR]) - Database-level Filtering: All queries scoped to
organization_idfrom path - Project-level Access Control: COLLABORATOR role checks ProjectAccess table when
project_idpresent
AuthMiddleware handles three authentication modes:
- Public Paths:
/health,/schema,/grants- no authentication - Webhook Paths: OIDC token validation with audience verification
- Authenticated Paths: Firebase JWT validation + organization/role checks
TraceIdMiddleware provides distributed tracing:
- Extracts
X-Trace-IDheader or generates UUID - Propagates to all database operations and Pub/Sub messages
- Enables request correlation across microservices
All database queries use the select_active() helper to filter soft-deleted entities:
from packages.db.src.query_helpers import select_active
stmt = select_active(GrantApplication).where(GrantApplication.id == application_id)This pattern ensures deleted entities are excluded from all query results without manual deleted_at IS NULL checks.
WebSocket implementation uses polling pattern:
- Client connects to
/api/socket/grant-applications/{id} - Server polls
GenerationNotificationtable every 3 seconds - New notifications pushed to client via WebSocket
- Application state updates streamed in real-time
- RAG service writes notifications to database during job execution