Ctrl K
ComunidadGuías…

prisma/schema.prisma

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// =============================================================================
// TENANT & ORGANIZATION MODELS
// =============================================================================

model Tenant {
  id                    String   @id @default(cuid())
  name                  String
  slug                  String   @unique
  domain                String?  @unique
  subdomain             String?  @unique

  // Database isolation configuration
  database_host         String?
  database_port         Int?
  database_name         String?
  database_schema       String?

  // Storage configuration
  storage_bucket        String?
  storage_prefix        String?

  // Feature flags and limits
  features              Json     @default("{}")
  limits                Json     @default("{}")
  settings              Json     @default("{}")
  metadata              Json     @default("{}")

  // Billing and plan information
  billing_plan          String   @default("free")
  isolation_level       String   @default("shared") // shared, semi-isolated, fully-isolated

  status                String   @default("active") // active, suspended, deleted
  created_at            DateTime @default(now())
  updated_at            DateTime @updatedAt

  // Relations
  users                 User[]
  teams                 Team[]
  billing_subscriptions BillingSubscription[]
  audit_logs            AuditLog[]
  sessions              Session[]
  roles                 Role[]

  @@map("tenants")
  @@index([slug])
  @@index([domain])
  @@index([subdomain])
  @@index([status])
  @@index([billing_plan])
}

// =============================================================================
// USER MANAGEMENT MODELS
// =============================================================================

model User {
  id                   String   @id @default(cuid())
  tenant_id            String
  email                String
  email_verified       Boolean  @default(false)
  email_verified_at    DateTime?

  // Profile information
  first_name           String?
  last_name            String?
  display_name         String?
  avatar_url           String?
  phone                String?
  phone_verified       Boolean  @default(false)
  phone_verified_at    DateTime?

  // Authentication
  password_hash        String?
  password_salt        String?
  password_updated_at  DateTime?

  // Account status
  status               String   @default("active") // active, suspended, deleted
  locked_at            DateTime?
  locked_reason        String?
  login_attempts       Int      @default(0)
  last_login_at        DateTime?
  last_login_ip        String?

  // Multi-factor authentication
  mfa_enabled          Boolean  @default(false)
  mfa_secret           String?
  mfa_backup_codes     String[] @default([])
  mfa_recovery_codes   String[] @default([])

  // Preferences and metadata
  language             String   @default("en")
  timezone             String   @default("UTC")
  preferences          Json     @default("{}")
  metadata             Json     @default("{}")

  // Audit fields
  created_at           DateTime @default(now())
  updated_at           DateTime @updatedAt
  created_by           String?
  updated_by           String?

  // Relations
  tenant               Tenant   @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
  team_memberships     TeamMember[]
  role_assignments     RoleAssignment[]
  sessions             Session[]
  mfa_methods          MfaMethod[]
  passkeys            Passkey[]
  audit_logs_actor     AuditLog[] @relation("ActorAudits")
  audit_logs_created   AuditLog[] @relation("CreatedByAudits")
  payment_customers    PaymentCustomer[]
  billing_alerts       BillingAlert[]

  @@map("users")
  @@unique([tenant_id, email])
  @@index([email])
  @@index([status])
  @@index([tenant_id])
  @@index([last_login_at])
  @@index([created_at])
}

model MfaMethod {
  id                   String   @id @default(cuid())
  user_id              String
  type                 String   // totp, sms, email, webauthn, hardware, backup_codes
  enabled              Boolean  @default(false)
  verified             Boolean  @default(false)
  primary              Boolean  @default(false)

  // Type-specific configuration
  totp_secret          String?
  phone_number         String?
  email_address        String?
  hardware_token_id    String?

  // Security metadata
  last_used_at         DateTime?
  verification_count   Int      @default(0)
  created_at           DateTime @default(now())
  updated_at           DateTime @updatedAt

  // Relations
  user                 User     @relation(fields: [user_id], references: [id], onDelete: Cascade)

  @@map("mfa_methods")
  @@index([user_id])
  @@index([type])
  @@index([enabled])
}

