Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt

Use this file to discover all available pages before exploring further.

KaireonAI persists all platform state in 101 Prisma models declared in platform/prisma/schema.prisma. This page indexes every model by domain, names the underlying PostgreSQL table (@@map), and links each model back to the API or operator surface that owns it.

What it indexes

The schema file at platform/prisma/schema.prisma is the single source of truth for every persisted entity in the platform. Every API route, cron job, MCP tool, and worker reads or writes one or more of these models. The Tenant model anchors multi-tenancy — most other models carry a tenantId column and an index on it. This page exists because operators and contributors need a one-screen overview before they grep for symbols. For per-API request and response shapes, follow the per-domain links in the Reference tables below; this page only documents the persistence layer.

Quick start

# View live models in a browser at localhost:5555
cd platform && npx prisma studio

# Format schema in place
cd platform && npx prisma format

# Sync schema changes to the database (dev only — destructive in production)
cd platform && npx prisma db push

# Regenerate the Prisma client into platform/generated/prisma/
cd platform && npx prisma generate
The schema file is 2,333 lines long; jump to a model with grep -n '^model {Name}' platform/prisma/schema.prisma.

How it works

Prisma 7 lifecycle

Every model in schema.prisma maps to one PostgreSQL table via the @@map("table_name") directive. The Prisma client generator at the top of the schema emits a typed client into platform/generated/prisma/; that path is gitignored and regenerated by npm run build (which runs prisma generate && next build) and by the postinstall hook on npm install. The datasource block in schema.prisma declares only the provider — provider = "postgresql" (no url). The connection URL lives in platform/prisma.config.ts and is read from DATABASE_URL. This split is required by Prisma 7; setting url directly inside schema.prisma errors out with The datasource property 'url' is no longer supported in schema files.

Multi-tenant isolation

Every tenant-scoped model carries a tenantId String column and at least one composite index that begins with tenantId. API routes enforce isolation by adding where: { tenantId } on every query through the requireTenant helper at platform/src/lib/tenant.ts. The Tenant table itself (tenants) holds tenant-level settings — settings (JSON), aiAnalyzerSettings (JSON), isPlayground (boolean) — that gate features per tenant. A handful of models — User, AuditLog, Account, Session, VerificationToken, TenantRegion — either hold cross-tenant rows or scope by userId instead. Each is called out in its row below.

Soft delete vs hard delete

Models with operator-visible “archive” semantics carry a nullable deletedAt DateTime? column and an index on [tenantId, deletedAt]. API routes filter out soft-deleted rows by default; the audit log captures the soft-delete event. Models with deletedAt include Category, SubCategory, Channel, Placement, FlowRoute, Offer, Creative, OutcomeType, QualificationRule, ContactPolicy, DecisionFlow, TriggerRule, GuardrailRule, ArbitrationProfile, SummaryDefinition. Other models — Suppression, OutboxEvent, DeadLetterEvent, PipelineRun, DecisionTrace, InteractionHistory — are hard-deleted by retention crons (platform/src/app/api/v1/cron/cleanup/route.ts) per the RetentionConfig.dataClass policy.

IR-native pipelines

Pipeline.irVersion is String NOT NULL DEFAULT "1.0" — every pipeline is IR-native after the legacy ETL editor was deleted on 2026-04-28. The full DAG lives in PipelineIrVersion.ir (JSONB). The legacy PipelineNode and PipelineEdge models were removed in the same change; the pipeline_nodes and pipeline_edges Postgres tables were dropped via platform/prisma/manual-sql/04_drop_legacy_pipeline_tables.sql.

Reference

Each subsection groups models by domain. Tables list the model name, the underlying Postgres table (@@map), the most operationally relevant fields, primary relations, and a one-line purpose. Schema line numbers below are valid against the schema as of 2026-04-30.

Decisioning core

