# Database Models Reference **Last Verified:** January 20, 2026 **Version:** 1.8.4 **Total Models:** 52+ --- ## Data Scoping Overview | Scope | Models | Base Class | Filter By | |-------|--------|------------|-----------| | **Global** | `IntegrationProvider`, `AIModelConfig`, `SystemAISettings`, `GlobalAIPrompt`, `GlobalAuthorProfile`, `GlobalStrategy`, `GlobalModuleSettings`, `Industry`, `IndustrySector`, `SeedKeyword` | `models.Model` | None (platform-wide) | | **Account** | `Account`, `User`, `Plan`, `Subscription`, `AccountSettings`, `ModuleEnableSettings`, `AISettings`, `AIPrompt`, `AuthorProfile`, `CreditBalance`, `PasswordResetToken` | `AccountBaseModel` | `account` | | **Site** | `Site`, `PublishingSettings`, `AutomationConfig`, `DefaultAutomationConfig`, `AutomationRun`, `SiteIntegration`, `SiteUserAccess` | `AccountBaseModel` | `account`, `site` | | **Site+Sector** | `Keywords`, `Clusters`, `ContentIdeas`, `Tasks`, `Content`, `Images`, `ContentTaxonomyRelation` | `SiteSectorBaseModel` | `site`, `sector` | | **Billing** | `CreditCostConfig`, `BillingConfiguration`, `CreditPackage`, `PaymentMethodConfig`, `WebhookEvent` | `models.Model` | varies | | **System** | `SystemSettings`, `UserSettings`, `EmailSettings`, `EmailTemplate`, `EmailLog` | `models.Model` | varies | | **Plugins** | `Plugin`, `PluginVersion`, `PluginInstallation`, `PluginDownload` | `models.Model` | varies | --- ## Model Count by Location | Location | Count | Models | |----------|-------|--------| | `auth/models.py` | 10 | Account, User, Plan, Subscription, Industry, IndustrySector, SeedKeyword, Site, SiteUserAccess, PasswordResetToken | | `modules/system/` | 10 | IntegrationProvider, SystemAISettings, SystemSettings, UserSettings, EmailSettings, EmailTemplate, EmailLog, GlobalAIPrompt, GlobalAuthorProfile, GlobalStrategy | | `business/automation/` | 3 | DefaultAutomationConfig, AutomationConfig, AutomationRun | | `business/billing/` | 6 | CreditCostConfig, BillingConfiguration, CreditPackage, PaymentMethodConfig, AIModelConfig, WebhookEvent | | `business/content/` | 1 | ContentTaxonomyRelation | | `plugins/` | 4 | Plugin, PluginVersion, PluginInstallation, PluginDownload | | `modules/planner/` | 3 | Keywords, Clusters, ContentIdeas | | `modules/writer/` | 3 | Tasks, Content, Images | --- ## System Models (v1.4.0+) (`igny8_core/modules/system/`) **Purpose:** Centralized AI configuration, provider API keys, and system-wide defaults. ### IntegrationProvider (NEW v1.4.0) Centralized storage for ALL external service API keys. Admin-only. ```python class IntegrationProvider(models.Model): """Per final-model-schemas.md - Centralized API key storage""" provider_id = CharField(max_length=50, primary_key=True) # openai, runware, stripe, etc. display_name = CharField(max_length=100) provider_type = CharField(max_length=20) # ai, payment, email, storage # Authentication api_key = CharField(max_length=500, blank=True) api_secret = CharField(max_length=500, blank=True) webhook_secret = CharField(max_length=500, blank=True) api_endpoint = URLField(blank=True) # Configuration config = JSONField(default=dict) is_active = BooleanField(default=True) is_sandbox = BooleanField(default=False) # Audit updated_by = ForeignKey(User, null=True) created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) ``` **Seeded Providers:** - `openai` - AI (text + DALL-E) - `runware` - AI (images) - `anthropic` - AI (future) - `stripe` - Payment - `paypal` - Payment - `resend` - Email **Helper Methods:** - `IntegrationProvider.get_provider(provider_id)` - Get active provider - `IntegrationProvider.get_api_key(provider_id)` - Get API key for provider - `IntegrationProvider.get_providers_by_type(type)` - List providers by type --- ### AIModelConfig (NEW v1.4.0) Single Source of Truth for all AI models with pricing and credit configuration. ```python class AIModelConfig(models.Model): """Per final-model-schemas.md - Model definitions + pricing""" model_name = CharField(max_length=100, unique=True) # gpt-4o-mini, dall-e-3, runware:97@1 model_type = CharField(max_length=20) # text, image provider = CharField(max_length=50) # Links to IntegrationProvider display_name = CharField(max_length=200) is_default = BooleanField(default=False) # One default per type is_active = BooleanField(default=True) # Text Model Pricing (per 1K tokens) cost_per_1k_input = DecimalField(max_digits=10, decimal_places=6, null=True) cost_per_1k_output = DecimalField(max_digits=10, decimal_places=6, null=True) tokens_per_credit = IntegerField(null=True) # e.g., 1000, 10000 # Image Model Pricing credits_per_image = IntegerField(null=True) # e.g., 1, 5, 15 quality_tier = CharField(max_length=20, null=True) # basic, quality, premium # Model Limits max_tokens = IntegerField(null=True) context_window = IntegerField(null=True) capabilities = JSONField(default=dict) # vision, function_calling, etc. created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) ``` **Credit Configuration Examples:** | Model | Type | tokens_per_credit | credits_per_image | quality_tier | |-------|------|-------------------|-------------------|--------------| | gpt-4o | text | 1000 | - | - | | gpt-4o-mini | text | 10000 | - | - | | runware:97@1 | image | - | 1 | basic | | dall-e-3 | image | - | 5 | quality | | google:4@2 | image | - | 15 | premium | **Image Model Reference (v1.5.0 Planned):** | Model | AIR ID | Tier | Supported Dimensions | |-------|--------|------|---------------------| | Hi Dream Full | `runware:97@1` | Basic | 1024×1024, 1280×768 | | Bria 3.2 | `bria:10@1` | Quality | 1024×1024, 1344×768 | | Nano Banana | `google:4@2` | Premium | 1024×1024, 1376×768 | **Helper Methods:** - `AIModelConfig.get_default_text_model()` - Get default text model - `AIModelConfig.get_default_image_model()` - Get default image model - `AIModelConfig.get_image_models_by_tier()` - List image models by quality tier --- ### SystemAISettings (NEW v1.4.0) System-wide AI defaults. Singleton (pk=1). ```python class SystemAISettings(models.Model): """Per final-model-schemas.md - Renamed from GlobalIntegrationSettings""" # AI Parameters temperature = FloatField(default=0.7) # 0.0-2.0 max_tokens = IntegerField(default=8192) # Image Generation Settings image_style = CharField(max_length=30, default='photorealistic') image_quality = CharField(max_length=20, default='standard') # standard, hd max_images_per_article = IntegerField(default=4) # 1-8 image_size = CharField(max_length=20, default='1024x1024') # Audit updated_by = ForeignKey(User, null=True) updated_at = DateTimeField(auto_now=True) ``` **Image Style Choices:** - `photorealistic` - Ultra realistic photography - `illustration` - Digital illustration - `3d_render` - Computer generated 3D - `minimal_flat` - Minimal / Flat Design - `artistic` - Artistic / Painterly - `cartoon` - Cartoon / Stylized **Image Size Choices:** - `1024x1024` - Square - `1792x1024` - Landscape - `1024x1792` - Portrait **Helper Methods:** - `SystemAISettings.get_instance()` - Get singleton instance - `SystemAISettings.get_effective_temperature(account)` - Get with account override - `SystemAISettings.get_effective_image_style(account)` - Get with account override --- ### AccountSettings (Per-Account Overrides) Generic key-value store for account-specific settings. ```python class AccountSettings(AccountBaseModel): """Per final-model-schemas.md - Account overrides""" key = CharField(max_length=100) # Setting key value = JSONField(default=dict) # Setting value created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) ``` **AI-Related Keys** (override SystemAISettings defaults): | Key | Type | Example | Notes | |-----|------|---------|-------| | `ai.temperature` | float | 0.8 | Override system default | | `ai.max_tokens` | int | 8192 | Override system default | | `ai.image_style` | string | "illustration" | Override system default | | `ai.image_quality` | string | "hd" | Override system default | | `ai.max_images` | int | 6 | Override system default | | `ai.image_quality_tier` | string | "premium" | User's preferred tier | --- ### CreditCostConfig (Operation-Level Pricing) Fixed credit costs per operation type. ```python class CreditCostConfig(models.Model): """Per final-model-schemas.md - Operation pricing""" operation_type = CharField(max_length=50, primary_key=True) # Unique operation ID display_name = CharField(max_length=100) base_credits = IntegerField(default=1) # Fixed credits per operation is_active = BooleanField(default=True) description = TextField(blank=True) ``` **Note:** `tokens_per_credit` moved to AIModelConfig as of v1.4.0. --- ## DEPRECATED Models ### GlobalIntegrationSettings (DEPRECATED in v1.4.0) **Replaced by:** `IntegrationProvider` (API keys) + `AIModelConfig` (model configs) + `SystemAISettings` (defaults) ```python # DEPRECATED - Do not use class GlobalIntegrationSettings(models.Model): # API keys now in IntegrationProvider openai_api_key = CharField(max_length=500) # → IntegrationProvider.get_api_key('openai') runware_api_key = CharField(max_length=500) # → IntegrationProvider.get_api_key('runware') # Models now in AIModelConfig openai_model = CharField(default='gpt-4o-mini') # → AIModelConfig.is_default # Settings now in SystemAISettings openai_temperature = FloatField(default=0.7) # → SystemAISettings.temperature openai_max_tokens = IntegerField(default=8192) # → SystemAISettings.max_tokens ``` --- ### GlobalAIPrompt ```python class GlobalAIPrompt(models.Model): prompt_type = CharField(max_length=100) # clustering, ideas, content_generation prompt_value = TextField() variables = JSONField(default=list) is_active = BooleanField(default=True) ``` ### GlobalAuthorProfile ```python class GlobalAuthorProfile(models.Model): name = CharField(max_length=255) tone = CharField(max_length=50) # professional, casual, technical language = CharField(max_length=10, default='en') is_active = BooleanField(default=True) ``` --- ## Auth Models (`igny8_core/auth/models/`) ### User ```python class User(AbstractBaseUser, PermissionsMixin): id = UUIDField(primary_key=True) email = EmailField(unique=True) first_name = CharField(max_length=150) last_name = CharField(max_length=150) account = ForeignKey(Account, related_name='users') role = ForeignKey(Group, null=True) is_active = BooleanField(default=True) is_staff = BooleanField(default=False) created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) ``` **Relations:** Account (many-to-one) --- ### Account ```python class Account(models.Model): id = UUIDField(primary_key=True) name = CharField(max_length=255) plan = ForeignKey(Plan, null=True) owner = ForeignKey(User, related_name='owned_accounts') is_active = BooleanField(default=True) created_at = DateTimeField(auto_now_add=True) ``` **Relations:** Plan (many-to-one), Users (one-to-many), Sites (one-to-many) --- ### Site ```python class Site(models.Model): id = UUIDField(primary_key=True) name = CharField(max_length=255) domain = CharField(max_length=255, blank=True) account = ForeignKey(Account, related_name='sites') industry = ForeignKey(Industry, null=True) is_active = BooleanField(default=True) created_at = DateTimeField(auto_now_add=True) ``` **Relations:** Account (many-to-one), Sectors (one-to-many), Industries (many-to-one) --- ### Sector ```python class Sector(models.Model): id = UUIDField(primary_key=True) name = CharField(max_length=255) description = TextField(blank=True) site = ForeignKey(Site, related_name='sectors') is_active = BooleanField(default=True) created_at = DateTimeField(auto_now_add=True) ``` **Relations:** Site (many-to-one) --- ### Industry ```python class Industry(models.Model): id = UUIDField(primary_key=True) name = CharField(max_length=255) description = TextField(blank=True) ``` **Used for:** Default seed keywords, industry-specific prompts --- ## Planner Models (`igny8_core/modules/planner/models.py`) ### Keyword ```python class Keyword(models.Model): id = UUIDField(primary_key=True) keyword = CharField(max_length=255) site = ForeignKey(Site, related_name='keywords') sector = ForeignKey(Sector, null=True, related_name='keywords') cluster = ForeignKey(Cluster, null=True, related_name='keywords') search_volume = IntegerField(null=True) difficulty = IntegerField(null=True) cpc = DecimalField(null=True) status = CharField(choices=KEYWORD_STATUS) # new, mapped created_by = ForeignKey(User) created_at = DateTimeField(auto_now_add=True) ``` **Status Values:** - `new` - Ready for clustering - `mapped` - Assigned to a cluster --- ### Cluster ```python class Cluster(models.Model): id = UUIDField(primary_key=True) name = CharField(max_length=255) description = TextField(blank=True) site = ForeignKey(Site, related_name='clusters') sector = ForeignKey(Sector, null=True, related_name='clusters') created_by = ForeignKey(User) created_at = DateTimeField(auto_now_add=True) ``` **Relations:** Site, Sector, Keywords (one-to-many), ContentIdeas (one-to-many) --- ### ContentIdea ```python class ContentIdea(models.Model): id = UUIDField(primary_key=True) title = CharField(max_length=255) description = TextField(blank=True) site = ForeignKey(Site, related_name='ideas') sector = ForeignKey(Sector, null=True) cluster = ForeignKey(Cluster, related_name='ideas') primary_keyword = ForeignKey(Keyword, related_name='primary_ideas') secondary_keywords = ManyToManyField(Keyword, related_name='secondary_ideas') status = CharField(choices=IDEA_STATUS) # pending, approved, used, archived created_at = DateTimeField(auto_now_add=True) ``` --- ## Writer Models (`igny8_core/modules/writer/models.py`) ### Task ```python class Task(models.Model): id = UUIDField(primary_key=True) title = CharField(max_length=255) brief = TextField(blank=True) site = ForeignKey(Site, related_name='tasks') sector = ForeignKey(Sector, null=True) idea = ForeignKey(ContentIdea, null=True, related_name='tasks') primary_keyword = CharField(max_length=255) secondary_keywords = JSONField(default=list) status = CharField(choices=TASK_STATUS) # queued, completed assigned_to = ForeignKey(User, null=True) due_date = DateField(null=True) created_by = ForeignKey(User) created_at = DateTimeField(auto_now_add=True) ``` --- ### Content ```python class Content(models.Model): id = UUIDField(primary_key=True) title = CharField(max_length=255) body = TextField() # HTML content excerpt = TextField(blank=True) site = ForeignKey(Site, related_name='content') sector = ForeignKey(Sector, null=True) task = ForeignKey(Task, related_name='content') meta_title = CharField(max_length=255, blank=True) meta_description = TextField(blank=True) # Workflow status status = CharField(choices=CONTENT_STATUS) # draft, review, approved, published # Publishing status (v1.3.2) site_status = CharField(choices=SITE_STATUS) # not_published, scheduled, publishing, published, failed scheduled_publish_at = DateTimeField(null=True) site_status_updated_at = DateTimeField(null=True) # External site reference external_id = CharField(max_length=255, blank=True) # WordPress post ID external_url = URLField(blank=True) # Published URL word_count = IntegerField(default=0) created_by = ForeignKey(User) created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) ``` **Workflow Status Values (status):** - `draft` - Initial state after generation - `review` - Pending human review - `approved` - Ready for publishing (v1.3.2: NEW status) - `published` - Published to WordPress **Publishing Status Values (site_status) - v1.3.2:** - `not_published` - Not yet scheduled for external site - `scheduled` - Scheduled for future publishing - `publishing` - Currently being published - `published` - Successfully published to external site - `failed` - Publishing failed --- ### ContentImage ```python class ContentImage(models.Model): id = UUIDField(primary_key=True) content = ForeignKey(Content, related_name='images') url = URLField() thumbnail_url = URLField(blank=True) alt_text = CharField(max_length=255) caption = TextField(blank=True) is_featured = BooleanField(default=False) position = IntegerField(default=0) # AI generation metadata prompt = TextField(blank=True) provider = CharField(max_length=50) # dalle, runware created_at = DateTimeField(auto_now_add=True) ``` --- ## Integration Models (`igny8_core/business/integration/models.py`) ### SiteIntegration **⚠️ Note:** For WordPress, `Site.wp_api_key` is the **SINGLE source of truth** for API authentication. SiteIntegration is used for sync tracking and future multi-platform support. ```python class SiteIntegration(models.Model): id = UUIDField(primary_key=True) name = CharField(max_length=255) site = ForeignKey(Site, related_name='integrations') platform = CharField(max_length=50) # wordpress, shopify (future) # Configuration external_site_url = URLField() config_json = JSONField(default=dict) # Platform-specific settings credentials_json = JSONField(default=dict) # Reserved for future platforms (NOT for WordPress) # Sync Tracking sync_enabled = BooleanField(default=True) sync_status = CharField(max_length=50) # pending/syncing/completed/error last_sync_at = DateTimeField(null=True) sync_error = TextField(null=True) # Connection connection_status = CharField(max_length=50) # connected/error is_active = BooleanField(default=True) # Cached WordPress structure (from initial sync) categories = JSONField(default=list) tags = JSONField(default=list) authors = JSONField(default=list) post_types = JSONField(default=list) structure_updated_at = DateTimeField(null=True) created_at = DateTimeField(auto_now_add=True) ``` --- ### PublishingSettings (NEW v1.3.2) ```python class PublishingSettings(AccountBaseModel): """Site-level publishing configuration. Controls auto-approval, publishing limits, and scheduling.""" site = OneToOneField(Site, related_name='publishing_settings') # Auto-approval settings auto_approval_enabled = BooleanField(default=True) # Auto-publish settings auto_publish_enabled = BooleanField(default=True) # Publishing limits daily_publish_limit = PositiveIntegerField(default=3) weekly_publish_limit = PositiveIntegerField(default=15) monthly_publish_limit = PositiveIntegerField(default=50) # Publishing schedule publish_days = JSONField(default=['mon', 'tue', 'wed', 'thu', 'fri']) publish_time_slots = JSONField(default=['09:00', '14:00', '18:00']) created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) ``` **Default Values:** - `auto_approval_enabled`: True (auto-approve after review) - `auto_publish_enabled`: True (auto-publish approved content) - `daily_publish_limit`: 3 articles per day - `weekly_publish_limit`: 15 articles per week - `monthly_publish_limit`: 50 articles per month - `publish_days`: Monday through Friday - `publish_time_slots`: 9:00 AM, 2:00 PM, 6:00 PM **Usage:** ```python # Get or create with defaults settings, created = PublishingSettings.get_or_create_for_site(site) ``` --- ## Billing Models (`igny8_core/business/billing/models.py`) ### Plan ```python class Plan(models.Model): id = UUIDField(primary_key=True) name = CharField(max_length=100) slug = SlugField(unique=True) idea_credits = IntegerField(default=0) content_credits = IntegerField(default=0) image_credits = IntegerField(default=0) optimization_credits = IntegerField(default=0) max_sites = IntegerField(default=1) max_users = IntegerField(default=1) price_monthly = DecimalField(max_digits=10, decimal_places=2) price_yearly = DecimalField(max_digits=10, decimal_places=2) is_active = BooleanField(default=True) is_internal = BooleanField(default=False) ``` --- ### CreditBalance ```python class CreditBalance(models.Model): account = ForeignKey(Account, related_name='credit_balances') site = ForeignKey(Site, null=True, related_name='credit_balances') idea_credits = IntegerField(default=0) content_credits = IntegerField(default=0) image_credits = IntegerField(default=0) optimization_credits = IntegerField(default=0) period_start = DateField() period_end = DateField() created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) ``` --- ### CreditUsage ```python class CreditUsage(models.Model): account = ForeignKey(Account, related_name='credit_usage') site = ForeignKey(Site, null=True) user = ForeignKey(User) credit_type = CharField(max_length=50) # idea, content, image, optimization amount = IntegerField() operation = CharField(max_length=100) # generate_content, etc. related_content_type = ForeignKey(ContentType, null=True) related_object_id = UUIDField(null=True) created_at = DateTimeField(auto_now_add=True) ``` --- ## System Models (`igny8_core/modules/system/`) ### ModuleEnableSettings ```python class ModuleEnableSettings(models.Model): account = OneToOneField(Account, primary_key=True) planner_enabled = BooleanField(default=True) writer_enabled = BooleanField(default=True) linker_enabled = BooleanField(default=False) optimizer_enabled = BooleanField(default=False) automation_enabled = BooleanField(default=True) integration_enabled = BooleanField(default=True) publisher_enabled = BooleanField(default=True) ``` --- ### AIIntegrationSettings ```python class AIIntegrationSettings(models.Model): account = ForeignKey(Account, related_name='ai_settings') # OpenAI openai_api_key = CharField(max_length=255, blank=True) openai_model = CharField(max_length=50, default='gpt-4') # Image generation image_provider = CharField(max_length=50, default='dalle') # dalle, runware dalle_api_key = CharField(max_length=255, blank=True) runware_api_key = CharField(max_length=255, blank=True) is_validated = BooleanField(default=False) validated_at = DateTimeField(null=True) ``` --- ### PromptTemplate ```python class PromptTemplate(models.Model): account = ForeignKey(Account, null=True) # null = system default prompt_type = CharField(max_length=100) # auto_cluster, generate_ideas, etc. template = TextField() variables = JSONField(default=list) is_active = BooleanField(default=True) created_at = DateTimeField(auto_now_add=True) updated_at = DateTimeField(auto_now=True) ``` --- ## Publisher Models (`igny8_core/modules/publisher/models.py`) ### PublishingRecord ```python class PublishingRecord(models.Model): id = UUIDField(primary_key=True) content = ForeignKey(Content, related_name='publishing_records') integration = ForeignKey(SiteIntegration, related_name='publishing_records') external_id = CharField(max_length=255) # WordPress post ID external_url = URLField(blank=True) status = CharField(max_length=50) # pending, published, failed published_at = DateTimeField(null=True) created_at = DateTimeField(auto_now_add=True) ``` --- ## Automation Models (`igny8_core/modules/automation/models.py`) ### AutomationConfig ```python class AutomationConfig(models.Model): site = ForeignKey(Site, related_name='automation_configs') # Stage limits clustering_limit = IntegerField(default=10) ideas_limit = IntegerField(default=10) content_limit = IntegerField(default=5) image_limit = IntegerField(default=10) publish_limit = IntegerField(default=5) # Timing delay_between_operations = IntegerField(default=5) # seconds max_runtime = IntegerField(default=3600) # 1 hour # Behavior auto_approve = BooleanField(default=False) auto_publish = BooleanField(default=False) stop_on_error = BooleanField(default=True) is_active = BooleanField(default=True) ``` --- ### AutomationRun ```python class AutomationRun(models.Model): id = UUIDField(primary_key=True) site = ForeignKey(Site, related_name='automation_runs') config = ForeignKey(AutomationConfig) status = CharField(max_length=50) # pending, running, paused, completed, failed, cancelled # Progress tracking current_stage = CharField(max_length=50, blank=True) items_processed = IntegerField(default=0) items_total = IntegerField(default=0) # Timing started_at = DateTimeField(null=True) completed_at = DateTimeField(null=True) # Results error_message = TextField(blank=True) started_by = ForeignKey(User) created_at = DateTimeField(auto_now_add=True) ``` --- ## Entity Relationship Overview ``` Account ├── Users (many) ├── Sites (many) │ ├── Sectors (many) │ ├── Keywords (many) │ ├── Clusters (many) │ ├── ContentIdeas (many) │ ├── Tasks (many) │ ├── Content (many) │ │ └── ContentImages (many) │ ├── SiteIntegrations (many) │ │ └── PublishingRecords (many) │ └── AutomationConfigs (many) │ └── AutomationRuns (many) ├── Plan (one) ├── CreditBalances (many) ├── CreditUsage (many) ├── ModuleEnableSettings (one) ├── AIIntegrationSettings (many) └── PromptTemplates (many) ```