model Passkey {
  id                     String   @id @default(cuid())
  user_id                String
  credential_id          String   @unique
  credential_public_key  String
  counter                BigInt   @default(0)
  name                   String?
  transports             String[] @default([])

  // Device information
  device_type            String   // platform, cross-platform, unknown
  backed_up              Boolean  @default(false)

  // Usage tracking
  created_at             DateTime @default(now())
  last_used_at           DateTime?

  // Relations
  user                   User     @relation(fields: [user_id], references: [id], onDelete: Cascade)

  @@map("passkeys")
  @@index([user_id])
  @@index([device_type])
  @@index([last_used_at])
}

// =============================================================================
// TEAM MANAGEMENT MODELS
// =============================================================================

model Team {
  id               String   @id @default(cuid())
  tenant_id        String
  name             String
  slug             String
  description      String?
  avatar_url       String?
  parent_team_id   String?

  // Configuration
  settings         Json     @default("{}")
  metadata         Json     @default("{}")
  is_default       Boolean  @default(false)

  // Audit fields
  created_at       DateTime @default(now())
  updated_at       DateTime @updatedAt
  created_by       String

  // Relations
  tenant           Tenant      @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
  parent_team      Team?       @relation("TeamHierarchy", fields: [parent_team_id], references: [id])
  child_teams      Team[]      @relation("TeamHierarchy")
  members          TeamMember[]
  invitations      TeamInvitation[]
  resources        TeamResource[]
  projects         TeamProject[]

  @@map("teams")
  @@unique([tenant_id, slug])
  @@index([tenant_id])
  @@index([parent_team_id])
  @@index([created_at])
}

model TeamMember {
  id           String   @id @default(cuid())
  team_id      String
  user_id      String
  role         String   // owner, lead, member, viewer, guest
  permissions  String[] @default([])
  is_lead      Boolean  @default(false)

  // Invitation tracking
  invited_by   String?
  joined_at    DateTime @default(now())

  // Metadata
  metadata     Json     @default("{}")

  // Relations
  team         Team     @relation(fields: [team_id], references: [id], onDelete: Cascade)
  user         User     @relation(fields: [user_id], references: [id], onDelete: Cascade)

  @@map("team_members")
  @@unique([team_id, user_id])
  @@index([team_id])
  @@index([user_id])
  @@index([role])
}

model TeamInvitation {
  id             String   @id @default(cuid())
  team_id        String
  email          String
  role           String
  permissions    String[] @default([])
  token          String   @unique

  // Invitation details
  invited_by     String
  status         String   @default("pending") // pending, accepted, expired, cancelled
  message        String?

  // Timestamps
  created_at     DateTime @default(now())
  expires_at     DateTime
  accepted_at    DateTime?

  // Relations
  team           Team     @relation(fields: [team_id], references: [id], onDelete: Cascade)

  @@map("team_invitations")
  @@index([team_id])
  @@index([email])
  @@index([status])
  @@index([expires_at])
}

model TeamProject {
  id           String   @id @default(cuid())
  team_id      String
  project_id   String
  access_level String   // read, write, admin

  // Assignment details
  assigned_at  DateTime @default(now())
  assigned_by  String

  // Metadata
  metadata     Json     @default("{}")

  // Relations
  team         Team     @relation(fields: [team_id], references: [id], onDelete: Cascade)

  @@map("team_projects")
  @@unique([team_id, project_id])
  @@index([team_id])
  @@index([project_id])
}

model TeamResource {
  id            String   @id @default(cuid())
  team_id       String
  resource_type String
  resource_id   String
  permissions   String[] @default([])
  constraints   Json     @default("{}")

  // Assignment details
  assigned_at   DateTime @default(now())
  assigned_by   String

  // Relations
  team          Team     @relation(fields: [team_id], references: [id], onDelete: Cascade)

  @@map("team_resources")
  @@unique([team_id, resource_type, resource_id])
  @@index([team_id])
  @@index([resource_type])
}

// =============================================================================
// RBAC MODELS
// =============================================================================

model Role {
  id               String   @id @default(cuid())
  tenant_id        String
  name             String
  description      String?
  permissions      Json     @default("[]") // Array of Permission objects
  parent_role_id   String?

  // Role configuration
  is_system        Boolean  @default(false)
  is_default       Boolean  @default(false)
  priority         Int      @default(100)
  constraints      Json     @default("{}")

  // Metadata
  metadata         Json     @default("{}")

  // Audit fields
  created_at       DateTime @default(now())
  updated_at       DateTime @updatedAt
  created_by       String

  // Relations
  tenant           Tenant   @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
  parent_role      Role?    @relation("RoleHierarchy", fields: [parent_role_id], references: [id])
  child_roles      Role[]   @relation("RoleHierarchy")
  assignments      RoleAssignment[]

  @@map("roles")
  @@unique([tenant_id, name])
  @@index([tenant_id])
  @@index([is_system])
  @@index([is_default])
  @@index([priority])
}

