1 System Overview
60+
API Endpoints
30+
DB Models
7
Celery Tasks
5
Docker Services
21
Migrations
3
CRM Providers

Smart Advisor OS sits across existing RIA tools — CRM, portfolio management, compliance, and custodian portals — and provides cross-system intelligence that no individual tool can offer alone. The core value proposition is converting raw meeting audio into structured notes, action items, client facts, compliance flags, follow-up emails, and CRM entries automatically.

🎙️
Phase 1 · AI Meeting Engine
Transcription, structured notes, action items, CRM sync, follow-up drafts, WORM archive
📊
Phase 2 · Practice Intel
Fee modeling, benchmarking, client segmentation, opportunity detection
👨‍👩‍👧
Phase 3 · Family Hub
Wealth transfer, heir engagement, values alignment, transfer milestones
⚖️
Phase 4 · Ops Layer
Compliance automation, marketing review, exam readiness, talent management
🤖
Phase 5 · Unified Assistant
Daily briefing, conversational AI, priority engine, firm health pulse
2 Tech Stack
LayerTechnology
API FrameworkFastAPI + uvicorn
LanguagePython 3.12
DatabasePostgreSQL 17 (alpine)
ORMSQLAlchemy 2.0 async (asyncpg)
MigrationsAlembic
Task QueueCelery + Redis broker
SchedulerCelery Beat (PersistentScheduler)
Cache / BrokerRedis 7 (alpine)
TranscriptionAssemblyAI (default) · Deepgram (optional)
AIAnthropic claude-sonnet-4-6
EncryptionFernet / MultiFernet (key rotation)
Passwordsbcrypt
StorageAWS S3 (uploads + WORM)
EmailSMTP (STARTTLS / SSL)
Reverse ProxyCaddy 2 (auto HTTPS via Let's Encrypt)

Source Layout

src/smartad/
├── api/
│   ├── main.py          # app setup, middleware
│   ├── ratelimit.py     # Redis rate limiter
│   └── routes/          # 25 route modules
├── auth/
│   ├── security.py      # JWT + bcrypt
│   └── dependencies.py  # bearer token guard
├── db/
│   ├── models.py        # 30+ ORM models
│   ├── repository.py    # data access layer
│   ├── session.py       # AsyncSession factory
│   └── crypto.py        # Fernet column types
├── worker/
│   ├── app.py           # Celery config + Beat
│   └── tasks.py         # 7 async tasks
├── intelligence/        # 16 Claude-powered modules
├── crm/                 # Wealthbox / Redtail / SF
├── transcription/       # AssemblyAI / Deepgram
├── archive/             # S3 WORM archival
├── storage/             # S3 upload presigning
├── notifications/       # SMTP briefing emails
├── documents/           # PDF/DOCX/CSV parsing
├── integrations/        # Teams + Google Meet
├── ssrf.py              # SSRF protection
└── config.py            # Pydantic settings
3 Docker Services
ServiceImagePortCommandRole
api smartad-api (Dockerfile) 172.18.0.1:8000 uvicorn … --reload FastAPI HTTP server; only service reachable from Caddy proxy
worker smartad-api celery worker --concurrency=4 Processes meeting pipeline, CRM sync, archive, document extraction
beat smartad-api celery beat Cron scheduler — fires daily morning briefing task
postgres postgres:17-alpine 5432 (internal) entrypoint Primary datastore; health-checked before dependents start
redis redis:7-alpine 6379 (internal) entrypoint Celery broker, result backend, rate-limit counters

Network Topology

Internet
HTTPS :443
Caddy
workspace_edge net
API
172.18.0.1:8000
PostgreSQL
postgres:5432
API
Redis
redis:6379
←→
Worker · Beat
same compose network

The API port is bound exclusively to 172.18.0.1 (the Docker bridge gateway used by Caddy). It is not exposed on any public interface.

4 Core Meeting Pipeline

Every meeting follows the same async pipeline. The HTTP request returns immediately after saving the record; all heavy work happens in the Celery worker.

POST /meetings
validate URL + consent
DB insert
status = pending
Celery dispatch
process_meeting.delay()
↓ inside the worker
Check consent
skip if false
Transcription
AssemblyAI / Deepgram
Claude AI
tool-use extraction
DB update
status = completed
archive_meeting
S3 WORM
+
sync_meeting_to_crm
Wealthbox · Redtail · SF

Claude Extraction Schema

Claude is called with structured tool use, guaranteeing typed output:

notes (5 categories)
action_items[]
client_facts{}
follow_up_email
compliance_flags[]
5 API Endpoints

All endpoints except webhooks are versioned under /api/v1/. Webhooks are at the root path.

Authentication · /api/v1/auth
POST/registerCreate account + token pair
POST/loginLogin + token pair
POST/refreshRotate refresh token
POST/logoutRevoke refresh token
GET/meCurrent user info
Meetings · /api/v1/meetings
POST/uploadS3 presigned upload (audio/video)
POST/Create meeting from recording URL
GET/List meetings (paginated)
GET/{meeting_id}Get meeting with notes + flags
GET/{meeting_id}/follow-up-emailGet generated follow-up
POST/{meeting_id}/follow-up-email/regenerateRegenerate via Claude
POST/{meeting_id}/crm-syncManual CRM sync trigger
Clients · /api/v1/clients
GET/engagementEngagement tiers + scores
GET/{client_name}/prep-briefPre-meeting intelligence brief
GET/{client_name}/goalsClient goals + progress
PUT/{client_name}/profileUpsert client profile (AUM, fee)
GET/{client_name}/profileGet client profile
Briefing · Opportunities · Practice · CRM · Documents · Compliance · Families · Talent · Firm Health · Assistant
GET/briefing/dailyGenerate morning briefing (real-time)
POST/opportunities/scanScan for financial opportunities
POST/practice/fee-compressionFee scenario modeling
GET/practice/kpisPractice KPI dashboard
POST/crm/connections/{provider}Connect CRM (wealthbox|redtail|salesforce)
POST/documentsUpload + extract client document
GET/documents/searchSearch documents by client name
POST/compliance/documentsGenerate compliance document (ADV, policy…)
POST/compliance/marketing-reviewsReview marketing content for compliance
POST/familyCreate family + wealth transfer tracking
POST/talent/career-laddersGenerate AI career ladder
POST/talent/comp-plansGenerate compensation plan
POST/firm-health/pulsesGenerate firm health pulse report
POST/assistant/chatUnified conversational AI assistant
POST/priorities/generateGenerate daily priority snapshot
Webhooks (no version prefix)
POST/webhooks/zoomZoom recording.completed (HMAC-SHA256 verified)
POST/webhooks/teamsTeams call_record.completed (Graph API)
POST/webhooks/meetGoogle Meet recording.completed (Pub/Sub)
6 Authentication

JWT + Stateful Refresh Tokens

TokenDetail
Access tokenHS256 JWT · 15 min TTL · claims: sub, exp, iss, aud
Refresh token32-byte random hex · 30-day TTL · SHA-256 hashed in DB
Issuer / Audiencesmartbuzzai
Password hashbcrypt (automatic cost factor)

Security Properties

Constant-time password verify
Constant-time refresh compare (hmac.compare_digest)
Dummy hash prevents email enumeration
Refresh tokens hashed — not reversible from DB
Explicit revocation on logout
Row-locked refresh lookup prevents replay

Rate Limits (Redis)

EndpointLimitWindowKey
POST /login560 sclient IP
POST /register560 sclient IP
AI routes2060 suser_id
7 Celery Worker Tasks
TaskRetriesDelayTriggerDescription
process_meeting 3 30 s API / webhooks Full pipeline: transcribe → Claude extraction → archive + CRM sync
archive_meeting 3 60 s process_meeting Serialise meeting JSON → S3 Object Lock COMPLIANCE (6-yr retention)
sync_meeting_to_crm 3 60 s process_meeting Build CRMNote + CRMTask list → push to all active providers in parallel
fetch_teams_recording 5 120 s /webhooks/teams Poll Microsoft Graph for recording URL (up to ~2 hrs), create Meeting
fetch_meet_recording 5 120 s /webhooks/meet Fetch Google Drive download URL via Meet API + service account impersonation
send_morning_briefings Beat cron Daily (configurable time) Build DailyBriefing per active user (semaphore=10), batch-send SMTP
process_document 3 30 s POST /documents Extract facts + summary from PDF/DOCX/CSV/TXT via Claude tool use
8 Database Models

All models carry a user_id foreign key enforcing row-level multi-tenancy. Sensitive columns use custom SQLAlchemy types that transparently encrypt/decrypt via Fernet.

ModelPhaseKey FieldsNotes
UserCoreid, email, hashed_password, is_activeRoot tenant anchor
RefreshTokenCoretoken_hash (SHA-256), expires_at, revokedStateful revocation
Meeting1status, recording_url, consent_given, raw_transcript (enc), notes, action_items, client_facts, follow_up_email, compliance_flags, archive_keyAppend-only by convention
ClientProfile1client_name, aum (Numeric 15,2), annual_fee_rateUnique per user + name
Referral2referrer_client, prospect_name, status, expected_aum, actual_aumGrowth pipeline tracking
Family3name, primary_client_name, estimated_total_aumWealth transfer root
FamilyMember3generation (0=primary, 1=children…), birth_year, has_independent_advisor, estimated_aumCASCADE from Family
ValuesProfile3growth_orientation, risk_appetite, social_impact, family_legacy, philanthropy, liquidity_preference (all 1–10)CASCADE from FamilyMember
TransferMilestone3category, due_date, completed_at, statusCASCADE from Family
HeirEngagementEvent3event_type, event_dateCASCADE from FamilyMember
ComplianceDocument4document_type, generated_content, status, regulatory_citationspolicy · ADV · annual_review
MarketingReview4content_type, is_compliant, issues, suggestionsAI-reviewed marketing copy
ExamReadinessAssessment4overall_score, domain_scores, deficienciesSeries 65/66 readiness
CareerLadder4stages (JSON), benchmarks (JSON)AI-generated advisor ladder
CompPlan4base_range, bonus_structure, equity_structure, kpis, market_comparisonAI-generated comp plan
NextGenAssessment4overall_score, dimension_scores, development_plan, transition_timeline_monthsSuccessor readiness
ChatSession / ChatMessage5role (user|assistant), contentUnified AI assistant history
PrioritySnapshot5priorities (JSON), focus_recommendationDaily AI priority list
DetectedOpportunity5opportunity_type, priority_score (0–100), is_dismissed529, drift, RMD, fee anomaly…
FirmHealthPulse5client_count, meeting_count, action_completion_rate, metrics, recommendations, ai_narrativePeriodic practice health report
CrmConnectionCRMprovider, credentials (EncryptedJSON), is_activeUnique per user + provider
ClientDocumentDocsfilename, raw_text (EncryptedText), extracted_facts, summary, statusPDF/DOCX/CSV/TXT intel
9 Alembic Migrations
VersionDescription
4ee95c27aba9Initial schema — users, meetings, clients
95486cc61302Add referrals table
h8i9j0k1l2m3Add refresh_tokens table
c4d5e6f7a8b9Add client_profiles
c335ca5ff915Phase 3 — Family, FamilyMember, ValuesProfile, TransferMilestone, HeirEngagementEvent
d4e5f6a7b8c9Phase 4 — ComplianceDocument, MarketingReview, ExamReadinessAssessment
e5f6a7b8c9d0Phase 4 — CareerLadder, CompPlan, NextGenAssessment
f6a7b8c9d0e1Phase 5 — ChatSession, ChatMessage, PrioritySnapshot, DetectedOpportunity, FirmHealthPulse
a1b2c3d4e5f6CRM connections table
b2c3d4e5f6a7client_documents table
b3c9e1f2a4d8WORM archive fields (archived_at, archive_key) on Meeting
g7h8i9j0k1l2Encrypt CRM credentials at rest (EncryptedJSON)
i9j0k1l2m3n4Encrypt raw_transcript in meetings (EncryptedText)
31c2dc772247Encrypt client_document raw_text and chat_messages
5af6d4bfc26cStatus indexes on meetings + related tables
89472f1a45ceStatus indexes on referrals + related tables
j0k1l2m3n4o5Check constraints — scores (0–100), birth_year (1900–2100)
f23ff3b0714fMerge / fix priority_score constraint
k1l2m3n4o5p6Check constraint on opportunity priority_score
11f896f1bda0Make meeting.user_id NOT NULL with CASCADE delete
3bbdfbbcfc6dChange client_document status column to enum
10 Encryption at Rest

Encrypted Columns

ModelColumnType
Meetingraw_transcriptEncryptedText
ClientDocumentraw_textEncryptedText
CrmConnectioncredentialsEncryptedJSON
ChatMessagecontentEncryptedText

Key Rotation

CREDENTIALS_ENCRYPTION_KEY accepts comma-separated Fernet keys. The first key encrypts new data; all keys are tried for decryption — enabling zero-downtime rotation.

# Rotate: prepend new key, restart, re-encrypt rows, remove old key
CREDENTIALS_ENCRYPTION_KEY=new_key,old_key

Decryption failures are surfaced as exceptions rather than silently returning None — data corruption or key mismatch is never hidden.

11 AI Integration (Anthropic Claude)

All AI features use claude-sonnet-4-6 via structured tool use for guaranteed typed output. Prompt caching is applied to static system prompts to reduce latency and cost.

Meeting Processing
Tool-use schema extracts notes (5 categories), action items, client facts, follow-up email, compliance flags from transcript
Document Extraction
Parses PDF/DOCX/CSV/TXT into structured client facts + summary
Daily Briefing
Synthesises meetings + profiles into morning advisory brief
Opportunity Detection
Scans portfolio data for 529 gaps, drift, RMD deadlines, fee anomalies, estate planning needs
Compliance Generation
Drafts Form ADV, policy docs, marketing compliance reviews, exam readiness assessments
Talent + Firm
Career ladder design, comp plan generation, next-gen readiness scoring, firm health narrative
Unified Assistant
Conversational interface across all practice data — meeting history, clients, compliance, pipeline
12 Transcription

Transcription is abstracted behind a TranscriptionProvider protocol in src/smartad/transcription/base.py. New providers slot in without touching the pipeline.

AssemblyAI (default)
Speaker labels enabled · Entity detection for financial terms · Blocks synchronously in the Celery task
Deepgram (optional)
Set TRANSCRIPTION_PROVIDER=deepgram + DEEPGRAM_API_KEY to activate
13 Meeting Platform Webhooks
PlatformEndpointValidationRecording FetchConsent
Zoom /webhooks/zoom HMAC-SHA256 signature + 300 s timestamp drift check Direct URL from payload (SSRF-checked) consent_given=False until advisor approves
Microsoft Teams /webhooks/teams clientState secret header Graph API → organizer's OneDrive (async retry up to ~2 hrs) consent_given=False
Google Meet /webhooks/meet Token in URL path (Pub/Sub push secret) Drive API with service account impersonation consent_given=False

All platforms set consent_given=False on creation. The advisor must explicitly grant consent before the meeting enters processing. This prevents cross-account injection even if webhook HMAC validation is bypassed.

14 CRM Integrations
Wealthbox
API token auth · Contacts, notes, tasks · Credentials stored as EncryptedJSON
Redtail
API key + username/password · Same interface · All creds encrypted
Salesforce
OAuth2 client credentials · Token refresh handled internally · Instance URL + creds encrypted

Sync creates a CRMNote (subject + body from meeting notes) and a list of CRMTask objects (title, due date, assignee) per action item. Contact lookup is by client name. All active connections sync in parallel; failures per provider are logged but don't fail the others.

15 Storage & Compliance Archive

Upload Bucket

SettingValue
Env varUPLOAD_BUCKET_NAME
Path schemeuploads/{year}/{month}/{uuid}.{ext}
Presign TTL7 days (configurable)
Max file size500 MB (configurable)
FormatsMP3 M4A MP4 WAV OGG FLAC AAC WEBM

WORM Compliance Archive

SettingValue
Env varWORM_BUCKET_NAME
Path schememeetings/{year}/{month}/{meeting_id}.json
Retention2190 days (6 years) · SEC Rule 17a-4
Lock modeS3 Object Lock COMPLIANCE (irrevocable)
ContentFull meeting JSON excluding encrypted transcript

If archival permanently fails after all retries, a COMPLIANCE ACTION REQUIRED error is logged — the failure is surfaced rather than swallowed.

16 Security Layers
LayerImplementationDetail
CORS FastAPI CORSMiddleware Origins from CORS_ALLOWED_ORIGINS; warns if empty (blocks all cross-origin)
Rate Limiting Redis Lua atomic counters 5/min on auth; 20/min on AI routes; HTTP 429 + Retry-After
SSRF Protection ssrf.py · is_safe_url() Blocks non-HTTPS + private/loopback IPs; resolves hostnames via DNS — all results checked
Webhook Validation HMAC-SHA256 (Zoom); clientState (Teams); URL token (Meet) 300 s timestamp drift window; consent gate as secondary defence
Data Encryption Fernet / MultiFernet Transcripts, CRM credentials, documents, chat messages — never plaintext in DB
Port Isolation docker-compose port binding API bound to 172.18.0.1:8000 only — not reachable from public interfaces
Global Error Handler FastAPI exception handler Logs full traceback; returns generic HTTP 500 — no internal detail leaked
Audit Trail Meeting rows append-only Convention: never DELETE meetings — use status fields + WORM archive
17 Configuration Reference
VariableRequiredDefaultDescription
DATABASE_URLyesPostgreSQL asyncpg connection string
REDIS_URLyesRedis broker + result backend
ANTHROPIC_API_KEYyesClaude API key
JWT_SECRETyes≥32 chars; HS256 signing key
CREDENTIALS_ENCRYPTION_KEYyesFernet key(s), comma-separated for rotation
CORS_ALLOWED_ORIGINSrec.""Comma-separated allowed origins
ASSEMBLYAI_API_KEYrec.Required if TRANSCRIPTION_PROVIDER=assemblyai
TRANSCRIPTION_PROVIDERnoassemblyaiassemblyai or deepgram
WORM_BUCKET_NAMEnoS3 bucket for compliance archive
WORM_OBJECT_LOCKnofalseSet true in production for immutable retention
WORM_RETENTION_DAYSno21906 years default (SEC Rule 17a-4)
UPLOAD_BUCKET_NAMEnoS3 bucket for user-uploaded audio/video
SMTP_HOST / PORT / USERNAME / PASSWORD / FROMnoSMTP config for daily briefing emails
BRIEFING_SEND_HOUR / MINUTE / TIMEZONEno6:30 ETDaily briefing send schedule
ZOOM_WEBHOOK_SECRET_TOKENnoZoom HMAC validation secret
TEAMS_TENANT_ID / CLIENT_ID / CLIENT_SECRETnoAzure AD app for Teams Graph API
GOOGLE_SERVICE_ACCOUNT_JSONnoService account for Google Meet + Drive
TRUSTED_PROXY_COUNTno0Reverse proxy hops for real IP extraction