The studio entities that make up an offer catalog and the four-stage decisioning pipeline (Eligibility / Fit Filters / Match Scoring / Ranking).
Model@@mapKey FieldsRelationsPurpose
Tenanttenantsname, slug, settings, aiAnalyzerSettings, isPlaygroundConversation[]Multi-tenant root; per-tenant feature flags live in settings JSON (schema.prisma:12).
Categorycategoriesname, customFields, ordinal, deletedAtSubCategory[], Offer[]Top-level offer grouping (e.g. “Cards”, “Loans”). Computed-field formulas live in customFields (schema.prisma:30).
SubCategorysub_categoriesname, categoryId, customFieldsCategory, Offer[]Child grouping below Category (e.g. “Travel” under “Cards”) (schema.prisma:54).
ChannelchannelschannelType, deliveryMode, impressionMode, providerConfig, fileConfigCreative[], Placement[]Delivery channel — email/sms/push/web/whatsapp/etc. impressionMode decides whether /recommend auto-records impressions (schema.prisma:80).
PlacementplacementsslotType, maxSlots, channelId, targetingChannel, Creative[]A targeted slot inside a channel (hero, banner, modal). Used by FlowRoute lookups (schema.prisma:105).
FlowRouteflow_routeschannelId, placementId, decisionFlowId, priorityMaps (channel, placement) to a DecisionFlow. Cached 120s in the /recommend hot path (schema.prisma:127).
OfferofferscategoryId, subCategoryId, priority, mandatory, eligibility, budget, scheduleCategory?, SubCategory?, Creative[], InteractionHistory[]Decisioning offer — what the platform recommends. mandatory=true bypasses eligibility (schema.prisma:147).
CreativecreativesofferId, channelId, placementId, templateType, content, personalization, weightOffer, Channel, Placement?, ContentItem?, InteractionHistory[]Channel-specific rendering of an offer. Maps to legacy Treatment table semantically (schema.prisma:194).
OutcomeTypeoutcome_typeskey, classification, category, isSystemPer-tenant taxonomy of outcomes (impression, click, dismiss, convert). System rows are undeletable (schema.prisma:231).
QualificationRulequalification_rulesruleType, config, priority, stage, scope (deprecated)QualificationRuleScope[]Stages: eligibility, fit, match. The legacy scope/scopeId columns are kept for back-compat; new code uses scopes[] (schema.prisma:360).
QualificationRuleScopequalification_rule_scopesqualificationRuleId, scope, scopeIdQualificationRuleMulti-scope assignment — one rule can apply globally and to specific categories/offers (schema.prisma:387).
ContactPolicycontact_policiesruleType, config, priority, scope (deprecated)ContactPolicyScope[]Frequency caps, cooldowns, mutual exclusion. ruleType includes customer_total_cap (Phase 2B-3) and offer_category_cap (schema.prisma:403).
ContactPolicyScopecontact_policy_scopescontactPolicyId, scope, scopeIdContactPolicyMulti-scope assignment for contact policies (schema.prisma:429).
DecisionFlowdecision_flowskey, draftConfig, publishedVersions, arbitrationProfileId, isDefault, isProtectedRun[]Visual decisioning flow. draftConfig holds the working version; publishedVersions[] holds immutable snapshots (schema.prisma:765).
ArbitrationProfilearbitration_profileskey, weights, statusMulti-objective ranking weights (revenue, margin, propensity, engagement) (schema.prisma:1705).
CrossOfferConstraintcross_offer_constraintsscope, ruleType, config, statusMulti-offer arbitration constraints. Read by Lagrangian arbitration in lib/arbitration/cross-offer.ts; threaded into both realtime (realtime-wire.ts:194) and batch (batch-executor.ts:489-510) decisioning paths (schema.prisma:2273).
GuardrailRuleguardrail_ruleskey, severity, expressionAstHard/soft filter expressions evaluated alongside qualification rules (schema.prisma:743).
SuppressionsuppressionscustomerId, scope, policyId, expiresAtPre-computed contact-policy block per customer × scope. Expired rows cleaned by retention cron (schema.prisma:1883).
VolumeConstraintvolume_constraintsscope, scopeId, maxVolume, period, currentCount, resetAtSystem-wide delivery caps on offers, categories, channels. See Volume Constraints (schema.prisma:1840).
BudgetAllocationbudget_allocationsscope, scopeId, periodType, maxImpressions, maxSpend, maxConversions, periodKeyBudget tracking with rolling-window counters per scope (global / category / offer) (schema.prisma:1307).

