---
title: "Midday Workflows and Data Flows"
description: "Key user journeys in Midday — bank sync, invoicing, receipt matching, AI assistant, and time tracking"
image: "https://tractiongrowth.com/og/open-source/midday-ai-finance/workflows/image.png"
---

# Midday Workflows and Data Flows

# Midday Workflows and Data Flows

Midday works by moving user actions from the dashboard into tRPC procedures, Hono API handlers, PostgreSQL records, and background workers, while external systems such as Plaid, Stripe, and OpenAI fill in the finance and AI layers. This page traces the most important workflows so you can see how Midday turns those integrations into a coherent product.

<Callout title="Reading Guide">
  Each workflow shows the complete data path from user action to final result. Pay attention to where **external services** (Plaid, Stripe, OpenAI) enter the picture — these integration points are where fintech apps get complex.
</Callout>

## How a Request Flows Through Midday

```
Browser (User Action)
    │
    ▼
React Component              ← dashboard UI
    │ calls tRPC mutation/query (or server action)
    ▼
Next.js Server / Hono API    ← validates JWT, runs business logic
    │
    ├──► Drizzle ORM (PostgreSQL)  ← data operations
    ├──► External APIs             ← Plaid, Stripe, OpenAI
    └──► BullMQ / Trigger.dev     ← queue background jobs
    │
    ▼
Response → React Component   ← TanStack Query caches, UI re-renders
```

## Workflow 1: Connecting a Bank Account

The foundational flow — linking a bank so transactions auto-import.

<Steps>
  ### User clicks "Connect Bank"

  The dashboard opens Plaid Link (US/CA), GoCardless Connect (EU), or Teller Connect depending on the user's region.

  ### User authenticates with their bank

  The banking provider's widget handles the bank login (multi-factor auth, account selection). Midday never sees the user's bank credentials.

  ### Provider returns a connection token

  The banking widget returns a public token/requisition ID. The dashboard sends it to the API:

  ```
  POST /trpc/bankAccounts.create
  Body: { provider: "plaid", publicToken: "..." }
  ```

  ### API exchanges token and stores connection

  1. **Exchange token** — API calls the provider to convert public token → access token
  2. **Fetch accounts** — API calls provider to list available accounts (checking, savings, etc.)
  3. **Store in DB** — `bank_connections` record (provider, access token encrypted) + `bank_accounts` records (name, balance, currency, type)

  ### Initial transaction sync triggers

  A BullMQ job is queued to fetch historical transactions:

  ```
  Worker: TransactionSyncJob
    1. Call provider API: getTransactions(last 90 days)
    2. For each transaction:
       a. Store in `transactions` table
       b. Queue AI categorization (match against category embeddings)
       c. Queue enrichment (merchant name, logo via provider data)
    3. Update bank_account balance
  ```

  ### Transactions appear in the dashboard

  TanStack Query invalidates the transaction list cache. The user sees their bank transactions, auto-categorized and enriched.
</Steps>

<Callout type="info" title="For PMs: Why Is Bank Sync Complex?">
  Each banking provider has different: (1) auth flows (OAuth, mTLS certificates, JWT), (2) data formats (Plaid uses internal IDs, GoCardless uses ISO 20022), (3) rate limits, (4) sync patterns (webhook vs polling), and (5) error handling (expired tokens, revoked access). The `@midday/banking` abstraction normalizes all of this into a single interface, but the underlying complexity is substantial.
</Callout>

## Workflow 2: Creating and Sending an Invoice

<Steps>
  ### User creates an invoice

  The invoice editor lets users add line items (`invoice_products`), set payment terms, choose a template, and select a customer.

  ### Invoice is saved

  ```
  tRPC: invoices.create → Drizzle INSERT into `invoices` + `invoice_products`
  ```

  The invoice number is auto-generated (configurable format). The status is set to `draft`.

  ### User clicks "Send"

  The dashboard calls the API to send the invoice:

  1. **Generate PDF** — `@midday/invoice` uses `@react-pdf/renderer` to produce a PDF
  2. **Send email** — `@midday/notifications` sends via Resend with a React Email template (`invoice.tsx`)
  3. **Update status** — Invoice status changes to `sent`
  4. **Create Stripe payment link** — If Stripe Connect is configured, a payment intent is created

  ### Customer receives and pays

  The customer gets an email with a link to the invoice. The link opens a public page where they can:

  * View the invoice
  * Pay via Stripe (credit card, bank transfer)
  * Download the PDF

  ### Stripe webhook confirms payment

  ```
  Stripe sends POST /webhooks/stripe
    Event: payment_intent.succeeded
      │
      ▼
    Update invoice status → "paid"
    Update invoice.paid_at timestamp
    Record payment details
      │
      ▼
    Notify user via email (invoice-paid.tsx template)
  ```

  ### Recurring invoices (optional)

  If the invoice is set to recurring, Trigger.dev schedules the next send:

  ```
  Trigger.dev: InvoiceSchedulerTask
    Runs on schedule → creates new invoice from template → sends automatically
  ```
</Steps>