model RoleAssignment {
  id               String   @id @default(cuid())
  role_id          String
  principal_type   String   // user, team, service
  principal_id     String
  tenant_id        String

  // Scope configuration
  scope_type       String?  // organization, team, project, resource
  scope_id         String?

  // Assignment conditions
  conditions       Json     @default("[]") // Array of condition objects
  expires_at       DateTime?

  // Assignment details
  assigned_by      String
  assigned_at      DateTime @default(now())

  // Relations
  role             Role     @relation(fields: [role_id], references: [id], onDelete: Cascade)
  user             User?    @relation(fields: [principal_id], references: [id], onDelete: Cascade)

  @@map("role_assignments")
  @@unique([role_id, principal_type, principal_id, scope_type, scope_id])
  @@index([role_id])
  @@index([principal_type, principal_id])
  @@index([tenant_id])
  @@index([expires_at])
}

model Policy {
  id            String   @id @default(cuid())
  tenant_id     String
  name          String
  document      Json     // PolicyDocument with statements

  // Policy metadata
  version       String   @default("1.0")
  description   String?

  // Audit fields
  created_at    DateTime @default(now())
  updated_at    DateTime @updatedAt
  created_by    String

  @@map("policies")
  @@unique([tenant_id, name])
  @@index([tenant_id])
}

model PolicyAttachment {
  id              String   @id @default(cuid())
  policy_id       String
  target_type     String   // role, user, team
  target_id       String

  // Attachment details
  attached_by     String
  attached_at     DateTime @default(now())

  // Relations
  policy          Policy   @relation(fields: [policy_id], references: [id], onDelete: Cascade)

  @@map("policy_attachments")
  @@unique([policy_id, target_type, target_id])
  @@index([policy_id])
  @@index([target_type, target_id])
}

// =============================================================================
// SESSION MANAGEMENT MODELS
// =============================================================================

model Session {
  id                        String   @id @default(cuid())
  user_id                   String
  tenant_id                 String?
  device_id                 String
  device_fingerprint        String

  // Network information
  ip_address                String
  user_agent                String
  location_country          String?
  location_region           String?
  location_city             String?
  location_latitude         Float?
  location_longitude        Float?

  // Session lifecycle
  created_at                DateTime @default(now())
  last_activity_at          DateTime @default(now())
  expires_at                DateTime

  // Token management
  refresh_token             String   @unique
  refresh_token_family      String
  refresh_token_expires_at  DateTime
  access_token_jti          String?

  // Session state
  is_active                 Boolean  @default(true)
  revoked                   Boolean  @default(false)
  revoked_at                DateTime?
  revoked_reason            String?

  // Security flags
  mfa_verified              Boolean  @default(false)
  suspicious_activity       Boolean  @default(false)
  high_risk                 Boolean  @default(false)
  requires_reauthentication Boolean  @default(false)

  // Metadata
  metadata                  Json     @default("{}")

  // Relations
  user                      User     @relation(fields: [user_id], references: [id], onDelete: Cascade)
  tenant                    Tenant?  @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
  anomaly_reports           SessionAnomalyReport[]

  @@map("sessions")
  @@index([user_id])
  @@index([tenant_id])
  @@index([device_fingerprint])
  @@index([is_active])
  @@index([expires_at])
  @@index([refresh_token_family])
  @@index([last_activity_at])
}

model SessionAnomalyReport {
  id                  String   @id @default(cuid())
  session_id          String
  anomalies           Json     // Array of SessionAnomaly objects
  risk_score          Float
  recommended_action  String   // allow, challenge, block, revoke
  detected_at         DateTime @default(now())

  // Relations
  session             Session  @relation(fields: [session_id], references: [id], onDelete: Cascade)

  @@map("session_anomaly_reports")
  @@index([session_id])
  @@index([risk_score])
  @@index([detected_at])
}