Customer & interactions

Customer-scoped rows that drive the decisioning loop and the analytics fact tables.
Model@@mapKey FieldsRelationsPurpose
InteractionHistoryinteraction_historycustomerId, recommendationId, offerId, creativeId, interactionType, outcomeTypeKey, direction, deduplicationIdOffer?, CreativeAppend-only fact table for impressions, recommendations, clicks, conversions. Partitioned in production via manual-sql/ (schema.prisma:255).
InteractionSummaryinteraction_summariesperiodType, periodKey, customerId, offerId, dimensionKey, definitionId, impressions, positive, converts, totalValuePre-aggregated rollups by [periodType, periodKey, customerId, offerId, creativeId, channelId]. Driven by SummaryDefinition (schema.prisma:304).
ImpressionimpressionscustomerId, offerId, creativeId, channelId, placementId, contextChannel-level impression mirror written by /respond; InteractionHistory is the canonical record (schema.prisma:1976).
CustomerEngagementHealthcustomer_engagement_healthcustomerId, score (0-1), inputs, computedAtNightly-computed engagement score from a 90-day rollup. Read by engagement-aware contact-policy caps (Phase 2C-3) (schema.prisma:344).
CustomerCLVcustomer_clvcustomerId, clvScore, predictedRevenue, churnProbability, rfmRecency, rfmFrequency, rfmMonetary, segmentPer-customer RFM and lifetime-value scores. Segments: high, medium, low, at_risk (schema.prisma:2061).
IdentityLinkidentity_linkscanonicalId, identifierType, identifierValue, confidenceIdentity resolution graph — maps email/phone/session/device → canonicalId per tenant (schema.prisma:1333).
SegmentsegmentsbaseSchemaId, joins, filters, viewName, customerCountDataSchema, Run[]Definition of an audience over DataSchema rows. Materializes to a Postgres view named seg_{idPrefix} (schema.prisma:796).
ConsentRecordconsent_recordssubjectId, purpose, status, source, grantedAt, revokedAtGDPR/privacy consent ledger per customer × purpose (schema.prisma:1996).

Algorithm models

ML model registry, versioning, governance, and per-scope adaptive learning state.
Model@@mapKey FieldsRelationsPurpose
AlgorithmModelalgorithm_modelskey, modelType, targetField, predictors, metrics, modelState, outcomeWeights, registryStatus, registryFamilyModelVersion[], ModelAdaptation[], Experiment[], ExperimentChallenger[]The model record. modelType is one of scorecard, bayesian, logistic_regression, gradient_boosted (schema.prisma:572).
ModelVersionmodel_versionsmodelId, version, config, modelState, metrics, predictorsAlgorithmModelImmutable historical snapshot of AlgorithmModel state (schema.prisma:657).
ModelAdaptationmodel_adaptationsmodelId, scope, scopeId, positives, negatives, positiveRate, predictorBins, predictorAucsAlgorithmModelPer-scope (global / category / offer / channel) learned propensity. Updated atomically by online learning loop (schema.prisma:629).
ModelApprovalmodel_approvalsmodelId, version, status, requestedBy, reviewedBy, metricsProduction-promotion governance ticket per model version (schema.prisma:1231).
ModelDriftCheckmodel_drift_checksmodelId, checkType, baseline, current, driftScore, driftedPeriodic drift snapshot. checkType is one of psi, ks_test, auc_decay, feature_drift (schema.prisma:1249).
Experimentexperimentskey, championModelId, trafficSplit, autoPromote, holdoutPercent, resultsAlgorithmModel?, ExperimentChallenger[]Champion/challenger experiment definition (schema.prisma:676).
ExperimentChallengerexperiment_challengersexperimentId, modelId, trafficPctExperiment, AlgorithmModelJoin row binding a challenger model to an experiment with a traffic percentage (schema.prisma:701).
ExperimentAssignmentexperiment_assignmentscustomerId, experimentId, variant, assignedAtSticky customer-to-experiment mapping (schema.prisma:1959).
VariantAssignmentvariant_assignmentscustomerId, experimentKey, variantName, assignedAt, expiresAtVariant assignment with optional TTL — used for blueprint/decision-flow A/B tests (schema.prisma:1449).
MlJobResultml_job_resultsjobType, status, input, result, errorAsync ML job ledger — policy_analysis, segmentation, content_analysis (schema.prisma:1746).

