---
title: Data Model Overview
tags: [data-model, mongoose, overview]
aliases: [Models Index, Schema Overview]
---

# Data Model Overview

This section documents every Mongoose model that backs the marketplace. On backend `integrate-main-into-development@cab0719`, these Mongoose models are still the live application persistence layer. The repo also contains a Drizzle/Postgres migration layer, but most services still call `backend/src/models/*` directly.

> [!note] Scope
> Twenty-two models are present in `backend/src/models/`. The "File" concept exists only at the service layer (`backend/src/services/file/`) and is not persisted as its own Mongoose collection, so it is not listed below.
>
> [!note] Documentation freshness
> The 2026-05-24 audit note that marked `Dispute`, `BlogPost`, `Review`, `PointTransaction`, `LevelConfig`, and `ShopSettings` as missing is now stale: schema files exist for those models. Newer operational models such as [[ConfigSetting]], [[DerivedDestination]], [[FundsLedgerEntry]], and [[TrezorAccount]] should be expanded into dedicated model pages when the docs are next deepened.

> [!warning] Mongo vs Postgres runtime status
> Postgres schemas and repositories exist for the money/relational core, but normal app traffic is not fully cut over. Payment quote rows are the only current conditional PG write in checkout, and even that requires `ORACLE_QUOTING_ENABLED=true` plus a resolvable PG payment row. See [[Postgres Runtime Cutover Status]].

## Index of Models

- [[User]] — Core identity. Stores credentials, profile, preferences, referral data, points, and WebAuthn passkeys. Every other model that records "who did what" points back at a `User._id`. Buyers, sellers, and admins all live in this collection, differentiated by a `role` enum.
- [[PurchaseRequest]] — The buyer-side document at the heart of the marketplace. Captures what a buyer wants, the budget, urgency, delivery preferences, and the full lifecycle status (`pending_payment` → `seller_paid`). Aggregates [[SellerOffer]] references and tracks delivery codes.
- [[SellerOffer]] — A seller's bid against a [[PurchaseRequest]]. Holds price, delivery ETA, attachments, and a small status machine (`pending` / `accepted` / `rejected` / `withdrawn`).
- [[Payment]] — Records monetary movement intent and state: buyer pay-in, seller release, and refund. The current primary provider path is Request Network plus in-house checkout, derived destinations, funds ledger entries, and Transaction Safety Provider metadata.
- [[Chat]] — Conversation container with embedded messages, participants, unread counters, and reactions. Used for direct buyer-seller chats, group chats, and support tickets. Can be linked to a [[PurchaseRequest]] or [[SellerOffer]].
- [[Notification]] — Per-user notification with category, type, and 90-day TTL for automatic cleanup. References any related entity by stringified id.
- [[RequestTemplate]] — A seller-authored, sharable template that pre-fills a [[PurchaseRequest]]. Carries a public shareable link, usage counter, and an optional default proposal.
- [[Dispute]] — Buyer-raised complaint tied to a [[PurchaseRequest]]. Captures evidence uploads, a timeline of admin actions, deadlines, and a structured resolution.
- [[BlogPost]] — Editorial content: title, slug, rich content, media, SEO metadata, view/like counters, and a draft/published/archived workflow.
- [[Address]] — User shipping address book entry. Enforces a single primary address per user via a pre-save hook.
- [[Category]] — Hierarchical product/service taxonomy referenced by [[PurchaseRequest]] and [[RequestTemplate]]. Supports parent/child via `parentId` and bilingual `name` / `nameEn`.
- [[Review]] — Polymorphic 1-5 star review against either a seller or a [[RequestTemplate]] (`subjectType` discriminator). One review per reviewer per subject (compound unique index).
- [[PointTransaction]] — Ledger of point grants and spends per user. Sources include purchase, referral, bonus, admin grant, and redemption.
- [[LevelConfig]] — Static configuration of loyalty tiers (level number, point thresholds, benefits, icon, color). Driven by admins; consumed by the [[User]].points.level field.
- [[ShopSettings]] — One-to-one storefront branding for a seller: name, description, avatar, cover image, review toggles, and social links.
- [[TempVerification]] — Short-lived signup record that holds candidate user data and a verification code. Auto-purges via TTL when `emailVerificationCodeExpires` passes.
- [[TelegramLink]] — Permanent auditable association between a Telegram user ID and an Amanat [[User]]. Stores Telegram profile metadata, link source (`miniapp` / `bot` / `login_widget`), status (`active` / `blocked`), and last-seen timestamp. One per Telegram user (unique on both `userId` and `telegramUserId`).
- [[TelegramSession]] — Short-lived Telegram Mini App session token issued when `initData` is verified. Carries the `initDataFingerprint` for replay protection and auto-expires via a MongoDB TTL index on `expiresAt`.
- [[ConfigSetting]] — Runtime configuration persisted in MongoDB for operational knobs that need an admin surface rather than a deploy.
- [[DerivedDestination]] — Per-payment derived wallet destination records used to reduce address reuse and reconcile on-chain pay-ins.
- [[FundsLedgerEntry]] — Immutable accounting ledger rows for pay-in, hold, release, refund, fee, adjustment, and reversal events.
- [[TrezorAccount]] — Hardware-wallet/safekeeping account metadata for custody operations and staged signer hardening.
- [[ConfigSettingHistory]] — Immutable audit trail of numeric runtime-config changes. Currently used for per-chain confirmation threshold change events, keyed as `confirmation_threshold:<chainId>`. Added in commit `27fb15a`.

