Skip to Content
DatabaseDatabase Documentation

Database Documentation

The system uses two database engines — PostgreSQL for relational, transactional data and MongoDB for large blobs, documents, and high-volume append-only logs.


Contents

DocWhat it covers
Database OverviewWhich data goes where and why — decision guide, split pattern, key relationships
PostgreSQL SchemaFull PostgreSQL schema: all tables, indexes, useful queries, security
MongoDB CollectionsAll MongoDB collections: schemas, indexes, TTLs, security
SQL Entity RelationsER diagrams for all PostgreSQL tables and their relationships
NoSQL Entity RelationsMongoDB collections and their cross-references to PostgreSQL

Quick Decision Guide

PostgreSQL — if the data:

  • Has relationships to other records (foreign keys matter)
  • Needs counting, aggregation, or billing queries
  • Participates in ACID transactions (status state machines)
  • Is small-to-medium in size with a fixed schema

MongoDB — if the data:

  • Has variable or unpredictable structure (arbitrary JSON diffs, integration payloads)
  • Is an append-only log with TTL cleanup and no relational queries
  • Needs full-text search (skills content)
  • Is small-to-medium structured metadata accessed by a single FK from PostgreSQL

S3 — if the data:

  • Is a large binary or large text blob (PDF, multi-KB Markdown, scraped website text)
  • Has no query requirements — accessed only by a known key
  • Needs immutability guarantees (Object Lock / WORM for legal records)
  • Benefits from direct client access without API proxying (pre-signed URLs)

See the overview for the full decision table.


Key Architecture Patterns

Split Pattern (PostgreSQL + MongoDB + S3)

Large content is split across all three stores. PostgreSQL holds structured metadata; MongoDB holds variable-structure metadata or operational logs; S3 holds the actual content bytes.

EntityPostgreSQL holdsMongoDB holdsS3 holds
Activity outputStatus, output_ref pointerMetadata + s3Key (activity_outputs)Full content (Markdown/JSON)
Blog postMetadata (status, channel, word count)Full article Markdown (via output_ref → activity_outputs.s3Key)
Social postMetadata (status, platform)Post copy + hashtags (via output_ref → activity_outputs.s3Key)
Onboarding runStatus, context_file_idS3 key pointers (onboarding_research)Scraped text, research, context file
StrategyVersion, status, dates, approvalS3 key pointer (strategies_content)Full strategy Markdown
ContractAcceptance metadata, pdf_s3_keyPDF binary (Object Lock / WORM)
Tool callSummary (name, method, status)Full payloads (tool_call_logs)

Multi-Tenancy

All records are scoped to a tenant:

  • PostgreSQL: tenant_id UUID NOT NULL REFERENCES tenants(id) on every table
  • MongoDB: tenantId: string on every document

Queries must always include the tenant filter. Global records (e.g. global skill templates) use tenantId: 'global' in MongoDB and tenant_id IS NULL in PostgreSQL.

Identifiers

IdentifierUsed forFormat
id (PostgreSQL)Internal FK joins onlyUUID
ref_id (PostgreSQL)External APIs, URLs, webhooksULID (26 chars, sortable)
_id (MongoDB)MongoDB internal primary keyObjectId
refId (MongoDB)External referencesULID

PostgreSQL ref_id values and MongoDB ObjectIds are stored as cross-reference pointers (e.g. output_ref, content_ref, pdf_ref).


Storage Engines

PostgreSQL 16MongoDB 7S3 / MinIO
ORM/ODM/SDKPrisma ORMMongoose@aws-sdk/client-s3
Packageproviders/provider-db/providers/provider-db/providers/provider-storage/
ConnectionVia PgBouncer (transaction mode)Direct MONGO_URLS3_ENDPOINT + credentials
Multi-tenancytenant_id column + FK on every tabletenantId field on every document{tenantId}/ key prefix
Soft deletesWHERE deleted_on IS NULL{ deletedOn: null }Object versioning + lifecycle rules
Local devDocker Compose postgres:16Docker Compose mongo:7Docker Compose minio/minio
ProductionManaged cloud PostgreSQLMongoDB Atlas or self-hostedAWS S3 or DO Spaces
On-premSelf-hosted PostgreSQLSelf-hosted MongoDBSelf-hosted MinIO

© 2026 Leadmetrics — Internal use only