Audit & compliance

Tamper-evident logs, retention, consent, and DSAR machinery.
Model@@mapKey FieldsRelationsPurpose
AuditLogaudit_logsrequestId, integrityHash, prevHash, userId, action, entityType, entityId, before, after, entityVersionImmutable audit chain. integrityHash = SHA-256(content + prevHash) builds a tamper-evident chain. API routes block UPDATE/DELETE on this table (schema.prisma:1063).
RetentionConfigretention_configsdataClass, retentionDays, legalHoldPer-tenant retention policy. dataClass includes interactions, decisions, metrics, audit. Cleanup cron honors per-tenant overrides (schema.prisma:1690).
DsarRequestdsar_requestsrequestType (export/delete/rectify), subjectId, subjectType, status, requestedBy, resultData Subject Access Request workflow. See DSAR Portability (schema.prisma:1286).
PolicySnapshotpolicy_snapshotspolicyVersionHash, interactionId, eligibilityPolicies, suppressionPolicies, guardrailRulesFrozen policy configuration referenced by DecisionTrace.policyVersionHash. One row per unique 16-char hash per tenant (schema.prisma:1269).
DecisionTracedecision_tracesrequestId, customerId, candidateCount, afterQualification, afterContactPolicy, qualificationResults, scoringResults, selectedOffers, policyVersionHash, degradedScoringForensic per-decision trace. Sample rate set by tenantSettings.decisionTraceSampleRate (schema.prisma:1604).

Governance

Approval workflows, four-eyes governance, and operator-driven change control.
Model@@mapKey FieldsRelationsPurpose
ApprovalRequestapproval_requestsentityType, entityId, action, payload, requesterId, approverId, statusSingle-stage approval ticket — gates production changes when four-eyes is enabled (schema.prisma:930).
ApprovalRequestStageapproval_request_stagesapprovalRequestId, stageName, ordinal, requiredRole, status, approverIdMulti-stage four-eyes (W14) — one ApprovalRequest can require N sequential approvals (schema.prisma:2255).
CustomRolecustom_rolesname, permissions (JSON array of permission strings)CustomRoleAssignment[]Tenant-defined role bundles for fine-grained authorization (schema.prisma:1418).
CustomRoleAssignmentcustom_role_assignmentsuserId, roleIdCustomRoleJoin row binding a User to a CustomRole per tenant (schema.prisma:1434).
SsoConfigsso_configsprovider (saml/oidc/none), samlEntityId, oidcIssuer, defaultRole, allowedDomains, autoProvision, enforceForAllUsersPer-tenant SSO configuration. One row per tenant (unique on tenantId) (schema.prisma:1393).
WebAuthnCredentialwebauthn_credentialsuserId, credentialId, publicKey (CBOR base64), counter, transports, deviceLabelHardware-backed FIDO2/WebAuthn credentials per user (W18) (schema.prisma:2303).

Operator & infra