// =============================================================================
// PAYMENT & BILLING MODELS
// =============================================================================

model PaymentCustomer {
  id                  String   @id @default(cuid())
  user_id             String
  email               String
  name                String
  phone               String?

  // Address information
  address_line1       String?
  address_line2       String?
  address_city        String?
  address_state       String?
  address_postal_code String?
  address_country     String?

  // Tax information
  tax_type            String?  // rfc, vat, gst, ein, abn
  tax_id              String?
  tax_name            String?
  tax_address_line1   String?
  tax_address_line2   String?
  tax_address_city    String?
  tax_address_state   String?
  tax_address_postal_code String?
  tax_address_country String?
  tax_verified        Boolean  @default(false)

  // Provider-specific customer IDs
  stripe_customer_id  String?
  conekta_customer_id String?
  fungies_customer_id String?

  // Default payment method
  default_payment_method_id String?

  // Audit fields
  created_at          DateTime @default(now())
  updated_at          DateTime @updatedAt

  // Relations
  user                User     @relation(fields: [user_id], references: [id], onDelete: Cascade)
  payment_methods     PaymentMethod[]
  payment_intents     PaymentIntent[]
  checkout_sessions   CheckoutSession[]

  @@map("payment_customers")
  @@unique([user_id])
  @@index([email])
  @@index([stripe_customer_id])
  @@index([conekta_customer_id])
  @@index([fungies_customer_id])
}

model PaymentMethod {
  id                         String   @id @default(cuid())
  customer_id                String
  provider                   String   // stripe, conekta, fungies
  provider_payment_method_id String?
  type                       String   // card, oxxo, spei, bank_transfer, paypal, crypto, wire

  // Card details (if applicable)
  card_brand                 String?
  card_last4                 String?
  card_exp_month             Int?
  card_exp_year              Int?

  // Bank details (if applicable)
  bank_name                  String?
  bank_account_last4         String?

  // Other details
  wallet_address             String?
  reference                  String?

  // Status
  is_default                 Boolean  @default(false)

  // Metadata
  metadata                   Json     @default("{}")

  // Audit fields
  created_at                 DateTime @default(now())

  // Relations
  customer                   PaymentCustomer @relation(fields: [customer_id], references: [id], onDelete: Cascade)

  @@map("payment_methods")
  @@index([customer_id])
  @@index([provider])
  @@index([type])
  @@index([is_default])
}

model PaymentIntent {
  id                    String   @id @default(cuid())
  customer_id           String
  provider              String   // stripe, conekta, fungies
  provider_intent_id    String?

  // Payment details
  amount                Int      // Amount in smallest currency unit (cents)
  currency              String   // ISO 4217 currency code
  description           String?

  // Status
  status                String   @default("pending") // pending, processing, succeeded, failed, canceled, refunded

  // Payment method
  payment_method_id     String?

  // Error information
  error_code            String?
  error_message         String?
  error_provider_details Json?

  // Metadata
  metadata              Json     @default("{}")

  // Audit fields
  created_at            DateTime @default(now())
  updated_at            DateTime @updatedAt

  // Relations
  customer              PaymentCustomer @relation(fields: [customer_id], references: [id], onDelete: Cascade)

  @@map("payment_intents")
  @@index([customer_id])
  @@index([provider])
  @@index([status])
  @@index([created_at])
}

model CheckoutSession {
  id                  String   @id @default(cuid())
  customer_id         String
  provider            String   // stripe, conekta, fungies
  provider_session_id String?

  // Session details
  amount              Int      // Total amount in smallest currency unit
  currency            String
  success_url         String
  cancel_url          String

  // Line items
  line_items          Json     // Array of LineItem objects

  // Status
  status              String   @default("open") // open, complete, expired
  payment_intent_id   String?

  // Expiration
  expires_at          DateTime

  // Metadata
  metadata            Json     @default("{}")

  // Audit fields
  created_at          DateTime @default(now())

  // Relations
  customer            PaymentCustomer @relation(fields: [customer_id], references: [id], onDelete: Cascade)

  @@map("checkout_sessions")
  @@index([customer_id])
  @@index([provider])
  @@index([status])
  @@index([expires_at])
}

// =============================================================================
// BILLING MODELS
// =============================================================================

