Skip to main content

Overview

BrokerOS relies on PostgreSQL managed through Prisma 7. The schema located at packages/prisma/schema.prisma serves as the single source of truth for the entire database architecture. All database queries across apps/api and apps/workers use the strongly typed Prisma client generated to packages/prisma/generated/client and re-exported via @brokeros/prisma. Raw SQL strings are strictly prohibited in application code.

The Core Invariant (Project.isCpProject)

The BrokerOS platform hosts two completely distinct business models inside a single database instance:
  1. Internal Brokerage: Internal sales team sells developer inventory directly to prospective homebuyers. Projects are flagged with isCpProject = false.
  2. Channel Partner (CP): External broker network recruited by Sourcing Managers and closed by Closing Managers. Projects are flagged with isCpProject = true.
The Law of Business Separation: Exactly one boolean flag (Project.isCpProject) separates these two enterprise worlds. Never mix leads, units, site visits, or commission records across this boundary. All queries, listing endpoints, and role scopes must filter explicitly on isCpProject.

Entity Relationship Overview


Telephony & Softphone Architecture Models

The database tracks both company-wide carrier infrastructure and individual softphone call sessions:

1. CrmTelephonyConfig

Manages the organization’s active carrier connection configured at Dashboard -> Admin -> Settings -> CRM Telephony & WebRTC:
Single Active Carrier Rule: Only one CrmTelephonyConfig record may have isActive = true at any given time. Connecting a new carrier automatically marks prior configurations inactive.

2. CallRecord

Stores every inbound and outbound telecalling interaction, carrier identifiers, audio recording URLs, and AI intelligence:

Transaction Law (prisma.$transaction)

Multi-model writes must always execute inside an interactive transaction (prisma.$transaction) to guarantee atomicity. For example, converting a lead into a confirmed booking:
If any step fails (e.g., duplicate customer constraint or broker commission calculation error), the entire transaction rolls back, leaving inventory units unlocked.

Soft-Delete Pattern

To comply with real estate regulatory requirements and maintain financial audit trails, entity records are never hard-deleted from the database:
All query services in apps/api/ must explicitly append the filter:
Records with non-null deletedAt timestamps are excluded from active dashboards, search indexes, and campaign recipient pools while remaining intact for historical audit reporting.

Indexing & Performance Guidelines

The BrokerOS schema enforces indexes on frequently filtered fields:
  • Composite index on [projectId, status, deletedAt] for fast lead pipeline queries
  • Index on [phoneNumberId, createdAt] for incoming WhatsApp message lookups
  • Unique index on [email, deletedAt] for customer deduplication
  • Indexes on foreign keys (brokerId, assignedUserId, unitId) to optimize relational joins
  • Index on providerCallId for fast lookup during carrier recording and status webhook callbacks
  • Composite index on [status, scheduledAt] across WhatsAppMessage, EmailMessage, and SmsMessage models for sub-millisecond worker polling of queued messages

Omnichannel Scheduled Messaging Models

To support time-delayed 1-on-1 team inbox messaging across WhatsApp, SMS, and Email, each messaging table incorporates dedicated scheduling attributes: All three models define @@index([status, scheduledAt]) to ensure the background scanner worker performs non-blocking index scans every 10 seconds.

Migration & Seed Workflows

Database schema changes and migrations must be managed through Turborepo from the repository root:
Never manually edit or delete applied migration files in packages/prisma/migrations/. Always generate forward-only migrations.