Tables consumed by operators, cron jobs, and out-of-band workers.
Model@@mapKey FieldsRelationsPurpose
Userusersemail, password, role, mfaSecret, mfaEnabled, mfaBackupCodes, lockedUntil, failedLoginAttempts, onboardingProgressAccount[], Session[], Conversation[]NextAuth v5 user. Optional tenantId because some users (admins) are cross-tenant (schema.prisma:990).
AccountaccountsuserId, provider, providerAccountId, OAuth tokensUserNextAuth v5 OAuth account binding (schema.prisma:1018).
SessionsessionssessionToken, userId, expiresUserNextAuth v5 active sessions (schema.prisma:1039).
VerificationTokenverification_tokensidentifier, token, expiresNextAuth v5 email-verification + magic-link tokens (schema.prisma:1051).
ApiKeyapi_keysprefix (first 12 chars displayed), hashedKey (SHA-256), expiresAt, lastUsedAt, revokedAtLong-lived API key for X-API-Key auth. Raw key starts with krn_ (schema.prisma:1124).
OAuthClientoauth_clientsclientId (kci_-prefixed), hashedSecret, scopes (read,write,admin)OAuth2 client credentials grant — server-to-server callers (schema.prisma:1144).
TenantSettingstenant_settingsmlWorkerConfig, aiAnalyzerSettings, flowIrEnabledPer-tenant operational toggles. aiAnalyzerSettings.arbitration holds EXP3-IX bandit config (schema.prisma:1944).
PlatformSettingplatform_settingscategory, key, value, encryptedPer-tenant key-value config — SES from-address, Twilio SID, etc. AES-encrypted when encrypted=true (schema.prisma:1586).
OutboxEventoutbox_eventstopic, payload, status, retryCount, maxRetries, lastError, publishedAtTransactional outbox for events. Drained by the outbox publisher worker; on max retries the row moves to DeadLetterEvent (schema.prisma:1162).
DeadLetterEventdead_letter_eventsoriginalEventId, topic, payload, failedAt, errorMessageTerminal event store for outbox publish failures. Read by Operations Runbook (schema.prisma:1180).
ChannelDeliverychannel_deliveriesinteractionId, channelType, providerName, providerMessageId, idempotencyKey, status (pending/sent/delivered/bounced/failed/opened/clicked), attemptsPer-message delivery tracking with provider IDs and idempotency. Powers retry/bounce handling (schema.prisma:1196).
ExportCheckpointexport_checkpointsexportType, lastExportAt, filePath, statusResume point for the Hive interaction-history export job (schema.prisma:1906).
AlertRulealert_rulesmetric, operator, threshold, windowMinutes, cooldownMinutes, channels, lastFiredAt, statusThreshold-based alerts. channels is a JSON list of notification destinations (schema.prisma:1466).
RunrunsdecisionFlowId (mapped to blueprintId), segmentId, scheduleType, volumeConstraints, fileConfig, channelIdsDecisionFlow, Segment, CampaignRun[]Scheduled batch campaign config. Triggers CampaignRun rows on each execution (schema.prisma:823).
CampaignRuncampaign_runscampaignId, runNumber, status, totalCustomers, processed, recommended, summaryRunOne execution of a Run (campaign). One row per fired schedule tick (schema.prisma:876).
TriggerRuletrigger_ruleseventType, condition, actionType, actionConfig, priority, cooldownMsEvent-driven trigger — when eventType matches condition, fire actionType (schema.prisma:904).
SimulationRunsimulation_runssegmentId, decisionFlowKey (mapped to blueprintKey), sampleSize, config, resultsWhat-if simulation snapshot. Re-runs a flow over a segment sample without writing decisions (schema.prisma:955).
AttributionResultattribution_resultscustomerId, conversionId, model, touchpoints, totalValuePer-conversion multi-touch attribution result. model is the attribution model name (e.g. linear, time_decay) (schema.prisma:973).
Geofencegeofenceslatitude, longitude, radiusMeters, triggerOn (enter/exit/dwell), actionPer-tenant geofence — fires action.type (recommend/notify) when a customer crosses (schema.prisma:2036).
TenantRegiontenant_regionstenantId (PK), region, primary, failoverRegionPer-tenant region tag for active-active routing (W18) (schema.prisma:2292).

AI & imports

