Skip to content

System Architecture

System diagram

graph TB
subgraph "Client Browsers"
Admin["Agency Staff\n(Admin Portal /admin)"]
User["Clients\n(IPs, EDs, GCs — /user)"]
Provider["Medical Providers\n(/provider)"]
Guest["Guest / 3rd Party\n(/guest)"]
end
subgraph "AWS Elastic Beanstalk — one instance per agency"
subgraph "flaskapp.py — Main Web Application"
direction TB
BR["before_request hook\nResolves agency, user, admin, provider"]
subgraph "Views Layer — returns HTML"
AV["/admin/* — 12 admin blueprints"]
UV["/user/* — client portal"]
PV["/provider/* — provider portal"]
FV["/forms/* — form intake"]
GV["/guest/* — guest access"]
end
subgraph "API Layer — returns JSON"
AA["/api/admin/*"]
UA["/api/user/*"]
PA["/api/profile/*"]
MA["/api/matching/*"]
EA["/api/email/*"]
WA["/api/workflow/*"]
MoreAPIs["...18 more API blueprints"]
end
AR["after_request hook\nMetrics, X-Ray, PostHog"]
end
subgraph "cronapp.py — Scheduled Jobs"
CJ["Iterates ALL agency DBs\nvia SHOW DATABASES"]
end
subgraph "orchid_jobs — Huey Task Queue"
EMS["email_sync"]
EMM["email_matching"]
end
end
subgraph "Per-Agency MySQL Database"
DB[(Agency DB\ncase, user, profile,\nmatching, workflow...)]
end
subgraph "External Services"
SG["SendGrid — email"]
GM["Gmail API — sync"]
MS["Microsoft Graph — sync"]
HS["HelloSign — e-signatures"]
SF["Salesforce — CRM sync"]
TW["Twilio — SMS"]
GU["Gurgle — messaging"]
end
subgraph "AWS Infrastructure"
S3["S3 — files & images"]
CF["CloudFront — CDN"]
CW["CloudWatch — logs"]
XR["X-Ray — tracing"]
end
Admin --> AV & AA
User --> UV & UA & PA & MA
Provider --> PV
Guest --> GV
AV & UV & PV & FV & GV --> BR
AA & UA & PA & MA & EA & WA & MoreAPIs --> BR
BR --> DB
AR --> CW & XR
CJ --> DB
EMS & EMM --> DB
EMS --> GM & MS
EA --> SG & GM & MS
AA --> HS & SF
GU --> DB
DB --> S3 & CF

Entry points

There are three separate Python applications:

flaskapp.py — The main web app

This is the application that end users interact with. It registers 50+ Flask blueprints — some serve HTML pages (the “views”), some serve JSON (the “APIs”). They share the same process, the same database connection, and the same g (Flask globals) object.

See Key Concepts for how the g object works.

cronapp.py — Background scheduler

A separate Flask application that runs on a schedule. Unlike flaskapp.py, which connects to one agency’s database, cronapp.py iterates over all agency databases using a SHOW DATABASES query and creates a fresh SQLAlchemy engine for each one.

Jobs in cronapp.py include: sending scheduled notifications, running health checks, processing overnight reports.

orchid_jobs/ — Async task queue

A Huey-based job queue application that handles work that is too slow for a web request. Currently the two main job types are:

  • email_sync — Fetches new emails from Gmail/Outlook and stores them in the database
  • email_matching — Analyzes stored emails and links them to the right case or contact

Request lifecycle

Every request through flaskapp.py follows this sequence:

1. Request arrives
2. before_request() fires:
a. Generate g.request_id (UUID4)
b. Record g.request_start_time
c. Parse user agent → g.client
d. Load g.agency (always Agency.query.first())
e. Check session → load g.admin, g.user, or g.provider
f. Check session timeout (20 min for users, 4 hr for admins)
3. Route handler runs → returns HTML or JSON
4. after_request() fires:
a. Attach X-Request-ID header
b. Log request metrics (status, duration_ms, query_count)
c. Fire PostHog analytics event
5. Response sent

Blueprint organization

All blueprints follow the same naming convention:

PatternExamplePurpose
{feature}_viewsadmin_case_management_viewsReturns HTML pages
{feature}_apimatching_apiReturns JSON responses

Views and APIs for the same feature are always in separate files (orchid/views/matching.py + orchid/api/matching.py).

Frontend build

The frontend is not a single-page application. It is server-rendered HTML with JavaScript layered on top via jQuery and custom modules. Webpack bundles the JS and SASS into 6 feature-specific bundles:

BundleEntry pointWho loads it
adminpack_admin.mjsAll admin portal pages
userpack_user.mjsClient portal pages
gurgleclientpack_gurgleclient.mjsIn-app messaging
journey_pagepack_journey.mjsCase journey page
form_intakepack_intake.mjsForm submission pages
profilepack_profile.mjsProfile editing pages

Infrastructure

  • Hosting: AWS Elastic Beanstalk, one EB environment per agency
  • Database: MySQL (one database per agency, isolated)
  • Files: S3 for documents, images, videos; CloudFront as CDN
  • Logging: Structured JSON to CloudWatch via watchtower
  • Tracing: AWS X-Ray
  • Analytics: PostHog (product events)
  • Email delivery: SendGrid
  • SMS: Twilio
  • Secrets: Environment variables via EB environment config (never committed)