Architecture Overview
Midday's system design — Supabase backend, Hono API, tRPC, banking integrations, and AI pipeline
Architecture Overview
For Product Managers
This page explains how Midday is structured as a software system. Focus on the diagrams and "Why It Matters" callouts to understand how bank connections, AI features, and invoicing work under the hood.
System Architecture Diagram
Midday uses a distributed services architecture — a Next.js dashboard, a Hono API server, and a BullMQ worker, backed by Supabase (managed PostgreSQL).
┌──────────────────────────────────────────────────────────────────┐
│ USERS (Browser / Desktop) │
└───────────────────────────────┬──────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌──────────▼──────┐ ┌─────▼──────┐ ┌──────▼───────┐
│ Dashboard │ │ Website │ │ Desktop │
│ (Next.js 16) │ │ (Next.js) │ │ (Tauri 2) │
│ Railway :3001 │ │ Vercel │ │ macOS app │
└────────┬─────────┘ └────────────┘ └──────┬───────┘
│ │
│ Server Actions + tRPC │ REST API
│ │
┌────────▼────────────────────────────────────▼───────┐
│ Hono API (Bun) │
│ Railway :3003 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ tRPC │ │ REST │ │ OpenAPI (Scalar) │ │
│ │ Routers │ │ Routers │ │ Documentation │ │
│ └──────────┘ └──────────┘ └──────────────────┘ │
└──────┬────────────┬──────────────┬──────────────────┘
│ │ │
┌────────▼───┐ ┌─────▼─────┐ ┌────▼──────────────┐
│ Supabase │ │ Redis │ │ External Services │
│ (Postgres) │ │ (Cache) │ │ │
│ │ │ │ │ • Plaid (banking) │
│ • Auth │ │ • BullMQ │ │ • GoCardless │
│ • Storage │ │ • API cache│ │ • Teller │
│ • Realtime │ │ • Sessions│ │ • Stripe (payments)│
│ • 45+ tables│ └─────┬─────┘ │ • OpenAI (AI) │
└────────────┘ │ │ • Resend (email) │
┌─────▼─────┐ │ • Xero/QuickBooks │
│ Worker │ │ • Typesense │
│ (BullMQ) │ └────────────────────┘
│ Railway │
│ │
│ • Bank sync│
│ • Documents│
│ • Inbox │
│ • Invoices │
│ • Insights │
└───────────┘Why It Matters
The Dashboard handles the UI and server-side rendering. The Hono API is a separate service providing tRPC + REST endpoints for the dashboard, desktop app, and third-party integrations. The Worker processes background jobs (bank sync, document processing, invoice scheduling) via BullMQ. All three services connect to the same Supabase PostgreSQL instance via Drizzle ORM.
API Architecture: Hono + tRPC
The API server (apps/api/) uses Hono as the HTTP framework and tRPC for type-safe RPC:
Hono Server (:3003)
├── /trpc/* → tRPC routers (type-safe, dashboard uses these)
└── /rest/* → OpenAPI REST routers (desktop, third-party)
├── /transactions
├── /invoices
├── /customers
├── /bank-accounts
├── /tracker-projects
├── /tracker-entries
├── /inbox
├── /documents
├── /chat
├── /insights
├── /search
├── /reports
├── /tags
├── /users
├── /teams
├── /notifications
├── /files
├── /apps
├── /oauth
├── /webhooks
├── /mcp
└── /transcriptionAuth: JWT verification via JWKS (Supabase's public key endpoint) with HS256 fallback. Every request extracts { user: { id, email, full_name } } from the token.
Rate Limiting: hono-rate-limiter — 100 requests per 10 minutes per user.
Database Architecture
Supabase + Drizzle ORM
Midday uses Supabase (managed PostgreSQL) with Drizzle ORM for type-safe queries. The schema is defined in packages/db/src/schema.ts.
Core Entity Groups
Financial:
| Table | Purpose |
|---|---|
transactions | Bank transactions (amount, date, category, status) |
transaction_categories | Category assignments |
transaction_enrichments | AI-enriched metadata (merchant, logo) |
transaction_attachments | Linked receipt files |
transaction_tags | Custom tag assignments |
bank_accounts | Connected bank accounts |
bank_connections | Provider connections (Plaid/GoCardless/Teller) |
institutions | Bank/institution directory |
exchange_rates | Currency conversion rates |
Invoicing:
| Table | Purpose |
|---|---|
invoices | Invoice header (number, status, due date, amount) |
invoice_products | Line items |
invoice_recurring | Recurring invoice schedules |
invoice_templates | Custom invoice templates |
invoice_comments | Team collaboration on invoices |
Time Tracking:
| Table | Purpose |
|---|---|
tracker_projects | Projects being tracked |
tracker_entries | Time log entries |
tracker_reports | Time reports |
Documents & Inbox:
| Table | Purpose |
|---|---|
documents | Stored files (vault) |
document_tags | Document categorization |
inbox | Incoming receipts/invoices |
inbox_accounts | Connected email accounts (Gmail/Outlook) |
Users & Teams:
| Table | Purpose |
|---|---|
users | User profiles |
teams | Business entities (tenancy unit) |
users_on_team | Team membership |
customers | Client directory |
AI & Insights:
| Table | Purpose |
|---|---|
insights | AI-generated financial analysis |
transaction_category_embeddings | Vector embeddings for categorization |
document_tag_embeddings | Vector embeddings for document classification |
Banking Integration Architecture
The @midday/banking package abstracts four providers behind a unified interface:
@midday/banking
├── PlaidProvider → US, Canada (Plaid SDK)
├── GoCardlessProvider → EU, UK (REST via xior)
├── TellerProvider → US (mTLS with certificates)
└── EnableBankingProvider → EU, UK (JWT auth)Each provider implements a common interface:
getAccounts()→ list connected accountsgetTransactions(since)→ fetch new transactionsgetBalance()→ current balancedisconnect()→ revoke connection
Why Four Providers?
No single banking API covers the world. Plaid dominates the US/Canada market; GoCardless (via Nordigen) leads in Europe; Teller offers a US alternative with mTLS security; EnableBanking fills additional European coverage gaps. The abstraction layer means the rest of Midday's codebase doesn't care which provider supplies the data.
AI Architecture
Midday uses the Vercel AI SDK with multiple model providers:
AI Tools (Financial Assistant)
| Tool | Purpose |
|---|---|
get-expenses | Query expense data by category, date, merchant |
get-cash-flow | Cash in/out analysis over time |
get-spending | Spending breakdown by category |
get-burn-rate | Monthly burn rate calculation |
get-forecast | Revenue/expense forecasting |
get-metrics-breakdown | Key financial metrics |
get-invoice-payment-analysis | Invoice payment patterns |
web-search | External data lookup |
AI Pipeline
User asks "What's my burn rate this quarter?"
│
▼
AI SDK → selects tool: get-burn-rate
│
▼
Tool executes: queries transactions table via Drizzle
│
▼
Data returned to AI model
│
▼
Model generates natural language response with data
│
▼
Streamed to user via AI SDK React hooksDocument Intelligence
- Azure Document Intelligence — OCR for receipt/invoice PDF extraction
- Google AI embeddings — Vector embeddings for document classification
- LangChain — Document processing pipeline (mammoth, officeparser, unpdf)
Authentication
| Method | Details |
|---|---|
| Email/Password | Via Supabase Auth |
| OAuth | Google, GitHub (via Supabase) |
| MFA | Multi-factor authentication support |
| API Keys | Stored in api_keys table for programmatic access |
| OAuth Apps | Full OAuth 2.0 server (authorization codes, access tokens) |