Conversation ledger, AI recommendations, and the AI document import pipeline (V1).
Model@@mapKey FieldsRelationsPurpose
ConversationconversationsuserId, titleTenant, User, ConversationMessage[]One AI assistant conversation per (tenant, user) (schema.prisma:1807).
ConversationMessageconversation_messagesconversationId, role, content, toolInvocationsConversationSingle message in a conversation. toolInvocations is the tool-call/result trace (schema.prisma:1824).
AiRecommendationai_recommendationstype (policy/rule/segment/content), title, payload, confidence, source (llm/ml_worker), statusPending AI suggestion that can be applied as an entity (schema.prisma:1726).
CustomerSegmentAicustomer_segments_ainame, filterRules, schemaId, size, percentage, characteristics, suggestedUseAI-generated customer segment proposal — separate from the user-defined Segment (schema.prisma:1785).
CreativePerformancecreative_performancecreativeId, channelId, offerId, impressions, clicks, conversions, revenue, periodStart, periodEndPre-aggregated creative-level performance window for AI content analysis (schema.prisma:1764).
AiAttachment (no @@map)AiAttachmentconversationId, originalFilename, mimeType, sha256, storageKey, pageCount, tokensUsed, estimatedTokens, statusUploaded document (PDF/PPTX) for AI document import. Dedup by [tenantId, sha256] (schema.prisma:2170).
AiImportProposal (no @@map)AiImportProposalattachmentId, entityType, extractedName, extractedFields, sourcePageNumber, sourceQuote, dedupeMatchId, dedupeStrategy, verdictTyped proposal extracted from an attachment with page citations (schema.prisma:2196).
AiImportApply (no @@map)AiImportApplyattachmentId, conversationId, appliedById, createdEntityIds, mergedEntityIds, skippedProposalIds, status, revertedAtAtomic apply ledger — records what was created/merged/skipped per import. Soft-revert via revertedAt (schema.prisma:2217).
AiImportTokenLedger (no @@map)AiImportTokenLedgertenantId, yearMonth, tokensUsedPer-tenant per-month LLM token spend for AI document import (schema.prisma:2233).
AiImportSkipDigest (no @@map)AiImportSkipDigestdigest, entityType, extractedNameSkip-tally bookkeeping — remembers proposals the operator dismissed so re-imports don’t re-surface them (schema.prisma:2243).
NegotiationSessionnegotiation_sessionscustomerId, offerId, state, status (active/accepted/rejected/expired)Multi-turn negotiation state (W15). state deserializes to MultiTurnSession from lib/negotiation/multi-turn.ts (schema.prisma:2320).

Journeys

Multi-step customer journey orchestration — entry conditions, step definitions, and per-customer enrollment state.
Model@@mapKey FieldsRelationsPurpose
Journeyjourneysname, definition (JourneyDefinition JSON), entryCondition, maxDurationDaysJourneyEnrollment[]Visual journey definition — steps, branches, wait/decision/action nodes. definition is the full DAG (schema.prisma:1349).
JourneyEnrollmentjourney_enrollmentsjourneyId, customerId, currentStepId, status (active/completed/exited), enteredAt, lastStepAt, historyJourneyPer-customer enrollment state. history is the chronological list of visited steps (schema.prisma:1368).

Flow & pipeline

Data ingestion, schema management, and pipeline runtime.
Model@@mapKey FieldsRelationsPurpose
Connectorconnectorstype (54 registered types), config, authMethod, authConfig, status, lastTestedAt, lastErrorPipeline[]External data source connection (S3, Snowflake, Kafka, Salesforce, etc.). type enum lives in src/domain/connector-registry.ts (schema.prisma:501).
DataSchemadata_schemasname, tableName (ds_*), entityType, schemaType, linkedSchemaId, joinMapping, summaryColumns, autoEnrichSchemaField[], Pipeline[], Segment[]Defines an entity (customer, account, custom). Creates a real Postgres table named ds_{name} (schema.prisma:445).
SchemaFieldschema_fieldsname, dataType, length, precision, scale, isNullable, isPrimaryKey, isUnique, isPredictor, defaultValueDataSchemaTyped column on a DataSchema. isPredictor=true exposes the field to ML model training (schema.prisma:476).
SchemaJoinschema_joinsprimarySchemaId, primaryKey, foreignSchemaId, foreignKey, joinType (left/inner), autoEnrichAuto-enrichment join definition between two DataSchemas. Used by the runtime to widen customer rows (schema.prisma:2016).
DataSourcedata_sourcesconnectorKey, sourceConfig, fileFormat, syncMode (full_refresh/incremental_append/incremental_dedupe/cdc), schedule, schemaKeyA logical source bound to a Connector. Schedules + sync mode drive the pipeline runner (schema.prisma:716).
PipelinepipelinesconnectorId, schemaId, irVersion (always "1.0"), executionConfig, lastRunStatusConnector, DataSchema, PipelineRun[]IR-native pipeline. The DAG is in PipelineIrVersion.ir (JSONB) — there is no PipelineNode/PipelineEdge table anymore (schema.prisma:525).
PipelineIrVersionpipeline_ir_versionspipelineId, version, ir, authoredBy (user id or "ai"), commentVersion-controlled pipeline IR document. version is monotonically increasing per pipeline (schema.prisma:550).
PipelineRunpipeline_runspipelineId, status, rowsProcessed, rowsTotal, rowsFailed, validationErrors, loadStrategy (append/truncate/upsert/blue_green), partitions, lastProcessedRowPipelineSingle pipeline execution record. lastProcessedRow is the resume checkpoint (schema.prisma:1091).