## Relationship Diagram

```mermaid
erDiagram
    USER ||--o{ PURCHASE_REQUEST : "creates as buyer"
    USER ||--o{ SELLER_OFFER : "submits as seller"
    USER ||--o{ ADDRESS : "owns"
    USER ||--o{ NOTIFICATION : "receives"
    USER ||--o{ POINT_TRANSACTION : "earns/spends"
    USER ||--o{ REQUEST_TEMPLATE : "authors as seller"
    USER ||--o| SHOP_SETTINGS : "configures"
    USER ||--o{ BLOG_POST : "publishes"
    USER ||--o{ REVIEW : "writes as reviewer"
    USER ||--o{ DISPUTE : "raises as buyer"
    USER ||--o{ USER : "referred by"
    USER ||--o{ TREZOR_ACCOUNT : "controls custody account"

    PURCHASE_REQUEST }o--|| CATEGORY : "belongs to"
    PURCHASE_REQUEST ||--o{ SELLER_OFFER : "receives"
    PURCHASE_REQUEST ||--o{ PAYMENT : "settled by"
    PURCHASE_REQUEST ||--o| CHAT : "discussed in"
    PURCHASE_REQUEST ||--o{ DISPUTE : "may trigger"
    PURCHASE_REQUEST ||--o| REVIEW : "rated by buyer"

    SELLER_OFFER ||--o| PAYMENT : "funds"
    SELLER_OFFER }o--|| PURCHASE_REQUEST : "responds to"

    PAYMENT }o--|| USER : "buyer"
    PAYMENT }o--|| USER : "seller"
    PAYMENT ||--o{ FUNDS_LEDGER_ENTRY : "accounted by"
    PAYMENT ||--o| DERIVED_DESTINATION : "collects into"

    CHAT }o--o{ USER : "participants"
    CHAT ||--o{ DISPUTE : "support channel"

    REQUEST_TEMPLATE }o--|| CATEGORY : "belongs to"
    REQUEST_TEMPLATE ||--o{ REVIEW : "rated as subject"

    CATEGORY ||--o{ CATEGORY : "parent of"

    POINT_TRANSACTION }o--|| USER : "owner"
    LEVEL_CONFIG ||..|| USER : "level lookup"

    TEMP_VERIFICATION ||..|| USER : "promoted to"

    TELEGRAM_LINK }o--|| USER : "links identity"
    TELEGRAM_SESSION }o--o| USER : "session for"
    TELEGRAM_SESSION }o--|| TELEGRAM_LINK : "matches"
```

## Conventions Across All Models

> [!note] Shared schema patterns
> - **Timestamps**: every model declares `{ timestamps: true }`, so `createdAt` and `updatedAt` are always present.
> - **ObjectId references**: foreign keys use `Schema.Types.ObjectId` with an explicit `ref` (e.g. `ref: 'User'`). The two exceptions are [[Notification]] and [[Payment]] which use string-typed or `Mixed` identifiers in places to support template-flow payments.
> - **Soft delete**: deletion is modelled as a `status` flag (e.g. `User.status = 'deleted'`, `BlogPost.status = 'archived'`) rather than physical removal.
> - **TTL indexes**: short-lived collections ([[Notification]], [[TempVerification]]) use `{ expireAfterSeconds: ... }` so MongoDB does the cleanup.
> - **toJSON sanitisation**: [[User]] overrides `toJSON` to strip credentials, refresh tokens, and verification codes before serialisation.

> [!warning] Index discipline
> Several schemas leave a comment noting that `unique: true` already creates an index — adding `schema.index({ field: 1 })` on top would produce a duplicate-index warning at startup. When introducing new indexes, search for `unique: true` first.

## Lifecycle View

The dominant happy-path flow exercises five collections in order:

1. A buyer (`User`) creates a `PurchaseRequest` with `status: 'pending'`.
2. Sellers (other `User`s) attach `SellerOffer` documents; the request transitions through `received_offers` → `in_negotiation` as the parties chat in a `Chat`.
3. The buyer accepts an offer; a `Payment` is opened against the Request Network provider and, once verified by webhook/reconciliation and safety checks, advances to a funded escrow state.
4. The seller marks the request `delivery` → `delivered`; the buyer confirms with the 6-digit `deliveryCode` and the request becomes `completed`.
5. The escrow `Payment` flips to `released` after a ledger-gated custody transfer instruction. Optionally the buyer writes a `Review` and earns a `PointTransaction`.

If anything goes sideways, the buyer can open a `Dispute`, which freezes release until an admin resolves it (refund, replacement, compensation, or no-action).

## How to Navigate

Each model has its own note in this folder. Cross-references use `[[wikilinks]]` so backlinks work in Obsidian's graph view. Schemas are documented at field-level granularity — every field is listed with its type, default, validation, and indexing decisions. Where a model carries a meaningful state machine, a Mermaid `stateDiagram-v2` accompanies the schema table.

> [!note] Source of truth
> The information below is mirrored from the TypeScript schema definitions. If a field listed here disagrees with the code, the code wins — please update the note. All source citations use the form `backend/src/models/<File>.ts:<line>`.