<Callout type="info" title="Why Stripe Connect?">
  Midday uses **Stripe Connect** (not regular Stripe) for invoice payments. This means the money flows directly to the freelancer's Stripe account — Midday acts as a platform facilitating the payment, not as a middleman holding funds. This is legally important for a tool used by independent businesses.
</Callout>

## Workflow 3: Magic Inbox (AI Receipt Matching)

The inbox automatically matches incoming receipts to bank transactions.

<Steps>
  ### Receipts arrive via email

  The `@midday/inbox` package syncs from connected Gmail or Outlook accounts:

  ```
  Worker: InboxSyncJob
    1. Fetch new emails from Gmail API / Microsoft Graph
    2. Filter for receipt-like attachments (PDF, images)
    3. Store in `inbox` table with file in Supabase Storage
  ```

  ### Document Intelligence extracts data

  ```
  Worker: DocumentProcessingJob
    1. Send PDF/image to Azure Document Intelligence (OCR)
    2. Extract: merchant name, amount, date, currency
    3. Store extracted data on inbox record
  ```

  ### AI matches receipt to transaction

  ```
  Worker: InboxMatchingJob
    1. Query `transactions` for similar amount + date range + merchant
    2. Use embedding similarity for fuzzy matching
    3. If confidence > threshold:
       → Auto-link receipt to transaction (transaction_attachments)
    4. If uncertain:
       → Store as suggestion (transaction_match_suggestions)
       → User reviews in dashboard
  ```

  ### User reviews matches

  Unmatched or low-confidence receipts appear in the Inbox view. The user can manually link them to transactions or dismiss them.
</Steps>

## Workflow 4: AI Financial Assistant

The chat assistant answers natural language questions about your finances.

<Steps>
  ### User types a question

  For example: "What's my burn rate this quarter?"

  The dashboard sends the message to the AI chat endpoint.

  ### AI SDK selects and executes tools

  ```
  AI SDK (OpenAI / Anthropic / Google):
    1. Parse user intent
    2. Select tool: "get-burn-rate"
    3. Tool executes: Drizzle query against transactions
       → SUM(amount) WHERE date IN quarter, GROUP BY month
    4. Return structured data to the model
  ```

  ### Model generates a response

  The AI model receives the financial data and generates a natural-language response with the burn rate breakdown. The response is **streamed** to the user via AI SDK React hooks for instant feedback.

  ### Suggested follow-ups

  The assistant suggests related questions: "Compare to last quarter?" or "What are my top 3 expenses?"
</Steps>

<Callout type="info" title="For PMs: AI Tools vs Free-Text">
  The assistant doesn't guess answers from training data. It uses **structured tools** that execute real database queries. When you ask about burn rate, the AI calls a `get-burn-rate` function that runs an actual SQL query against your transaction data. The AI's role is understanding the question and formatting the answer — the data is always real and current.
</Callout>

## Workflow 5: Time Tracking → Invoice

How tracked time becomes a billable invoice.

<Steps>
  ### User tracks time on a project

  The time tracker records entries against a `tracker_project`:

  * Start/stop timer (live tracking)
  * Manual entry (date, duration, description)
  * Entries stored in `tracker_entries`

  ### User generates an invoice from tracked time

  The dashboard aggregates billable entries for a project and pre-fills an invoice:

  1. Sum hours per entry
  2. Apply project hourly rate
  3. Create line items from entries
  4. Link invoice to customer and project

  ### Invoice follows the standard send/pay flow

  Same as Workflow 2 — generate PDF, send via email, collect payment via Stripe.
</Steps>

## Information Flow Summary

| From                     | To                 | Mechanism                                    | Example |
| ------------------------ | ------------------ | -------------------------------------------- | ------- |
| Browser → API            | tRPC over HTTPS    | Creating invoices, querying transactions     |         |
| Browser → API            | REST over HTTPS    | Desktop app, third-party integrations        |         |
| API → PostgreSQL         | Drizzle ORM        | All data operations                          |         |
| API → Redis              | Cache set/get      | API key lookup, user sessions, rate limiting |         |
| API → BullMQ (via Redis) | Job queue          | Bank sync, document processing               |         |
| Plaid/GoCardless → API   | REST API (OAuth)   | Fetching bank transactions                   |         |
| Stripe → API             | Webhook (POST)     | Payment confirmations                        |         |
| Gmail/Outlook → Worker   | REST API (OAuth)   | Email receipt sync                           |         |
| Worker → Azure           | REST API           | OCR / document intelligence                  |         |
| AI SDK → OpenAI          | REST API           | Financial assistant queries                  |         |
| API → Resend             | REST API           | Sending invoice and notification emails      |         |
| Trigger.dev → Worker     | Scheduled triggers | Recurring invoices, bank sync polling        |         |

## What's Next

<Cards>
  <Card title="What Is Midday?" description="Product overview, positioning, and the financial workflows the product brings together." href="/open-source/midday-ai-finance" />

  <Card title="How Midday Is Built" description="Repository structure, build system, dependencies, and deployment." href="/open-source/midday-ai-finance/how-it-is-built" />

  <Card title="Midday Architecture Overview" description="System design, database schema, banking integrations, and AI pipeline." href="/open-source/midday-ai-finance/architecture" />
</Cards>