Content

Reusable content templates, CMS-sync content items, and template inheritance.
Model@@mapKey FieldsRelationsPurpose
Templatetemplatescategory (email/push/sms/blueprint/offer), type, content, variables, isSystemReusable template — including system-shipped seeds when isSystem=true (schema.prisma:1489).
ContentItem(no @@map; inferred ContentItem table)name, channelType, status (draft/in_review/approved/published/archived), parentTemplateId, sourceType (internal/wordpress/contentful/strapi/sanity), versionContentItem? (parent), ContentItem[] (children), ContentSource?, ContentVersion[], Creative[]Channel-typed content payload. CMS-synced when sourceType != "internal" (schema.prisma:1510).
ContentVersion(no @@map)contentItemId, version, content, blocks, personalization, changedBy, changeNoteContentItemImmutable historical snapshot of ContentItem (schema.prisma:1545).
ContentSource(no @@map)provider (wordpress/contentful/strapi/sanity), config, syncMode (webhook/polling), autoPublish, mappings, webhookSecretContentItem[]External CMS connection — pulls content into ContentItem rows (schema.prisma:1562).
Treatmenttreatmentsname, channel, subject, headline, body, config, impressions, clicks, conversionsAI-generated content entity — separate table from Creative (schema.prisma:1921).

Reports

Scheduled report templates, schedules, and execution runs.
Model@@mapKey FieldsRelationsPurpose
ReportTemplatereport_templatesdataSources, sections, formats (default ["pdf"]), narrative ({enabled, prompt?, modelOverride?}), filters, enabledComposable report definition. Reusable across schedules (schema.prisma:2103).
ReportSchedulereport_schedulestemplateId, cronExpression, timezone, destinations, lastRunAt, nextRunAt, lastStatusCron schedule binding a template to delivery destinations (schema.prisma:2123).
ReportRunreport_runstemplateId, scheduleId, triggeredBy (default cron), sectionsData, narrativeOutput, artifactPayloads, deliveryResultsPer-execution audit row. Artifact payloads are inlined; the runner documents an S3 upgrade path (schema.prisma:2143).

Behavioral metrics & summaries

User-defined metrics evaluated against the interaction stream.
Model@@mapKey FieldsRelationsPurpose
MetricDefinitionmetric_definitionsaggregateFunction (count/sum/avg/min/max/ratio), sourceField, windowDays (max 365), groupByDimensions (max 2), filterConditions, sqlFilter (max 500 chars), computeMode (realtime/batch), batchIntervalMinMetricValue[]User-defined behavioral metric. Compiled into a query against InteractionSummary (schema.prisma:1641).
MetricValuemetric_valuesmetricId, customerId, dimensionKey, value, computedAtMetricDefinitionMaterialized per-customer × dimension value. Unique on [tenantId, metricId, customerId, dimensionKey] (schema.prisma:1670).
SummaryDefinitionsummary_definitionsdimensions, aggregates (default ["count"]), windows (default ["7d","30d"]), isSystemPluggable shape for InteractionSummary aggregation. isSystem=true rows are undeletable (schema.prisma:1860).

Configuration

Prisma 7 datasource split

