Skip to content

Architecture Overview

Subscribe Flow is a multi-tenant SaaS platform following a layered architecture with strict per-organization data isolation.

System Architecture

graph TB
    subgraph "Layer 1: Clients"
        PC[Preference Center UI]
        AD[Admin Dashboard]
        SDK[SDK / API Client]
        MCP[MCP Server]
        EXT[External Systems]
    end

    subgraph "Layer 2: API Gateway"
        FA[FastAPI Application]
        AUTH[Dual Auth: Magic Link + API Key]
        RL[Per-Org Rate Limiter]
        VAL[Request Validation]
        TENANT[Tenant Resolution]
    end

    subgraph "Layer 3: Business Logic"
        SS[Subscriber Service]
        TS[Tag Service]
        PS[Preference Service]
        RS[Resend Sync Service]
        WH[Webhook Service]
        OS[Organization Service]
        BS[Billing / Payment Service]
        US[User Service]
        MLS[Magic Link Service]
        DS[Domain Service]
        EU[Email Utils]
    end

    subgraph "Layer 4: Data Layer"
        REPO[Repositories]
        CACHE[Cache Layer]
        PG[(PostgreSQL)]
        RD[(Redis)]
    end

    subgraph "Layer 5: External Services"
        RESEND[Resend API]
        STRIPE[Stripe API]
        WEBHOOKS[Webhook Endpoints]
    end

    PC --> FA
    AD --> FA
    SDK --> FA
    MCP --> FA
    EXT --> FA

    FA --> AUTH
    AUTH --> TENANT
    TENANT --> RL
    RL --> VAL

    VAL --> SS
    VAL --> TS
    VAL --> PS
    VAL --> WH
    VAL --> OS
    VAL --> BS
    VAL --> US

    SS --> REPO
    TS --> REPO
    PS --> REPO
    OS --> REPO
    BS --> REPO
    US --> REPO
    SS --> CACHE
    MLS --> CACHE

    RS --> RESEND
    DS --> RESEND
    BS --> STRIPE
    WH --> WEBHOOKS

    REPO --> PG
    CACHE --> RD

Multi-Tenant Isolation

All data access is scoped to the authenticated organization. The tenant resolution flow works as follows:

  1. API Key Auth: API key is looked up, the associated organization_id is extracted
  2. Magic Link Auth (Dashboard): User session contains organization_id via OrganizationMembers
  3. Service Layer: Every service method receives the organization_id and filters all queries accordingly
  4. Database: All tenant-scoped tables include an organization_id foreign key with indexed lookups
sequenceDiagram
    participant C as Client
    participant G as API Gateway
    participant A as Auth (Dual)
    participant T as Tenant Resolution
    participant S as Service Layer
    participant R as Repository
    participant D as Database

    C->>G: HTTP Request
    G->>A: Authenticate (API Key or Session)
    A->>T: Resolve Organization
    T-->>G: Org Context (organization_id, tier, limits)
    G->>S: Business Operation (org-scoped)
    S->>R: Data Operation (filtered by org_id)
    R->>D: SQL Query (WHERE organization_id = ...)
    D-->>R: Result
    R-->>S: Domain Object
    S-->>G: Response DTO
    G-->>C: HTTP Response

Components

Layer 1: Clients

Component Description
Preference Center UI Embeddable React component for self-service
Admin Dashboard React SPA dashboard for administrators
SDK TypeScript/Python SDK for developers
MCP Server Model Context Protocol server for LLM agents
External Systems CRM, e-commerce, marketing automation

Layer 2: API Gateway

Component Responsibility
FastAPI Request routing, OpenAPI documentation
Dual Auth Magic Link sessions (Dashboard) + API Key auth (SDK/API)
Tenant Resolution Extract organization_id from auth context
Per-Org Rate Limiter Rate limiting scoped to organization tier
Validation Pydantic request/response validation

Layer 3: Business Logic

Service Responsibility
Subscriber Service CRUD, validation, status management (org-scoped)
Tag Service Tag hierarchy, categories (org-scoped)
Preference Service Subscription logic, audit trail
Resend Sync Bidirectional synchronization
Webhook Service Event dispatching (org-scoped)
Organization Service Org CRUD, settings, member management
Billing / Payment Service Stripe integration, tier management, usage tracking
User Service User CRUD, org membership
Magic Link Service Magic link generation, email sending, token verification
Domain Service Custom email domain verification via Resend API
Email Utils Per-org from address resolution, send domain management

Layer 4: Data Layer

Component Technology Purpose
Repositories SQLAlchemy Data access abstraction
Cache Redis Session, rate limits, magic link tokens
PostgreSQL PostgreSQL 15 Primary data storage
Redis Redis 7 Caching, queue, Celery broker

Layer 5: External Services

Service Integration
Resend API Email sending, contact sync, domain verification
Stripe API Billing, checkout, customer portal, webhooks
Webhooks Event notifications to customer endpoints

Dual Authentication

Subscribe Flow supports two authentication mechanisms:

Method Used By Flow
Magic Link Admin Dashboard users Request -> Email -> Click -> Session cookie
API Key SDK, API clients, MCP X-API-Key: sf_live_xxx header

Both methods resolve to an organization_id that scopes all subsequent operations.

Design Principles

1. API-First

The API is the primary interface. All functionality is available via REST.

Text Only
1
2
3
4
POST   /api/v1/subscribers      # Create
GET    /api/v1/subscribers/:id  # Read
PATCH  /api/v1/subscribers/:id  # Update
DELETE /api/v1/subscribers/:id  # Delete

2. Multi-Tenant by Default

Every resource belongs to an organization. There is no global data access.

3. Event-Driven

Changes trigger events that are communicated via webhooks:

flowchart LR
    A[Preference Change] --> B[Event Created]
    B --> C[Webhook Dispatched]
    C --> D[External System]

4. Audit Trail

Every change is logged:

SQL
1
2
3
SELECT * FROM subscription_history
WHERE subscriber_id = 'uuid'
ORDER BY changed_at DESC;

Scaling

Horizontal Scaling

  • API Server: Stateless, scales horizontally without limits
  • Background Workers: Celery for async tasks
  • Celery-Beat: Scheduled tasks (e.g., monthly email counter reset)
  • Database: Read replicas for read operations

Caching Strategy

flowchart LR
    A[Request] --> B{Cache Hit?}
    B -->|Yes| C[Return Cached]
    B -->|No| D[Query DB]
    D --> E[Update Cache]
    E --> F[Return Response]

Further Documentation