Reference

Data model

The Convex tables that back documents, their extracted content, and conversations — plus the indexes that power retrieval.

Tables

5 core

Relations

5 foreign keys

Embeddings

1536-dim

Indexes

Vector + text

Schema at a glance

Five tables back the product. A documents row owns its extracted documentPages and documentChunks, plus every conversations thread started against it; each thread owns its messages. Foreign keys are Convex document IDs (v.id("table")), and every content row also carries an ownerTokenIdentifier that authorization filters on for every read.

LegendPKPrimary keyFKForeign keyIDXIndexedVECVector index (1536-dim)FTSFull-text search indexOWNAuthorization scope — filtered on every read
documents12 cols

One row per uploaded PDF; tracks its lifecycle and artifacts.

  • _ididPK
  • ownerTokenIdentifierstringOWNIDX
  • titlestring
  • originalFilenamestring
  • sha256string
  • fileStorageId?id_storageFK
  • statusunionIDX
  • pageCount?number
  • documentSummarystring
  • processingError?string
  • lastProcessedAt?number
  • embeddedChunkCount?number
documentPages7 cols

OCR text and a summary for a single page of a document.

  • _ididPK
  • documentIdiddocumentsFKIDX
  • ownerTokenIdentifierstringOWNIDX
  • pageNumbernumberIDX
  • extractedTextstring
  • summarystring
  • embedding?float64[1536]VEC
documentChunks10 cols

The retrieval unit — overlapping text windows with page spans.

  • _ididPK
  • documentIdiddocumentsFKIDX
  • ownerTokenIdentifierstringOWNIDX
  • chunkIndexnumberIDX
  • startPageNumbernumber
  • endPageNumbernumber
  • textstringFTS
  • tokenCountnumber
  • pageSpansobject[]
  • embeddingfloat64[1536]VEC
conversations4 cols

A chat thread scoped to a single document.

  • _ididPK
  • documentIdiddocumentsFKIDX
  • ownerTokenIdentifierstringOWNIDX
  • titlestring
messages6 cols

A single turn in a conversation, with validated citations.

  • _ididPK
  • conversationIdidconversationsFKIDX
  • roleunion
  • contentstring
  • status?union
  • citations?object[]documentChunks
Relationships
  • documents1 : NdocumentPageseach page of the PDF
  • documents1 : NdocumentChunksretrieval windows
  • documents1 : Nconversationschats about the doc
  • conversations1 : Nmessagesturns in a thread
  • documentChunks0 : Nmessagescited by (citations[].chunkId)

Trimmed to the columns that carry weight — see “Columns trimmed from the design” below for what was removed and why.

Entity relationships

The same model as an entity-relationship diagram. Solid edges are hard foreign keys; the dashed edge is a soft reference — a message's citations point back at the documentChunks they quote.

Rendering diagram…
documents is the aggregate root; conversations → messages is the chat hierarchy. Owner tokens are omitted for readability.

Indexes & citation fields

Indexes that power retrieval

documentChunks carries a vector index by_embedding (1536-dim, filtered by documentId) and a full-text search_text index. Hybrid retrieval queries both and fuses the results; documentPages keeps its own vector index for page-level search.

Two composite fields do the heavy lifting for grounded answers:

FieldTypeDescription
documentChunks.pageSpansobject[]{ pageNumber, startOffset, endOffset } — maps any position in a chunk back to a page, so a quote resolves to the page it came from.
messages.citationsobject[]?Validated citations: pageNumber, snippet, chunkId (→ documentChunks), verbatim quote, and its character offsets.

Columns trimmed from the design

The live schema stores extra provenance the design above leaves out. These are safe candidates to drop — they duplicate information already available from status, _creationTime, or environment config:

  • documents: per-stage timestamps (uploadCompletedAt, processingStartedAt, ocrCompletedAt, embeddingsCompletedAt) collapse into _creationTime + lastProcessedAt + status.
  • documents: model / provider strings (summaryModel, embeddingModel, ocrModel, ocrMethod, ocrProvider) and vendor handles (mistralFileId, ocrResultStorageId, storageContentType, processingAttemptCount) are config- or log-level detail, not query inputs.
  • documentPages / documentChunks: extractionMethod and per-row embeddingModel / embeddingTokenCount are constant or derivable; ownerDocumentKey duplicates ownerTokenIdentifier + documentId.
  • conversations / messages: explicit createdAt duplicates Convex's built-in _creationTime.

Documentation only

This page shows the trimmed design. The trims are not yet applied to convex/schema.ts — removing a column also means updating every mutation and query that writes or reads it.