// platform/prisma/schema.prisma — datasource block
datasource db {
  provider = "postgresql"
}
// platform/prisma.config.ts — connection URL
import "dotenv/config";
import { defineConfig } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: { path: "prisma/migrations" },
  datasource: { url: process.env["DATABASE_URL"]! },
});
The url field is illegal inside schema.prisma under Prisma 7. The connection URL is read from DATABASE_URL by prisma.config.ts and passed to the generated client.

ds_* tables created outside the schema

DataSchema rows do not declare their column shape inside schema.prisma. Each DataSchema row holds metadata; the actual ds_{name} table is created at runtime by platform/src/lib/db/ddl.ts via CREATE TABLE statements when the schema is published. Adding a SchemaField issues ALTER TABLE ds_{name} ADD COLUMN. This split exists because the column shape is per-tenant and dynamic — it cannot be declared statically in a global schema.prisma. Operators viewing \d ds_* in psql will see real Postgres tables that are not represented in schema.prisma.

Manual SQL for partitioning and migrations

Schema changes that Prisma cannot express are stored in platform/prisma/manual-sql/:
FilePurpose
01_registry_status_check.sqlAdds CHECK constraint to algorithm_models.registryStatus
02_flow_ir_phase1.sqlInitial flow IR table grants
04_drop_legacy_pipeline_tables.sqlDrops legacy pipeline_nodes + pipeline_edges (2026-04-28)
05_campaign_id_additive.sqlAdds nullable campaignId to interaction_history + interaction_summaries
06_customer_engagement_health.sqlBootstrap for CustomerEngagementHealth
07_flow_run_checkpoints.sqlPer-tenant flow checkpoint storage
08_ai_import.sqlAI document import bootstrap
09_parity_w11_to_w19.sqlW11–W19 parity-sprint additions
Run these in numerical order against any database that pre-dates them. Production databases run them through CI; local databases need a manual psql -f or prisma db execute --file.

Honest limits

  • ds_* tables are not in schema.prisma. The dynamic per-tenant entity tables created from DataSchema rows live outside the schema file. There is no way to type-check them through Prisma — runtime queries against ds_{name} go through raw SQL or generated query builders.
  • InteractionHistory is partitioned in production. The model declares one logical table; production deployments range-partition interaction_history by month, which is why /recommend writes use prisma.$executeRaw instead of prisma.createMany({ skipDuplicates }). The Prisma model does not declare partitioning.
  • Soft-delete is not Prisma-enforced. Models with deletedAt rely on every API route to filter where: { deletedAt: null }. There is no global Prisma middleware enforcing this — a raw query or a route that forgets the filter will return soft-deleted rows.
  • CrossOfferConstraint has no UI surface yet. The model is wired into Lagrangian arbitration in both realtime (lib/arbitration/realtime-wire.ts:194) and batch (lib/batch-executor.ts:489-510) decisioning paths via lib/arbitration/cross-offer.ts, but rows must be inserted directly via SQL or a future admin API — there is no Studio UI for editing them today.
  • Some operator-internal models have no public API surface. OutboxEvent, DeadLetterEvent, ChannelDelivery, ExportCheckpoint, MlJobResult, AiImportTokenLedger, AiImportSkipDigest, PolicySnapshot, ApprovalRequestStage, CustomRoleAssignment, VerificationToken, WebAuthnCredential, TenantRegion, ModelAdaptation, CustomerEngagementHealth are read/written by internal code paths only — there is no /api/v1/{model} REST surface for them.
  • Several alternate storage backends are referenced but not declared here. ScyllaDB / DynamoDB / OpenSearch backends mentioned in architecture/scaling.mdx are write paths inside platform/src/lib/ adapters; their row shapes are not declared in schema.prisma. Treat schema.prisma as the authoritative shape for the canonical Postgres path only.
  • AI import models (AiAttachment, AiImportProposal, AiImportApply, AiImportTokenLedger, AiImportSkipDigest) and content models (ContentItem, ContentVersion, ContentSource) lack @@map directives. Their Postgres table names default to the model name as written (AiAttachment, ContentItem, etc.) rather than snake_case — verify via \dt in psql.