model BillingPlan {
  id                  String   @id @default(cuid())
  name                String   @unique
  display_name        String
  description         String

  // Pricing
  price_amount        Int      // Amount in smallest currency unit
  price_currency      String   @default("USD")
  price_interval      String   // monthly, yearly, one-time
  price_interval_count Int     @default(1)

  // Features and limits
  features            Json     @default("{}")
  limits              Json     @default("{}")

  // Provider integration
  stripe_price_id     String?
  stripe_product_id   String?

  // Status
  is_active           Boolean  @default(true)
  is_default          Boolean  @default(false)

  // Metadata
  metadata            Json     @default("{}")

  // Audit fields
  created_at          DateTime @default(now())
  updated_at          DateTime @updatedAt

  // Relations
  subscriptions       BillingSubscription[]

  @@map("billing_plans")
  @@index([is_active])
  @@index([is_default])
}

model BillingSubscription {
  id                      String   @id @default(cuid())
  tenant_id               String
  plan_id                 String

  // Subscription details
  status                  String   @default("active") // trialing, active, past_due, canceled, unpaid, incomplete
  quantity                Int      @default(1)

  // Period management
  current_period_start    DateTime
  current_period_end      DateTime
  trial_start             DateTime?
  trial_end               DateTime?
  cancel_at               DateTime?
  canceled_at             DateTime?

  // Provider integration
  stripe_subscription_id  String?
  stripe_customer_id      String?

  // Payment method
  payment_method_id       String?

  // Discount
  discount_coupon_id      String?
  discount_percent_off    Int?
  discount_amount_off     Int?
  discount_valid_until    DateTime?

  // Metadata
  metadata                Json     @default("{}")

  // Audit fields
  created_at              DateTime @default(now())
  updated_at              DateTime @updatedAt

  // Relations
  tenant                  Tenant   @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
  plan                    BillingPlan @relation(fields: [plan_id], references: [id])
  invoices                BillingInvoice[]

  @@map("billing_subscriptions")
  @@unique([tenant_id])
  @@index([plan_id])
  @@index([status])
  @@index([current_period_end])
  @@index([trial_end])
}

model BillingInvoice {
  id                  String   @id @default(cuid())
  subscription_id     String
  tenant_id           String
  invoice_number      String   @unique

  // Invoice details
  status              String   @default("draft") // draft, open, paid, void, uncollectible
  amount_due          Int      // Amount in smallest currency unit
  amount_paid         Int      @default(0)
  currency            String   @default("USD")

  // Period
  period_start        DateTime
  period_end          DateTime
  due_date            DateTime?
  paid_at             DateTime?

  // Provider integration
  stripe_invoice_id   String?

  // Line items
  line_items          Json     @default("[]") // Array of InvoiceLineItem objects

  // URLs
  pdf_url             String?
  hosted_invoice_url  String?

  // Audit fields
  created_at          DateTime @default(now())

  // Relations
  subscription        BillingSubscription @relation(fields: [subscription_id], references: [id], onDelete: Cascade)

  @@map("billing_invoices")
  @@index([subscription_id])
  @@index([tenant_id])
  @@index([status])
  @@index([due_date])
  @@index([period_end])
}

model BillingAlert {
  id                  String   @id @default(cuid())
  tenant_id           String
  user_id             String?
  type                String   // usage_limit, payment_failed, subscription_ending, trial_ending
  severity            String   // info, warning, critical

  // Alert details
  message             String
  details             Json     @default("{}")

  // Acknowledgment
  acknowledged        Boolean  @default(false)
  acknowledged_by     String?
  acknowledged_at     DateTime?

  // Audit fields
  created_at          DateTime @default(now())

  // Relations
  user                User?    @relation(fields: [user_id], references: [id], onDelete: Cascade)

  @@map("billing_alerts")
  @@index([tenant_id])
  @@index([type])
  @@index([severity])
  @@index([acknowledged])
  @@index([created_at])
}

model UsageRecord {
  id             String   @id @default(cuid())
  tenant_id      String
  metric         String
  quantity       Int
  unit           String   @default("unit")
  timestamp      DateTime @default(now())

  // Metadata
  metadata       Json     @default("{}")

  @@map("usage_records")
  @@index([tenant_id])
  @@index([metric])
  @@index([timestamp])
  @@index([tenant_id, metric, timestamp])
}

