Data Dictionary
Catalog of Mongoose models and schemas in src/data/. Updated when models or schema fields change. Admin panel groups these into four sidebar sections (Users & Identity, Commerce, Configuration, Monitoring). Theme and display-name settings are stored in UserSettings / UserTheme models. System
themes define overrides (sidebar, container, sectionBg, text, colors) for universal layout and are
seeded/updated on deploy via seedSystemThemes. The User Settings page
(user-settings-page) and LoggedIn override use explicit theme-aware color
tokens (grey100/grey80) plus a global CSS override (ThemeAwareGlobals) to
ensure readable text on all theme backgrounds.
Trigger: Changes to src/data/** or *.model.ts
Regenerate: Use generate-reference-docs skill or run pre-commit with affected model files staged.
Note: src/data/discord-commands/ is a service layer (command router, notify service) with no database model. The router exposes execute, executeComponent, and executeModal. See Discord Commands.
Address
Model: Address | File: src/data/address/address.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| name | String | Yes | Recipient name |
| line1 | String | Yes | Street address |
| line2 | String | No | Apt/suite |
| city | String | Yes | City |
| state | String | Yes | State/province |
| postalCode | String | Yes | ZIP/postal code |
| country | String | Yes | Country code |
| phone | String | No | Phone number |
Alert
Model: Alert | File: src/data/alert/alert.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| type | enum | Yes | LOW_INVENTORY, NEW_ORDER, PAYMENT_FAILED, FULFILLMENT_FAILED |
| severity | enum | Yes | INFO, WARN, ERROR |
| message | String | Yes | Alert message |
| entityType | String | No | Related entity type |
| entityId | String | No | Related entity ID |
| isRead | Boolean | Yes | Read status |
AuditLog
Model: AuditLog | File: src/data/audit-log/audit-log.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| actorId | String | Yes | User who performed action |
| action | String | Yes | Action name |
| entityType | String | Yes | Entity type affected |
| entityId | String | Yes | Entity ID affected |
| meta | Mixed | Yes | Additional metadata |
BotConfig
Model: BotConfig | File: src/data/bot-config/bot-config.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| guildId | String | Yes | Discord guild ID |
| defaultOrderChannelId | String | No | Default order channel |
| announcementChannelId | String | No | Announcement channel |
| adminRoleIds | [String] | Yes | Admin role IDs |
| staffRoleIds | [String] | Yes | Staff role IDs |
| responseMode | enum | Yes | EPHEMERAL, DM |
| lowInventoryThreshold | Number | Yes | Low stock threshold |
| enabledPaymentMethods | [String] | Yes | Payment methods |
| paymentInstructions | String | Yes | Payment instructions text |
| paymentInstructionsByMethod | Mixed | Yes | Per-method instructions |
| registeredDiscordUserIds | [String] | Yes | Registered user IDs |
| defaultQtyVisibility | enum | Yes | public or private — tenant-wide default for product stock count display |
| defaultProductImageUrl | String | No | Fallback image URL when a product has none |
| shippingTypes | Mixed | Yes | Array of { code, label, costCents, estimatedDays } — default [] |
| Field | Type | Required | Description |
| --------------------------- | -------- | -------- | ----------------------------------------------------------------------------------- |
| tenantId | String | Yes | Tenant identifier |
| guildId | String | Yes | Discord guild ID |
| defaultOrderChannelId | String | No | Default order channel |
| announcementChannelId | String | No | Announcement / admin channel (takes precedence over adminChannelId) |
| adminChannelId | String | No | Fallback admin channel for notifications (used when announcementChannelId is blank) |
| adminRoleIds | [String] | Yes | Admin role IDs |
| staffRoleIds | [String] | Yes | Staff role IDs |
| responseMode | enum | Yes | EPHEMERAL, DM |
| lowInventoryThreshold | Number | Yes | Low stock threshold |
| enabledPaymentMethods | [String] | Yes | Payment methods |
| paymentInstructions | String | Yes | Payment instructions text |
| paymentInstructionsByMethod | Mixed | Yes | Per-method instructions |
| registeredDiscordUserIds | [String] | Yes | Registered user IDs |
| defaultQtyVisibility | enum | Yes | public or private — tenant-wide default for product stock count display |
| defaultProductImageUrl | String | No | Fallback image URL when a product has none |
| adminNotifyEmail | String | No | Optional email address for admin notifications (e.g. payment-rejected alerts). Not exposed in AdminJS — set via seed or direct DB update. Requires EMAIL_PROVIDER=sendgrid. |
Customer
Model: Customer | File: src/data/customer/customer.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| userId | String | No | Linked platform user |
| discordId | String | Yes | Discord user ID |
| discordUsername | String | Yes | Discord username |
| discordDisplayName | String | Yes | Display name |
| source | enum | Yes | DISCORD, EMAIL, MANUAL |
| String | No | Email address | |
| phone | String | No | Phone |
| addresses | [addressWithId] | Yes | Shipping addresses |
| defaultAddressId | String | No | Default address |
| notes | String | No | Notes |
| isActive | Boolean | Yes | Active flag |
| dmOptOut | Boolean | Yes | When true, customer DMs are skipped (default false) |
| dmPreferences | Object | Yes | Per-event DM toggles: orderReceived, paymentConfirmed, orderShipped, refunds (all default true) |
| emailPreferences | Object | Yes | Per-event email toggles: orderReceived, paymentConfirmed, orderShipped, refunds (all default true) |
Discount
Model: Discount | File: src/data/discount/discount.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| code | String | No | Discount code |
| type | enum | Yes | PERCENT, AMOUNT |
| percent | Number | No | Percent off |
| amountCents | Number | No | Fixed amount off |
| appliesToAll | Boolean | Yes | Applies to all products |
| productIds | [String] | Yes | Eligible product IDs |
| roleIds | [String] | Yes | Eligible role IDs |
| customerIds | [String] | Yes | Eligible customer IDs |
| isActive | Boolean | Yes | Active flag |
| startsAt | Date | No | Start time |
| endsAt | Date | No | End time |
| usageLimit | Number | No | Max uses |
| usageCount | Number | Yes | Current uses |
| consumedOrderIds | [String] | Yes | Orders that used discount |
DiscountAssignment
Model: DiscountAssignment | File: src/data/discount/discount-assignment.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| discountId | String | Yes | The discount this assignment is for |
| customerId | String | Yes | Customer who holds the assignment |
| rule | enum | Yes | one_time, next_n, forever |
| totalAllowed | Number | No | Max uses (null for forever) |
| usedCount | Number | Yes | How many times the customer has used it |
| expiresAt | Date | No | Assignment expiration (soft-revoke sets this) |
Entitlement
Model: Entitlement | File: src/data/entitlement/entitlement.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| userId | String | Yes | Discord user ID who received entitlement |
| orderId | String | Yes | Order that triggered the entitlement |
| productId | String | Yes | Product that carries the role grant |
| type | enum | Yes | DISCORD_ROLE, DIGITAL_ITEM |
| roleId | String | No | Discord role ID (type=DISCORD_ROLE) |
| status | enum | Yes | PENDING, GRANTED, REVOKED, FAILED |
| metadata | Mixed | Yes | Additional data |
| grantedAt | Date | No | When Discord grant succeeded |
| revokedAt | Date | No | When Discord role removal ran (refund) |
| failedReason | String | No | Error message when status is FAILED |
| Field | Type | Required | Description |
| ------------ | ------ | -------- | ----------------------------------- |
| tenantId | String | Yes | Tenant identifier |
| userId | String | Yes | User who received entitlement |
| orderId | String | Yes | Order that granted it |
| productId | String | Yes | Product granted |
| type | enum | Yes | DISCORD_ROLE, DIGITAL_ITEM |
| roleId | String | No | Discord role (if type=DISCORD_ROLE) |
| status | enum | Yes | PENDING, GRANTED, REVOKED, FAILED |
| metadata | Mixed | Yes | Additional data |
| grantedAt | Date | No | When granted |
| revokedAt | Date | No | When revoked |
| failedReason | String | No | Error message when status=FAILED |
FeatureFlag
Model: FeatureFlag | File: src/data/feature-flag/feature-flag.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| key | String | Yes | Flag key |
| scope | enum | Yes | system, tenant |
| tenantId | String | No | Tenant (if scope=tenant) |
| enabled | Boolean | Yes | Enabled flag |
| payload | Mixed | Yes | Flag payload |
Identity
Model: Identity | File: src/data/identity/identity.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | Platform user ID |
| tenantId | String | No | Tenant |
| provider | String | Yes | Auth provider (discord, local) |
| providerUserId | String | Yes | Provider user ID |
| profile | Mixed | Yes | Profile data |
| secrets | Mixed | No | Opaque secrets |
InventorySnapshot / InventoryTransaction
Models: InventorySnapshot, InventoryTransaction | File: src/data/inventory/inventory.model.ts
InventorySnapshot:
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| productId | String | Yes | Product ID |
| quantityAvailable | Number | Yes | Available quantity |
| quantityReserved | Number | Yes | Reserved quantity |
InventoryTransaction:
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| productId | String | Yes | Product ID |
| orderId | String | No | Related order |
| type | enum | Yes | ADJUSTMENT, RESERVE, RELEASE, SALE, RESTOCK, REFUND |
| quantityDelta | Number | Yes | Quantity change |
| reason | String | No | Reason text |
Order
Model: Order | File: src/data/order/order.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| orderNumber | String | Yes | Unique order number |
| status | enum | Yes | PENDING_PAYMENT, PAID, FULFILLING, SHIPPED, COMPLETE, CANCELLED, REFUNDED |
| items | [orderItem] | Yes | Line items |
| subtotalCents | Number | Yes | Subtotal |
| totalCents | Number | Yes | Total |
| discountId | String | No | Applied discount |
| discountCode | String | No | Discount code used |
| discountAmountCents | Number | Yes | Discount amount |
| taxAmountCents | Number | No | Sales tax collected (cents); defaults to 0 for pre-TaxJar orders |
| currency | String | Yes | Currency code |
| customer | orderCustomer | Yes | Customer snapshot |
| customerId | String | No | Customer record ID |
| customerRoleIds | [String] | Yes | Customer roles at order time |
| shippingAddress | address | No | Shipping address |
| shippingStatus | enum | Yes | PENDING, LABEL_CREATED, SHIPPED, DELIVERED, FAILED |
| shipmentId | String | No | Shipment record |
| trackingNumber | String | No | Tracking number |
| notes | String | No | Order notes |
OrderInvoiceLinkToken (virtual — not persisted)
Invoice link tokens are not stored in the database. They are HMAC-signed payloads
issued by OrderInvoiceLinkService.generateLink and verified by verifyLinkToken.
| Field | Type | Description |
|---|---|---|
| orderId | string | ID of the order this token authorises access to |
| tenantId | string | Tenant that owns the order (verified on redemption) |
| exp | number | Unix epoch seconds; token is rejected once now > exp |
Token format: base64url(JSON payload).base64url(HMAC-SHA256 signature)
OrderSession
Model: OrderSession | File: src/data/order-session/order-session.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| discordUserId | String | Yes | Discord user ID |
| guildId | String | No | Guild ID |
| state | enum | Yes | Order session state |
| items | [orderItem] | Yes | Cart items |
| shippingAddress | Mixed | No | Shipping address |
| paymentMethod | String | No | Selected payment method |
| submittedOrderId | String | No | Order ID after submit |
| expiresAt | Date | Yes | Session expiry |
Order flow orchestration: OrderFlowService (order-flow.service.ts) exposes getActiveSession for Discord component/modal handlers. See Order.
PaymentMethod Registry
No model (pure constant data) | File: src/data/payment-methods/payment-methods.registry.ts
Static registry mapping every PaymentMethod enum value to a PaymentMethodDescriptor. Not a database model — no Mongoose schema. Used by bot-config admin and Discord checkout to display human-readable labels.
PaymentMethodDescriptor fields
| Field | Type | Description |
|---|---|---|
| value | PaymentMethod | The enum value this descriptor covers |
| displayName | string | Human-readable label for UI elements (e.g. "Zelle", "Cash App") |
| kind | p2p | processor | manual | Broad category driving filtering and UI logic |
| icon | string | Emoji or symbol used next to the name in Discord messages |
| requiresProcessor | boolean | True when a processor adapter must be configured before accepting payments |
| supportsAutomaticConfirmation | boolean | True when a webhook can confirm payment without admin intervention |
| supportsRefund | boolean | True when refunds can be issued programmatically through the processor |
| description | string | One-line description for admin UI tooltips |
Helper functions (payment-methods.service.ts)
| Function | Returns | Description |
|---|---|---|
getPaymentMethodDescriptor(method) | PaymentMethodDescriptor | Lookup a single descriptor by enum value |
getEnabledDescriptors(enabled) | readonly PaymentMethodDescriptor[] | Map an array of enabled methods to their descriptors |
filterDescriptorsByKind(kind, enabled) | readonly PaymentMethodDescriptor[] | Filter enabled methods by kind (p2p / processor / manual) |
Payment
Model: Payment | File: src/data/payment/payment.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| orderId | String | Yes | Order ID |
| method | enum | Yes | Payment method |
| status | enum | Yes | Payment status (includes P2P flow: pending_payment, awaiting_admin_approval, confirmed, rejected) |
| amountCents | Number | Yes | Amount in cents |
| currency | String | Yes | Currency code |
| providerRef | String | No | Provider reference (for BTC_NOWPAYMENTS: the NOWPayments invoice ID used to match IPN callbacks) |
| referenceText | String | No | Reference text |
| confirmedBy | String | No | Legacy confirmer field |
| confirmedAt | Date | No | Legacy confirmation time |
| approverUserId | String | No | Admin who confirmed or rejected the P2P payment |
| approvedAt | Date | No | When the payment was confirmed |
| rejectedAt | Date | No | When the payment was rejected |
| rejectionReason | String | No | Admin-supplied rejection reason |
| customerReferenceNote | String | No | Venmo/CashApp note the admin observed when confirming |
| nowpaymentsHostedUrl | String | No | BTC_NOWPAYMENTS: hosted invoice URL shown to the customer |
| nowpaymentsDepositAddress | String | No | BTC_NOWPAYMENTS: on-chain deposit address |
| nowpaymentsDepositAmountCrypto | String | No | BTC_NOWPAYMENTS: expected deposit amount in crypto (decimal string, e.g. "0.00012345") |
| nowpaymentsCryptoCurrency | String | No | BTC_NOWPAYMENTS: crypto ticker (e.g. "btc") |
| nowpaymentsExpiresAt | Date | No | BTC_NOWPAYMENTS: invoice expiry timestamp |
Product
Model: Product | File: src/data/product/product.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| name | String | Yes | Product name |
| description | String | No | Description |
| sku | String | Yes | SKU |
| priceCents | Number | Yes | Price |
| currency | String | Yes | Currency |
| requiredRoleIds | [String] | Yes | Roles required to buy |
| grantedRoleIds | [String] | Yes | Roles granted on purchase |
| isDigital | Boolean | Yes | Digital product |
| isActive | Boolean | Yes | Active flag |
| inventoryTracked | Boolean | Yes | Track inventory |
| costCents | Number | No | Admin-only cost basis (never returned in customer-facing output) |
| qtyVisibility | enum | No | public, private, or inherit (default inherit — falls back to tenant defaultQtyVisibility) |
Provider
Model: Provider | File: src/data/provider/provider.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| type | String | Yes | Provider type (discord, local) |
| tenantId | String | No | Tenant |
| isActive | Boolean | Yes | Active flag |
| config | Mixed | Yes | Provider config |
Role / RoleAssignment
Models: Role, RoleAssignment | Files: src/data/role/role.model.ts, role.assignments.model.ts
Role:
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| name | String | Yes | Role name |
| permissionKeys | [String] | Yes | Permission keys |
RoleAssignment:
| Field | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | User ID |
| tenantId | String | Yes | Tenant ID |
| roleId | String | Yes | Role ID |
Shipment
Model: Shipment | File: src/data/shipment/shipment.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| orderId | String | Yes | Order ID (unique per tenant) |
| status | enum | Yes | pending, label_created, in_transit, delivered, exception |
| carrier | String | No | Carrier name (e.g. USPS, UPS) |
| trackingNumber | String | No | Tracking number |
| labelUrl | String | No | Label URL (partner adapter only) |
| shippingTypeCode | String | Yes | References BotConfig.shippingTypes[].code |
| shippingCostCents | Number | Yes | Cost in cents, copied from shipping type at order time |
| shippedAt | Date | No | Timestamp when marked as shipped |
| deliveredAt | Date | No | Timestamp when delivered |
| metadata | Mixed | Yes | Partner-specific blob; default {} |
Tenant
Model: Tenant | File: src/data/tenant/tenant.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| name | String | Yes | Tenant name |
| ownerUserId | String | Yes | Owner user ID |
| adminUserIds | [String] | Yes | Admin user IDs |
User
Model: User | File: src/data/user/user.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| username | String | Yes | Username |
| displayName | String | Yes | Display name |
| String | No | ||
| avatarUrl | String | No | Avatar URL |
| discordId | String | No | Discord ID |
| roles | [String] | Yes | Role names |
| isAdmin | Boolean | Yes | Admin flag |
| customerId | String | No | Linked customer |
| primaryCustomerId | String | No | Primary customer |
UserSettings
Model: UserSettings | File: src/data/user-settings/user-settings.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | User ID |
| displayName | String | No | Display name |
| activeThemeId | String | No | Active AdminJS theme ID |
| notifications | Mixed | Yes | Notification preferences |
UserTheme
Model: UserTheme | File: src/data/user-theme/user-theme.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| name | String | Yes | Theme name |
| slug | String | Yes | URL slug |
| ownerUserId | String | No | Owner (null for system) |
| isSystem | Boolean | Yes | System theme flag |
| baseThemeId | String | No | Base theme to extend |
| overrides | Mixed | Yes | Theme overrides |
WebhookEvent
Model: WebhookEvent | File: src/data/webhook-event/webhook-event.model.ts
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| provider | enum | Yes | PAYPAL, CASHAPP_PAY, COINBASE_COMMERCE, DISCORD |
| eventId | String | Yes | Provider event ID |
| receivedAt | Date | Yes | Receipt time |
| payload | Mixed | Yes | Webhook payload |
Analytics (virtual — no Mongoose model)
Analytics reports are computed aggregations over the Order collection. They are not persisted; results are cached in Valkey for 60 seconds.
RevenueOverviewReport
| Field | Type | Description |
|---|---|---|
period.grossRevenueCents | number | Total revenue in cents |
period.orderCount | number | Submitted/completed orders |
period.avgOrderValueCents | number | Gross / count |
delta.grossRevenuePct | number | null | % change vs prior equal-length period |
dailyBreakdown | array | { date, revenueCents, orderCount }[] |
SalesVelocityReport
| Field | Type | Description |
|---|---|---|
ordersPerDay | array | { date, orderCount }[] |
dowHeatmap | array | 7 × 24 matrix (DOW × hour) |
slowDayCallout | object | null | { dow, label, avgOrders, lowestAvgHour } |
ProductPerformanceReport
| Field | Type | Description |
|---|---|---|
products | array | { productId, name, revenueCents, unitsSold, orderCount, avgOrderValueCents, isTopRevenue, isTopVolume, isSlowMover }[] |
AnalyticsShareLinkPayload (token)
| Field | Type | Description |
|---|---|---|
reportType | string | One of the 10 ANALYTICS_REPORT_TYPE values |
tenantId | string | Tenant the report is scoped to |
period.from | string | ISO-8601 start date |
period.to | string | ISO-8601 end date |
exp | number | Unix expiry timestamp |
CommunityFundContribution
Model: CommunityFundContribution | File: src/data/community-fund/community-fund.model.ts
Represents one voluntary donation event. Written by the service on order confirmation (order-flow integration deferred).
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| customerId | String | Yes | Donating customer |
| orderId | String | No | Linked order (null until order-flow integration lands) |
| amountCents | Number | Yes | Contribution amount in cents (positive integer) |
| anonymous | Boolean | Yes | When true, celebration message omits donor name |
| createdAt | Date | Auto | Auto-stamped by Mongoose |
CommunityFundDisbursement
Model: CommunityFundDisbursement | File: src/data/community-fund/community-fund.model.ts
Represents one approved fund disbursement to a community member in need.
| Field | Type | Required | Description |
|---|---|---|---|
| tenantId | String | Yes | Tenant identifier |
| recipientCustomerId | String | Yes | Beneficiary customer ID |
| amountCents | Number | Yes | Disbursement amount in cents (positive) |
| reason | String | Yes | Human-readable reason (audit trail) |
| approvedBy | String | Yes | User ID of approver |
| createdAt | Date | Auto | Auto-stamped by Mongoose |
CommunityFundConfig
Model: CommunityFundConfig | File: src/data/community-fund/community-fund.model.ts
Per-tenant singleton configuration for the community fund. Created with defaults on first access.
| Field | Type | Required | Default |
|---|---|---|---|
| tenantId | String | Yes | — |
| enabled | Boolean | Yes | false |
| allowedAmountsCents | Number[] | Yes | [500, 1000, 2500, 5000, 10000] |
| customAmountAllowed | Boolean | Yes | true |
| customAmountMinCents | Number | Yes | 100 |
| customAmountMaxCents | Number | Yes | 100000 |
| celebrationChannelId | String | No | null |
| celebrationMessageTemplate | String | Yes | See spec §4 |
| anonymousMessageTemplate | String | Yes | See spec §4 |
| anonymityAllowed | Boolean | Yes | true |
| disbursementApprovalRoles | String[] | Yes | ["admin", "community_leader"] |
| disclaimerText | String | Yes | See spec §6 (informal pool, not tax-deductible) |
| createdAt / updatedAt | Date | Auto | Timestamps |