Why Rapto AI Needs This Architecture
Modern meeting assistants typically suffer from two severe structural shortcomings:
Rapto AI is engineered specifically to solve the Accountability Gap—turning ephemeral spoken conversations into persistent, tracked commitments that bi-directionally sync with developer issue trackers (Linear, Jira) and verify resolution across successive meetings.
To deliver this at production scale, the platform must satisfy five demanding technical requirements:
High-Level System Architecture
Rapto AI employs a decoupled microservices architecture separated along functional domain boundaries: an edge reverse proxy gateway, a Node.js Core API, a Python AI extraction microservice, an asynchronous BullMQ worker cluster, and a polyglot data tier.
Core Services Breakdown
The backend is partitioned into three specialized runtime environments:
1. Node.js Core API Gateway (Express + TypeScript)
Located in services/api, this service runs on Node.js 20+ with Express and TypeScript. It acts as the central business logic controller, multi-tenant authentication provider, and external webhook listener.
teamId partition rules using Prisma ORM middleware and RBAC/ABAC permission checks (OWNER, ADMIN, MANAGER, MEMBER).calendar-sync.service.ts for polling Google Calendar and Outlook Graph deltas, and dedup.service.ts to ensure only one bot joins a multi-attendee meeting.owner-resolver.service.ts to map fuzzy speech tags (e.g., "Sarah", "sarah@company.com", "Sarah C") to verified team member IDs.2. Python AI Pipeline Microservice (FastAPI + Pydantic)
Located in services/ai-pipeline, this service runs on Python 3.11 with FastAPI and Uvicorn. It is dedicated strictly to compute-heavy natural language processing, semantic similarity calculations, prompt template compilation, and OpenAI API orchestration.
chunker.py): Splits raw transcripts into token-bounded segments (EXTRACTION_CHUNK_MAX_TOKENS = 3000) with overlapping dialogue turns (EXTRACTION_CHUNK_OVERLAP_TURNS = 2) to preserve conversational context across split boundaries.extractor.py): Executes concurrent multi-chunk extraction via asyncio.gather(return_exceptions=True), followed by a meta-summary synthesis pass._CROSS_CHUNK_DEDUP_THRESHOLD = 0.70).resolution_detector.py & commitment_resolver.py): Evaluates active open commitments from historical meetings against current conversational utterances to detect fulfillment, blockers, or timeline amendments.3. Distributed Background Workers (BullMQ + Redis)
Running as a dedicated container (rapto-worker), this Node.js process consumes jobs from Redis-backed BullMQ queues. It decouples long-running asynchronous workflows from client HTTP latency.
Request / API Data Flow
Every external HTTP request entering Rapto AI passes through an engineered validation, authentication, and rate-limiting pipeline:
sequenceDiagram
autonumber
actor User as Web / Mobile Client
participant Nginx as NGINX Reverse Proxy
participant API as Node.js API Gateway
participant Redis as Redis 7 (Rate Limit & Cache)
participant Postgres as PostgreSQL (Prisma ORM)
participant Worker as BullMQ Queue
User->>Nginx: POST /api/v1/meetings/{id}/process (Bearer JWT)
Nginx->>Nginx: Rate Limit Check (Zone: api_limit)
Nginx->>API: Proxy to http://api:5000
API->>API: Validate JWT Signature & Expiry
API->>Redis: Check User Session & Permissions Cache
alt Cache Miss
API->>Postgres: Query TeamMember & Role (Prisma RLS)
API->>Redis: Cache Session (TTL: 300s)
end
API->>Postgres: Verify Resource Ownership (teamId match)
API->>Worker: Enqueue Job into 'extract.queue'
API->>Postgres: Update MeetingStatus to 'PROCESSING'
API-->>User: HTTP 202 Accepted { jobId: "job_9841fbc", status: "PROCESSING" }Service-to-Service Communication & HMAC Security
The Node.js API and Python AI Pipeline communicate over private internal Docker networks (rapto-app-network). To prevent unauthorized execution or internal request spoofing, all inter-service endpoints enforce strict cryptographic authentication:
sequenceDiagram
autonumber
participant Worker as Node.js BullMQ Worker
participant AIPipeline as Python FastAPI (:8001)
participant OpenAI as OpenAI API
Worker->>Worker: Prepare ExtractRequest Payload
Worker->>Worker: Attach 'X-Service-Key' Header (API_SHARED_SECRET)
Worker->>AIPipeline: POST /api/v1/extract (JSON Payload)
AIPipeline->>AIPipeline: verify_internal_service_key() (Constant-Time Compare)
alt Key Invalid / Missing
AIPipeline-->>Worker: HTTP 401 Unauthorized
end
AIPipeline->>AIPipeline: Chunk Transcript & Tokenize (tiktoken)
AIPipeline->>OpenAI: Concurrent Structured Extraction (GPT-4o)
OpenAI-->>AIPipeline: Structured JSON Entities
AIPipeline->>AIPipeline: Cross-Chunk Deduplication (threshold: 0.70)
AIPipeline-->>Worker: HTTP 200 OK (ExtractResponse Model)# Python FastAPI Inter-Service Authentication Dependency
async def verify_internal_service_key(
x_service_key: Optional[str] = Header(None, alias="X-Service-Key")
) -> None:
if not x_service_key:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing X-Service-Key authentication header"
)
if not secrets.compare_digest(x_service_key, settings.API_SHARED_SECRET):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid service key credentials"
)Database & Polyglot Persistence Architecture
Rapto AI rejects the anti-pattern of forcing all data structures into a single database. Instead, it leverages a Polyglot Persistence model optimized for access patterns, scale, and compliance:
PostgreSQL: Relational Source of Truth & Tenant Isolation
team_id foreign key. PostgreSQL Row-Level Security (RLS) policies enforce hardware-level database isolation.MongoDB: Large Semi-Structured Transcript Document Store
Redis: BullMQ Queues, Distributed Locks & Session Caching
oauth:state:{token}, TTL 600s).redlock) to prevent race conditions during calendar bot dispatches and Paddle webhook processing.Async Processing & Queue Topology
The platform relies on a distributed cluster of 9 specialized BullMQ queues to handle all asynchronous computation:
| Queue Name | Worker File | Concurrency | Job Responsibility |
|---|---|---|---|
| **`transcribe.queue`** | `transcribe.worker.ts` | 5 | Ingests Recall.ai webhooks, stores diarized transcripts in MongoDB, triggers extraction. |
| **`extract.queue`** | `extract.worker.ts` | 10 | Chunks transcript, calls Python AI microservice, persists extracted commitments to PostgreSQL. |
| **`resolve.queue`** | `resolve.worker.ts` | 5 | Executes cross-meeting resolution against past open promises, updates fulfillment states. |
| **`integrate.queue`** | `integrate.worker.ts` | 15 | Dispatches bi-directional issues into Linear, Jira, Notion, and Slack. |
| **`notify.queue`** | `notify.worker.ts` | 20 | Sends Brevo transactional emails, Slack Block Kit messages, and Socket.io events. |
| **`calendar-sync.queue`** | `calendar-sync.worker.ts` | 5 | Syncs Google Calendar and Microsoft Outlook Graph deltas every 15 minutes. |
| **`calendar.queue`** | `calendar.worker.ts` | 10 | Schedules and dispatches Recall.ai bot instances for upcoming calendar events. |
| **`deadline.queue`** | `deadline.worker.ts` | 2 | Cron-like worker evaluating upcoming and overdue commitment deadlines. |
| **`token-refresh.queue`** | `token-refresh.worker.ts` | 3 | Proactively refreshes expiring OAuth access tokens before integration API calls fail. |
Important End-to-End User Workflow
Here is how a real-world commitment transitions from spoken audio to a closed Linear issue and an updated Team Health Score:
sequenceDiagram
autonumber
actor Engineer as Engineer (Dave)
participant Meet as Video Conference (Google Meet / Zoom)
participant Recall as Recall.ai Recording Bot
participant API as Node.js API Gateway
participant Worker as BullMQ Worker Cluster
participant PythonAI as Python AI Pipeline
participant DB as PostgreSQL & MongoDB
participant Linear as Linear / Jira Backlog
participant Slack as Slack Channel
Engineer->>Meet: "I'll benchmark the connection pool before Thursday's release"
Meet-->>Recall: Audio Stream Captured
Meet->>Meet: Meeting Concludes
Recall->>API: Webhook: bot.status_change (DONE + Transcript URL)
API->>Worker: Enqueue to 'transcribe.queue'
Worker->>DB: Store Diarized Transcript into MongoDB
Worker->>Worker: Enqueue to 'extract.queue'
Worker->>PythonAI: POST /extract (Cleaned Transcript)
PythonAI->>PythonAI: Chunking + Structured Entity Extraction
PythonAI-->>Worker: Extracted Commitment: { Actor: "Dave", Action: "Benchmark connection pool", Deadline: "2026-09-04" }
Worker->>DB: Resolve Speaker Dave -> TeamMember usr_982 & Insert Commitment (PENDING)
Worker->>Worker: Enqueue 'integrate.queue' & 'resolve.queue'
Worker->>Linear: POST /issues (Create Issue: "Benchmark connection pool")
Linear-->>Worker: Issue Created (ENG-842)
Worker->>DB: Save ExternalCommitmentMapping (Jira/Linear ID: ENG-842)
Worker->>Slack: Send Interactive Block Kit Card to #eng-backend
Note over Engineer,Meet: Next Standup (3 Days Later)
Engineer->>Meet: "That connection pool benchmark is done, latency dropped 40%"
Meet-->>Recall: Audio Stream Captured
Recall->>API: Webhook: Transcript Ready
Worker->>PythonAI: POST /resolve (Unresolved Commitments + Current Transcript)
PythonAI->>PythonAI: Vector Similarity Match (0.89 Confidence)
PythonAI-->>Worker: Resolution: { commitmentId: "cmt_104", status: "FULFILLED" }
Worker->>DB: Update Commitment status = 'FULFILLED'
Worker->>DB: Recalculate TeamHealthScore (Recency-Weighted Velocity)
Worker->>Slack: Update Slack Card: FULFILLED ✓External Integrations & Bot Orchestration
Rapto AI connects directly with enterprise identity, meeting, productivity, and billing platforms:
Paddle-Signature SHA-256 HMAC) managing multi-tier team subscriptions (STARTER, GROWTH, BUSINESS, ENTERPRISE).Security, Authentication & Multi-Tenancy
Rapto AI is engineered to satisfy enterprise security audits and SOC-2 Type II controls:
graph TD
subgraph Security_Architecture [Defense-in-Depth Security Layers]
L1[Layer 1: Edge TLS 1.3 & NGINX Rate Limiting]
L2[Layer 2: JWT Access Token 15m + Rotating HttpOnly Refresh Token 30d]
L3[Layer 3: Prisma ORM Tenant Middleware auto-injecting teamId]
L4[Layer 4: PostgreSQL Row-Level Security RLS Policies]
L5[Layer 5: AES-256-GCM Envelope Encryption for OAuth Tokens]
L6[Layer 6: Zero-Data-Retention Agreements on LLM Pipelines]
end
L1 --> L2
L2 --> L3
L3 --> L4
L4 --> L5
L5 --> L6Scalability, Resilience & Fault Tolerance
To guarantee 99.9% uptime during traffic surges (e.g. all client standups concluding simultaneously at 9:30 AM EST), the architecture implements several defensive patterns:
services/ai-pipeline/src/services/extraction/extractor.py, multi-chunk processing uses asyncio.gather(return_exceptions=True). If chunk 3 fails due to a transient API blip, chunks 1, 2, and 4 still succeed, returning a Partial Extraction (HTTP 206) rather than crashing the entire meeting job.TRANSCRIPT_CLEANUP_DEGRADED and immediately routes the raw transcript to the extractor.token-refresh.worker.ts runs every 10 minutes to refresh tokens expiring within the next 30 minutes, eliminating 401 Unauthorized errors during webhook dispatches.Key Architectural Trade-offs
Every production system design represents deliberate trade-offs:
| Decision | Chosen Architecture | Rejected Alternative | Engineering Rationale |
|---|---|---|---|
| **Backend Language Split** | Node.js (API) + Python (AI) | Single Node.js or Python Monolith | Node.js excels at high-concurrency webhook I/O and OAuth integrations. Python is non-negotiable for robust AI chunking, tokenization, and vector similarity mathematics. |
| **Data Storage Model** | Dual (PostgreSQL + MongoDB) | Pure PostgreSQL (JSONB) or Pure MongoDB | JSONB in PostgreSQL causes table bloat on multi-megabyte transcripts. Pure MongoDB lacks strict relational foreign keys and ACID billing transactions. |
| **Task Synchronization** | Asynchronous BullMQ Queue | Synchronous HTTP Chaining | Processing audio, LLM extraction, and syncing 4 external issue trackers takes 15–45 seconds. Synchronous calls would cause client HTTP timeouts. |
| **Multi-Tenancy** | Shared Schema + Tenant Column | Database-per-tenant | Database-per-tenant creates massive operational overhead (running 1,000+ Postgres instances) and slow schema migrations for B2B SaaS. |
Lessons for Building a Production SaaS Platform
EXTRACTED_PARTIAL, TRANSCRIPT_CLEANUP_DEGRADED) so users still get value.Frequently Asked Questions
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Why did Rapto AI split the backend between Node.js and Python?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Node.js with TypeScript is uniquely suited for high-throughput, I/O-intensive operations like handling hundreds of concurrent OAuth webhook integrations, managing real-time Socket.io connections, and serving low-latency REST APIs. Python with FastAPI provides first-class native tooling for tokenization, chunking, and AI model orchestration."
}
},
{
"@type": "Question",
"name": "How does Rapto AI ensure multi-tenant data isolation?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Rapto AI implements defense-in-depth tenant isolation: application middleware, Prisma ORM query extensions auto-injecting teamId filters, PostgreSQL Row-Level Security (RLS), and namespace-scoped Redis keys."
}
}
]
}