// =============================================================================
// AUDIT & MONITORING MODELS
// =============================================================================

model AuditLog {
  id                String   @id @default(cuid())
  tenant_id         String?

  // Actor information
  actor_id          String
  actor_type        String   // user, system, api_key, service_account
  actor_email       String?
  actor_ip_address  String?
  actor_user_agent  String?

  // Action details
  action            String

  // Resource information
  resource_type     String
  resource_id       String?
  resource_name     String?

  // Event details
  details           Json     @default("{}")
  outcome           String   // success, failure, error

  // Risk and compliance
  risk_score        Float?
  compliance_flags  String[] @default([])

  // Session context
  session_id        String?
  correlation_id    String?
  request_id        String?

  // Metadata
  metadata          Json     @default("{}")

  // Timestamp
  timestamp         DateTime @default(now())

  // Relations
  tenant            Tenant?  @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
  actor_user        User?    @relation("ActorAudits", fields: [actor_id], references: [id], onDelete: SetNull)
  created_by_user   User?    @relation("CreatedByAudits", fields: [actor_id], references: [id], onDelete: SetNull)

  @@map("audit_logs")
  @@index([tenant_id])
  @@index([actor_id])
  @@index([action])
  @@index([resource_type])
  @@index([outcome])
  @@index([timestamp])
  @@index([risk_score])
  @@index([compliance_flags])
  @@index([tenant_id, timestamp])
  @@index([actor_id, timestamp])
}

// =============================================================================
// SYSTEM & CONFIGURATION MODELS
// =============================================================================

model SystemConfiguration {
  id            String   @id @default(cuid())
  key           String   @unique
  value         Json
  description   String?
  category      String?
  is_secret     Boolean  @default(false)

  // Audit fields
  created_at    DateTime @default(now())
  updated_at    DateTime @updatedAt
  updated_by    String

  @@map("system_configurations")
  @@index([category])
  @@index([is_secret])
}

model ApiKey {
  id            String   @id @default(cuid())
  tenant_id     String?
  user_id       String?
  name          String
  key_hash      String   @unique
  key_prefix    String

  // Permissions and scope
  permissions   String[] @default([])
  scope         Json     @default("{}")

  // Status and limits
  is_active     Boolean  @default(true)
  rate_limit    Int?

  // Usage tracking
  last_used_at  DateTime?
  usage_count   Int      @default(0)

  // Expiration
  expires_at    DateTime?

  // Audit fields
  created_at    DateTime @default(now())
  created_by    String

  @@map("api_keys")
  @@index([tenant_id])
  @@index([user_id])
  @@index([is_active])
  @@index([expires_at])
  @@index([key_prefix])
}

model WebhookEndpoint {
  id            String   @id @default(cuid())
  tenant_id     String?
  url           String
  secret        String
  events        String[] @default([])

  // Status
  is_active     Boolean  @default(true)

  // Retry configuration
  max_retries   Int      @default(3)
  retry_delay   Int      @default(1000) // milliseconds

  // Audit fields
  created_at    DateTime @default(now())
  updated_at    DateTime @updatedAt
  created_by    String

  // Relations
  deliveries    WebhookDelivery[]

  @@map("webhook_endpoints")
  @@index([tenant_id])
  @@index([is_active])
}

model WebhookDelivery {
  id                String   @id @default(cuid())
  webhook_endpoint_id String
  event_type        String
  payload           Json

  // Delivery status
  status            String   @default("pending") // pending, success, failed, retrying
  http_status_code  Int?
  response_body     String?
  error_message     String?

  // Retry tracking
  attempts          Int      @default(0)
  next_retry_at     DateTime?

  // Timing
  created_at        DateTime @default(now())
  delivered_at      DateTime?

  // Relations
  webhook_endpoint  WebhookEndpoint @relation(fields: [webhook_endpoint_id], references: [id], onDelete: Cascade)

  @@map("webhook_deliveries")
  @@index([webhook_endpoint_id])
  @@index([status])
  @@index([event_type])
  @@index([next_retry_at])
  @@index([created_at])
}

// =============================================================================
// ADDITIONAL INDEXES FOR PERFORMANCE
// =============================================================================

// Composite indexes for common query patterns
// These are defined within the models above using @@index([...])