# Meetily SQLite Database Schema for Meetings, Transcripts, and Summaries

> Discover the Meetily SQLite database schema used to store meetings transcripts and AI summaries. Explore the SQL migration files for seamless data management. Learn more.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: api-reference
- Published: 2026-07-19

---

**Meetily stores all persistent meeting data, transcripts, and AI summaries in a single SQLite database that is created and migrated using SQL files under `frontend/src-tauri/migrations`.**

Meetily is an open-source meeting assistant that relies on a SQLite database schema for meetings, transcripts, and summaries to persist every recording and AI-generated result locally. Defined by SQL migrations in the Tauri backend, this schema maintains strict referential integrity between meetings, their transcripts, and asynchronous summarisation jobs.

## Database Overview and Migration Files

The database is initialized and versioned through numbered migration scripts in the `frontend/src-tauri/migrations` directory. The core schema is established in [`frontend/src-tauri/migrations/20250916100000_initial_schema.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/migrations/20250916100000_initial_schema.sql), which creates six primary tables. Subsequent migrations such as [`frontend/src-tauri/migrations/20250920155811_add_openrouter_api_key.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/migrations/20250920155811_add_openrouter_api_key.sql) and [`frontend/src-tauri/migrations/20251223000000_add_meeting_notes.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/migrations/20251223000000_add_meeting_notes.sql) incrementally extend the schema without breaking existing data.

All core tables use **TEXT** as the primary key type and store timestamps as **TEXT** for SQLite portability. Foreign-key constraints with `ON DELETE CASCADE` ensure that removing a meeting automatically cleans up its related transcripts, summary processes, and transcript chunks. The Rust database wrapper in [`frontend/src-tauri/src/database/mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/mod.rs) opens the connection and runs migrations on startup, while query helpers are provided in [`frontend/src-tauri/src/database/queries.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/queries.rs).

## Core Tables in the Meetily SQLite Schema

### meetings

The `meetings` table is the root entity for every session created in the app. It is defined in [`frontend/src-tauri/migrations/20250916100000_initial_schema.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/migrations/20250916100000_initial_schema.sql) as follows:

```sql
CREATE TABLE IF NOT EXISTS meetings (
    id TEXT PRIMARY KEY,
    title TEXT NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

```

Each row represents a single meeting with a unique text identifier, a mandatory title, and timestamp tracking for creation and last update.

### transcripts

The `transcripts` table stores the raw output from Whisper and any post-processing results. It references `meetings(id)` through a foreign key that cascades on deletion:

```sql
CREATE TABLE IF NOT EXISTS transcripts (
    id TEXT PRIMARY KEY,
    meeting_id TEXT NOT NULL,
    transcript TEXT NOT NULL,
    timestamp TEXT NOT NULL,
    summary TEXT,
    action_items TEXT,
    key_points TEXT,
    FOREIGN KEY (meeting_id) REFERENCES meetings(id) ON DELETE CASCADE
);

```

In addition to the full `transcript` text, this table reserves nullable columns for `summary`, `action_items`, and `key_points` that may be populated after LLM processing.

### summary_processes

Async summarisation state is tracked in `summary_processes`, keyed directly by `meeting_id`. This design guarantees a one-to-one relationship between a meeting and its current summarisation job:

```sql
CREATE TABLE IF NOT EXISTS summary_processes (
    meeting_id TEXT PRIMARY KEY,
    status TEXT NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    error TEXT,
    result TEXT,
    start_time TEXT,
    end_time TEXT,
    chunk_count INTEGER DEFAULT 0,
    processing_time REAL DEFAULT 0.0,
    metadata TEXT,
    FOREIGN KEY (meeting_id) REFERENCES meetings(id) ON DELETE CASCADE
);

```

Columns such as `status`, `chunk_count`, and `processing_time` allow the UI to report progress and diagnose failures for long-running LLM operations.

### transcript_chunks

Before sending text to the LLM summariser, Meetily splits transcripts into manageable pieces. The `transcript_chunks` table persists this chunked representation:

```sql
CREATE TABLE IF NOT EXISTS transcript_chunks (
    meeting_id TEXT PRIMARY KEY,
    meeting_name TEXT,
    transcript_text TEXT NOT NULL,
    model TEXT NOT NULL,
    model_name TEXT NOT NULL,
    chunk_size INTEGER,
    overlap INTEGER,
    created_at TEXT NOT NULL,
    FOREIGN KEY (meeting_id) REFERENCES meetings(id) ON DELETE CASCADE
);

```

Fields like `chunk_size`, `overlap`, and `model_name` provide reproducibility by recording exactly how the text was segmented and which model configuration was active at the time.

### settings

Global application configuration lives in the `settings` table. The initial migration defines provider credentials and model selections:

```sql
CREATE TABLE IF NOT EXISTS settings (
    id TEXT PRIMARY KEY,
    provider TEXT NOT NULL,
    model TEXT NOT NULL,
    whisperModel TEXT NOT NULL,
    groqApiKey TEXT,
    openaiApiKey TEXT,
    anthropicApiKey TEXT,
    ollamaApiKey TEXT
);

```

This row typically uses a singleton `id` value such as `'default'` and stores API keys for Groq, OpenAI, Anthropic, and Ollama.

### transcript_settings

For per-meeting overrides, Meetily provides the `transcript_settings` table. This allows individual transcriptions to use different providers or API keys without changing the global configuration:

```sql
CREATE TABLE IF NOT EXISTS transcript_settings (
    id TEXT PRIMARY KEY,
    provider TEXT NOT NULL,
    model TEXT NOT NULL,
    whisperApiKey TEXT,
    deepgramApiKey TEXT,
    elevenLabsApiKey TEXT,
    groqApiKey TEXT,
    openaiApiKey TEXT
);

```

## Schema Evolution with Later Migrations

The Meetily SQLite schema is not static. Beyond the initial six tables, later migrations introduce new functionality.

The migration [`frontend/src-tauri/migrations/20250920155811_add_openrouter_api_key.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/migrations/20250920155811_add_openrouter_api_key.sql) extends the `settings` table with an `openRouterApiKey` column for OpenRouter integration. Another migration, [`frontend/src-tauri/migrations/20251223000000_add_meeting_notes.sql`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/migrations/20251223000000_add_meeting_notes.sql), introduces a `meeting_notes` table designed for user-taken markdown or JSON notes. These incremental changes are applied automatically by the Rust migration runner in [`frontend/src-tauri/src/database/mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/mod.rs), ensuring that existing installations upgrade seamlessly.

## Rust Examples for Database Operations

Meetily's Tauri backend interacts with the schema through standard SQLite queries executed via the Rust `rusqlite` interface. Below are common patterns extracted from the codebase.

### Inserting a New Meeting

```rust
let sql = r#"
    INSERT INTO meetings (id, title, created_at, updated_at)
    VALUES (?1, ?2, datetime('now'), datetime('now'));
"#;
db.execute(sql, (&meeting_id, &title))?;

```

### Adding a Transcript for a Meeting

```rust
let sql = r#"
    INSERT INTO transcripts (id, meeting_id, transcript, timestamp)
    VALUES (?1, ?2, ?3, datetime('now'));
"#;
db.execute(sql, (&transcript_id, &meeting_id, &raw_text))?;

```

### Updating the Summary Status

```rust
let sql = r#"
    INSERT INTO summary_processes (meeting_id, status, created_at, updated_at)
    VALUES (?1, 'running', datetime('now'), datetime('now'))
    ON CONFLICT(meeting_id) DO UPDATE SET
        status = excluded.status,
        updated_at = datetime('now');
"#;
db.execute(sql, (&meeting_id,))?;

```

### Reading the Configured LLM Provider

```rust
let row: (String, String, String) = db.query_row(
    "SELECT provider, model, whisperModel FROM settings WHERE id = 'default'",
    [],
    |r| Ok((r.get(0)?, r.get(1)?, r.get(2)?))
)?;

```

## Summary

- Meetily uses a single SQLite database with migrations stored under `frontend/src-tauri/migrations`.
- The core schema defines six tables: `meetings`, `transcripts`, `summary_processes`, `transcript_chunks`, `settings`, and `transcript_settings`.
- Foreign keys with `ON DELETE CASCADE` tie transcripts, chunks, and summary state directly to a meeting record.
- `summary_processes` tracks asynchronous LLM jobs, while `transcript_chunks` stores the segmented text sent to the model.
- Global and per-transcription settings are separated into the `settings` and `transcript_settings` tables.
- Subsequent migrations add columns such as `openRouterApiKey` and new tables such as `meeting_notes`.

## Frequently Asked Questions

### What is the primary key type used across Meetily's SQLite tables?

Meetily uses **TEXT** primary keys for all tables, including the `meetings` and `transcripts` tables. This design choice keeps identifiers human-readable and avoids integer-sequence complexity in the Tauri backend.

### How does Meetily handle deletion of a meeting and its related data?

The schema enforces `ON DELETE CASCADE` on foreign keys in `transcripts`, `summary_processes`, and `transcript_chunks`. When a row in the `meetings` table is deleted, SQLite automatically removes all linked transcripts, summarisation states, and chunk records.

### Where is the Meetily database migration logic implemented?

Migration files are located in `frontend/src-tauri/migrations` and executed by the Rust database module at [`frontend/src-tauri/src/database/mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/mod.rs). This module opens the SQLite connection and applies pending migrations when the application starts.

### Can different meetings use different LLM providers or API keys?

Yes. While the `settings` table stores global defaults, the `transcript_settings` table allows per-transcription overrides. It supports distinct values for `provider`, `model`, and provider-specific API keys such as `groqApiKey`, `openaiApiKey`, and `deepgramApiKey`.