29 KiB
IGNY8 Phase 2: Socializer (02H)
Native Social Media Posting & Engagement Engine — Stage 8
Document Version: 1.0
Date: 2026-03-23
Phase: IGNY8 Phase 2 — Feature Expansion
Status: Build Ready
Source of Truth: Codebase at /data/app/igny8/
Audience: Claude Code, Backend Developers, Architects
1. CURRENT STATE
Social Media Today
There is no social media functionality in IGNY8. No OAuth connections to any social platform exist. No social post generation, scheduling, or analytics. The automation pipeline ends at Stage 7 (publish to WordPress).
What Exists
- 7-stage automation pipeline (01E) terminates at publish — no post-publish social distribution
SiteIntegrationmodel (integration app) — handles WordPress OAuth; pattern reusable for social OAuthContentmodel — stores the content that feeds social post generationImagesmodel (writer app) — stores generated images that can be resized for social platformsAutomationConfigmodel (automation app) — per-site automation settings; extendable for social toggles- Celery infrastructure with beat scheduler — ready for social scheduling tasks
What Does Not Exist
- No social app or models
- No OAuth connections for LinkedIn, Twitter/X, Facebook, Instagram, TikTok
- No AI social post generation or platform adaptation
- No scheduling/calendar system
- No engagement tracking
- No Stage 8 pipeline integration
2. WHAT TO BUILD
Overview
Build Stage 8 of the automation pipeline: a social media engine that connects to 5 platforms via OAuth, generates AI-adapted posts from published content, schedules posts with best-time optimization, and tracks engagement metrics.
2.1 Platform Support
| Platform | Max Length | Key Format | OAuth Flow | API |
|---|---|---|---|---|
| 1,300 chars | Professional, industry insights | OAuth 2.0 (3-legged) | Marketing API v2 | |
| Twitter/X | 280 chars (thread option) | Concise, hashtags, hooks | OAuth 2.0 (PKCE) | API v2 |
| ~63,206 chars (500 optimal) | Conversational, visual | OAuth 2.0 (Facebook Login) | Graph API v18+ | |
| 2,200 chars, 30 hashtags | Visual-first, hashtag-heavy | Via Facebook Business | Graph API (IG) | |
| TikTok | varies | Gen-Z tone, trending hooks | OAuth 2.0 | Content Posting API |
2.2 OAuth Infrastructure
Reusable OAuth service structure:
Location: igny8_core/integration/oauth/
base_oauth.py— base class with token encrypt/decrypt, refresh logiclinkedin_oauth.py— LinkedIn 3-legged OAuth, scopes:w_member_social,r_organization_social,w_organization_socialtwitter_oauth.py— Twitter OAuth 2.0 with PKCEfacebook_oauth.py— Facebook Login, page selection, scopes:pages_manage_posts,pages_read_engagementinstagram_oauth.py— via Facebook Business, Instagram Basic Display APItiktok_oauth.py— TikTok OAuth 2.0
Token Security:
- Access tokens and refresh tokens encrypted using the same encryption pattern as GSC tokens (02C)
- Tokens stored in
SocialAccount.access_tokenandSocialAccount.refresh_token(encrypted TextField) - Auto-refresh via Celery task (
refresh_social_tokens) runs hourly
Platform-Specific Auth Notes:
- Facebook: Requires App Review for
pages_manage_postspermission. Flow: Login → Page Selection → Permission Grant → Storepage_access_token. Webhook subscription for engagement updates. - LinkedIn: Requires Marketing API Partner Program access. Flow: Authorization → Organization Selection → Store
access_token. - Instagram: No direct OAuth — connected via Facebook Business account. Requires Facebook Page linked to Instagram Professional account.
- Multi-account support: Multiple SocialAccount records per platform per site.
2.3 AI Content Adaptation
Input: Content record (title, content_html, meta_description, cluster keywords from SAGCluster)
Per platform, AI generates:
- Adapted text matching platform tone + length limits
- Hashtag set (topic-aware, platform-appropriate count)
- CTA appropriate to platform conventions
- Image sizing recommendations
Platform-Specific Generation:
- LinkedIn: Professional tone, industry framing, 3-5 hashtags, link in text
- Twitter/X: Hook + key point + CTA in 280 chars. Thread generation option (2-10 tweets) for long content
- Facebook: Conversational, 2-3 paragraphs, 3-5 hashtags, link at end
- Instagram: Visual-first caption, 20-30 hashtags in first comment or caption, no clickable link (use "link in bio" CTA)
- TikTok: Gen-Z/casual tone, trending hook format, 3-5 hashtags
2.4 Post Types
| # | Post Type | Description | Platforms |
|---|---|---|---|
| 1 | Announcement | Link post with title + excerpt + URL | All |
| 2 | Highlights | Key takeaways from article + CTA | All |
| 3 | Quote Card | Branded insight/statistic as image post | All |
| 4 | FAQ Snippet | Single FAQ from content as post | LinkedIn, Twitter, Facebook |
| 5 | Carousel | Multi-image posts (future) | Instagram, LinkedIn |
2.5 Image Sizing per Platform
| Platform | Dimension | Aspect Ratio |
|---|---|---|
| 1200×627px | ~1.91:1 | |
| 1600×900px | ~16:9 | |
| 1200×630px | ~1.91:1 | |
| Instagram (square) | 1080×1080px | 1:1 |
| Instagram (portrait) | 1080×1350px | 4:5 |
| TikTok | 1080×1920px | 9:16 |
Image resizing uses the Images model records generated by pipeline Stages 5-6, resized to platform specs using Pillow.
2.6 Scheduling & Calendar
Best-Time Algorithm:
- Configurable default best-time slots per platform
- Per-account override via
SocialAccount.settings.best_times[] - If engagement data available, learn optimal posting times from past performance
Queue System:
- Posts with
status='scheduled'andscheduled_atin the past → Celery task publishes - Celery task
publish_scheduled_postsruns every minute - Frequency caps: max posts per day per platform (configurable, default: 2)
- Cooldown: 4-6 hours between posts to same platform (configurable via
SocialAccount.settings.cooldown_hours)
Calendar View:
- API endpoint returns week/month layout of scheduled + published posts
- Frontend renders visual calendar with drag-drop rescheduling (
.tsxcomponent)
Bulk Scheduling:
- Select multiple content → generate social posts for all connected platforms
- Auto-space across optimal times over days/weeks
- Review before confirming schedule
2.7 Engagement Tracking
- Celery task
fetch_engagementruns every 6 hours - Fetches metrics from each platform API for recent posts (last 30 days)
- Metrics: likes, comments, shares/retweets, impressions, clicks, engagement_rate
- Aggregate dashboard: per-platform performance, best performing posts, optimal posting times
- Attribution: UTM parameters appended to all shared URLs (
utm_source={platform}&utm_medium=social&utm_campaign=igny8)
2.8 Stage 8 Pipeline Integration
After Stage 7 (publish to WordPress), if social accounts are connected for the site:
- Pipeline triggers Stage 8
- Load all connected
SocialAccountrecords for the site - For each connected account, AI generates platform-adapted post
- If
AutomationConfig.auto_social_publishenabled: posts queue immediately at next best-time slot - If manual review enabled: posts go to
status='draft'for user review - Published posts logged with
platform_post_idfor engagement tracking
AutomationConfig Extension:
# Add to AutomationConfig.settings JSONField:
{
"social_enabled": True, # Master toggle
"auto_social_publish": False, # Auto-queue or manual review
"social_platforms": ["linkedin", "twitter"], # Which platforms to auto-post
"social_post_types": ["announcement", "highlights"], # Which post types
}
3. DATA MODELS & APIS
3.1 New Models
All models in a new social app.
SocialAccount (social app)
class SocialAccount(SiteSectorBaseModel):
"""
Connected social media account for a site.
Stores OAuth tokens (encrypted) and per-account settings.
Multi-account support: multiple accounts per platform per site.
"""
platform = models.CharField(
max_length=15,
choices=[
('linkedin', 'LinkedIn'),
('twitter', 'Twitter/X'),
('facebook', 'Facebook'),
('instagram', 'Instagram'),
('tiktok', 'TikTok'),
]
)
platform_account_id = models.CharField(
max_length=255,
help_text='External platform account/page ID'
)
account_name = models.CharField(max_length=255, help_text='Display name')
account_type = models.CharField(
max_length=20,
choices=[
('personal', 'Personal'),
('page', 'Page'),
('organization', 'Organization'),
],
default='personal'
)
access_token = models.TextField(
help_text='Encrypted OAuth access token'
)
refresh_token = models.TextField(
blank=True,
default='',
help_text='Encrypted OAuth refresh token (nullable for platforms without refresh)'
)
token_expiry = models.DateTimeField(null=True, blank=True)
permissions = models.JSONField(
default=list,
help_text='Granted OAuth scopes'
)
status = models.CharField(
max_length=10,
choices=[
('active', 'Active'),
('expired', 'Expired'),
('revoked', 'Revoked'),
('error', 'Error'),
],
default='active'
)
settings = models.JSONField(
default=dict,
help_text='{auto_publish, max_per_day, best_times[], cooldown_hours}'
)
class Meta:
app_label = 'social'
db_table = 'igny8_social_accounts'
PK: BigAutoField (integer) — inherits from SiteSectorBaseModel
SocialPost (social app)
class SocialPost(SiteSectorBaseModel):
"""
A social media post — generated from content or standalone.
Tracks full lifecycle from draft to published with engagement.
"""
content = models.ForeignKey(
'writer.Content',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='social_posts',
help_text='Source content (null for standalone posts)'
)
social_account = models.ForeignKey(
'social.SocialAccount',
on_delete=models.CASCADE,
related_name='posts'
)
platform = models.CharField(
max_length=15,
help_text='Denormalized from social_account for filtering'
)
post_type = models.CharField(
max_length=15,
choices=[
('announcement', 'Announcement'),
('highlights', 'Highlights'),
('quote_card', 'Quote Card'),
('faq_snippet', 'FAQ Snippet'),
('carousel', 'Carousel'),
]
)
text = models.TextField()
hashtags = models.JSONField(default=list, help_text='List of hashtag strings')
media_urls = models.JSONField(default=list, help_text='List of image/video URLs')
link_url = models.URLField(blank=True, default='')
utm_params = models.JSONField(
default=dict,
blank=True,
help_text='{utm_source, utm_medium, utm_campaign}'
)
thread_posts = models.JSONField(
null=True,
blank=True,
help_text='For Twitter threads: [{text, media_url}]'
)
scheduled_at = models.DateTimeField(null=True, blank=True)
published_at = models.DateTimeField(null=True, blank=True)
platform_post_id = models.CharField(
max_length=255,
blank=True,
default='',
help_text='External post ID after publishing'
)
platform_post_url = models.URLField(blank=True, default='')
status = models.CharField(
max_length=15,
choices=[
('draft', 'Draft'),
('scheduled', 'Scheduled'),
('publishing', 'Publishing'),
('published', 'Published'),
('failed', 'Failed'),
('cancelled', 'Cancelled'),
],
default='draft'
)
error_message = models.TextField(blank=True, default='')
class Meta:
app_label = 'social'
db_table = 'igny8_social_posts'
PK: BigAutoField (integer) — inherits from SiteSectorBaseModel
SocialEngagement (social app)
class SocialEngagement(models.Model):
"""
Engagement metrics for a published social post.
Fetched periodically from platform APIs.
"""
social_post = models.ForeignKey(
'social.SocialPost',
on_delete=models.CASCADE,
related_name='engagement_records'
)
likes = models.IntegerField(default=0)
comments = models.IntegerField(default=0)
shares = models.IntegerField(default=0)
impressions = models.IntegerField(default=0)
clicks = models.IntegerField(default=0)
engagement_rate = models.FloatField(default=0.0)
raw_data = models.JSONField(default=dict, help_text='Full platform API response')
fetched_at = models.DateTimeField(auto_now_add=True)
class Meta:
app_label = 'social'
db_table = 'igny8_social_engagement'
PK: BigAutoField (integer) — standard Django Model (not AccountBaseModel since engagement is tied to post)
3.2 New App Registration
Create social app:
- App config:
igny8_core/modules/social/apps.pywithapp_label = 'social' - Add to INSTALLED_APPS in
igny8_core/settings.py
3.3 Migration
igny8_core/migrations/XXXX_add_social_models.py
Operations:
CreateModel('SocialAccount', ...)— with index on platform, statusCreateModel('SocialPost', ...)— with indexes on social_account, platform, status, scheduled_atCreateModel('SocialEngagement', ...)— with index on social_post
3.4 API Endpoints
All endpoints under /api/v1/social/:
Account Management
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/social/accounts/connect/{platform}/ |
Initiate OAuth flow for platform. Returns redirect URL. |
| GET | /api/v1/social/accounts/callback/{platform}/ |
OAuth callback handler. Exchanges code for tokens, creates SocialAccount. |
| GET | /api/v1/social/accounts/?site_id=X |
List connected accounts for site. |
| DELETE | /api/v1/social/accounts/{id}/ |
Disconnect account (revoke tokens, soft-delete). |
| PUT | /api/v1/social/accounts/{id}/settings/ |
Update account settings (max_per_day, best_times, cooldown). |
Post Management
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/social/posts/generate/ |
AI-generate posts for content across all connected platforms. Body: {content_id, post_types: []}. |
| POST | /api/v1/social/posts/ |
Create manual post. Body: {social_account_id, text, ...}. |
| GET | /api/v1/social/posts/?site_id=X |
List posts with filters (platform, status, date range). |
| PUT | /api/v1/social/posts/{id}/ |
Edit draft/scheduled post. |
| POST | /api/v1/social/posts/{id}/publish/ |
Publish immediately. |
| POST | /api/v1/social/posts/{id}/schedule/ |
Schedule for later. Body: {scheduled_at}. |
| DELETE | /api/v1/social/posts/{id}/ |
Cancel/delete post. |
| POST | /api/v1/social/posts/bulk-generate/ |
Generate for multiple content. Body: {content_ids: [int]}. |
| POST | /api/v1/social/posts/bulk-schedule/ |
Schedule multiple posts. Body: {post_ids: [int], auto_space: true}. |
Calendar & Analytics
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/social/calendar/?site_id=X&month=YYYY-MM |
Calendar view — scheduled + published posts for month. |
| GET | /api/v1/social/analytics/?site_id=X |
Aggregate analytics: per-platform performance, top posts. |
| GET | /api/v1/social/analytics/posts/?site_id=X |
Per-post analytics with engagement breakdown. |
Permissions: All endpoints use SiteSectorModelViewSet permission patterns.
3.5 AI Function — GenerateSocialPostsFunction
Registry key: generate_social_posts
Location: igny8_core/ai/functions/generate_social_posts.py
class GenerateSocialPostsFunction(BaseAIFunction):
"""
Generates platform-adapted social media posts from content.
One call produces posts for all requested platforms.
"""
function_name = 'generate_social_posts'
def validate(self, content_id, platforms=None, post_types=None, **kwargs):
# Verify content exists, has content_html
# Verify requested platforms have connected accounts
pass
def prepare(self, content_id, platforms=None, post_types=None, **kwargs):
# Load Content record
# Load cluster keywords for hashtag generation
# Load connected SocialAccount records for the site
# Determine applicable post_types per platform
pass
def build_prompt(self):
# Per platform, build adaptation instructions:
# - Platform tone + length limits
# - Content title, meta_description, key points from content_html
# - Hashtag generation instructions
# - CTA format
# - Thread instructions (Twitter if content > 280 chars summary)
pass
def parse_response(self, response):
# Parse per-platform posts:
# {platform: {text, hashtags[], cta, thread_posts?}}
pass
def save_output(self, parsed):
# Create SocialPost records per platform per post_type
# Attach UTM parameters
# Set status='draft' (default) or 'scheduled' if auto-publish
pass
3.6 OAuth Service
Location: igny8_core/integration/oauth/
# base_oauth.py
class BaseOAuthService:
"""Base class for OAuth integrations. Handles token encryption/decryption."""
def encrypt_token(self, token):
"""Encrypt token using Django's Fernet key (same pattern as 02C GSC tokens)."""
pass
def decrypt_token(self, encrypted_token):
"""Decrypt stored token."""
pass
def get_authorization_url(self, site_id, account_id=None):
"""Generate OAuth authorization URL with state parameter."""
pass
def handle_callback(self, code, state):
"""Exchange authorization code for tokens, create/update SocialAccount."""
pass
def refresh_tokens(self, social_account):
"""Refresh expired access token using refresh_token."""
pass
# linkedin_oauth.py
class LinkedInOAuthService(BaseOAuthService):
"""LinkedIn Marketing API OAuth 2.0 (3-legged)."""
SCOPES = ['w_member_social', 'r_organization_social', 'w_organization_social']
# ...
# twitter_oauth.py
class TwitterOAuthService(BaseOAuthService):
"""Twitter/X OAuth 2.0 with PKCE."""
SCOPES = ['tweet.read', 'tweet.write', 'users.read']
# ...
# facebook_oauth.py
class FacebookOAuthService(BaseOAuthService):
"""Facebook Login with page selection."""
SCOPES = ['pages_manage_posts', 'pages_read_engagement']
# ...
# tiktok_oauth.py
class TikTokOAuthService(BaseOAuthService):
"""TikTok Content Posting API OAuth."""
# ...
3.7 Social Publisher Service
Location: igny8_core/business/social_publisher.py
class SocialPublisherService:
"""
Handles actual posting to platform APIs.
Routes to platform-specific publishers.
"""
PUBLISHERS = {
'linkedin': LinkedInPublisher,
'twitter': TwitterPublisher,
'facebook': FacebookPublisher,
'instagram': InstagramPublisher,
'tiktok': TikTokPublisher,
}
def publish(self, social_post_id):
"""
Publish a SocialPost to its target platform.
1. Load SocialPost + SocialAccount
2. Decrypt access token
3. Route to platform-specific publisher
4. Update SocialPost with platform_post_id, platform_post_url, published_at
5. Set status='published' or 'failed'
"""
pass
3.8 Celery Tasks
Location: igny8_core/tasks/social_tasks.py
@shared_task(name='publish_scheduled_posts')
def publish_scheduled_posts():
"""
Runs every minute. Finds SocialPost records with
status='scheduled' and scheduled_at <= now(). Publishes each.
"""
pass
@shared_task(name='refresh_social_tokens')
def refresh_social_tokens():
"""
Runs hourly. Refreshes tokens expiring within next 2 hours.
Updates SocialAccount.access_token, token_expiry.
Sets status='expired' if refresh fails.
"""
pass
@shared_task(name='fetch_engagement')
def fetch_engagement():
"""
Runs every 6 hours. Fetches engagement metrics from platform APIs
for all published posts in last 30 days.
Creates SocialEngagement records.
"""
pass
@shared_task(name='generate_social_posts_stage8')
def generate_social_posts_stage8(content_id):
"""
Triggered by pipeline after Stage 7 (publish).
Generates social posts for all connected platforms for the site.
"""
pass
Beat Schedule Additions:
| Task | Schedule | Notes |
|---|---|---|
publish_scheduled_posts |
Every minute | Publishes due scheduled posts |
refresh_social_tokens |
Hourly | Refreshes expiring OAuth tokens |
fetch_engagement |
Every 6 hours | Fetches engagement metrics for recent posts |
3.9 Image Resizing Service
Location: igny8_core/business/social_image_resize.py
class SocialImageResizeService:
"""
Resizes images from the Images model to platform-specific dimensions.
Uses Pillow for image processing.
"""
PLATFORM_SIZES = {
'linkedin': (1200, 627),
'twitter': (1600, 900),
'facebook': (1200, 630),
'instagram_square': (1080, 1080),
'instagram_portrait': (1080, 1350),
'tiktok': (1080, 1920),
}
def resize(self, image_path, platform, variant='default'):
"""Resize image, return new file path."""
pass
4. IMPLEMENTATION STEPS
Step 1: Create Social App
- Create
igny8_core/modules/social/directory with__init__.pyandapps.py - Add
socialtoINSTALLED_APPSin settings.py - Create models: SocialAccount, SocialPost, SocialEngagement
Step 2: Migration
- Create migration for 3 new models
- Run migration
Step 3: OAuth Infrastructure
- Create
igny8_core/integration/oauth/directory - Implement
BaseOAuthServicewith token encryption/decryption - Implement platform-specific OAuth handlers (LinkedIn, Twitter, Facebook, TikTok)
- Instagram OAuth routes through Facebook Business
Step 4: AI Function
- Implement
GenerateSocialPostsFunctioninigny8_core/ai/functions/generate_social_posts.py - Register
generate_social_postsinigny8_core/ai/registry.py
Step 5: Services
- Implement
SocialPublisherServiceinigny8_core/business/social_publisher.py - Implement platform-specific publishers (LinkedIn, Twitter, Facebook, Instagram, TikTok)
- Implement
SocialImageResizeServiceinigny8_core/business/social_image_resize.py
Step 6: Pipeline Integration
Add Stage 8 trigger after Stage 7 (publish):
# In pipeline after publish completes:
def post_publish(content_id):
site = content.site
# Check if social is enabled for this site
config = AutomationConfig.objects.get(site=site)
if config.settings.get('social_enabled'):
generate_social_posts_stage8.delay(content_id)
Step 7: API Endpoints
- Create
igny8_core/urls/social.pywith account, post, calendar, and analytics endpoints - Create views:
AccountConnectView,AccountCallbackView,AccountListView - Create
PostGenerateView,PostViewSet,CalendarView,AnalyticsView - Register URL patterns under
/api/v1/social/
Step 8: Celery Tasks
- Implement 4 tasks in
igny8_core/tasks/social_tasks.py - Add
publish_scheduled_posts,refresh_social_tokens,fetch_engagementto beat schedule
Step 9: Serializers & Admin
- Create DRF serializers for SocialAccount (exclude encrypted tokens from response), SocialPost, SocialEngagement
- Register models in Django admin
Step 10: Credit Cost Configuration
Add to CreditCostConfig (billing app):
| operation_type | default_cost | description |
|---|---|---|
social_adaptation |
1 | AI-generate post for one platform |
social_hashtag |
0.5 | Hashtag generation for one post |
social_thread |
2 | Twitter thread generation (2-10 tweets) |
social_carousel |
5 | Carousel post generation |
social_image_resize |
3-10 | Image resizing/generation per platform |
social_suite |
15-25 | Full suite for all 5 platforms |
5. ACCEPTANCE CRITERIA
OAuth Connections
- LinkedIn OAuth 2.0 connects and stores encrypted tokens
- Twitter/X OAuth 2.0 with PKCE connects successfully
- Facebook Login with page selection works, stores page_access_token
- Instagram connects via Facebook Business account
- TikTok OAuth connects and stores tokens
- Token refresh runs hourly, updates expired tokens
- Multi-account support: multiple accounts per platform per site
Post Generation
- AI generates platform-adapted text for each connected platform
- LinkedIn posts use professional tone, 1,300 char limit
- Twitter posts respect 280 char limit, thread option for long content
- Facebook posts use conversational tone, optimal 500 char length
- Instagram captions include 20-30 hashtags, "link in bio" CTA
- TikTok posts use casual/Gen-Z tone, trending hook format
- UTM parameters appended to all shared URLs
Post Types
- Announcement, Highlights, Quote Card, FAQ Snippet post types generated
- Post type selection respects platform compatibility
Scheduling
- Posts can be scheduled for future time
publish_scheduled_poststask runs every minute, publishes due posts- Frequency caps enforced (max per day per platform)
- Cooldown period enforced between posts to same platform
- Calendar endpoint returns month view of scheduled/published posts
- Bulk scheduling auto-spaces posts across optimal times
Engagement
- Engagement metrics fetched every 6 hours for recent posts
- SocialEngagement records created with likes, comments, shares, impressions, clicks
- Aggregate analytics endpoint returns per-platform performance + top posts
- Per-post analytics available with engagement breakdown
Pipeline Integration
- Stage 8 triggers automatically after Stage 7 (publish)
- Social posts generated for all connected platforms
- auto_social_publish toggle controls immediate vs manual review
- Platform selection configurable via AutomationConfig.settings
Images
- Images from pipeline resized to platform-specific dimensions
- media_urls populated on SocialPost records
6. CLAUDE CODE INSTRUCTIONS
File Locations
igny8_core/
├── modules/
│ └── social/
│ ├── __init__.py
│ ├── apps.py # app_label = 'social'
│ └── models.py # SocialAccount, SocialPost, SocialEngagement
├── ai/
│ └── functions/
│ └── generate_social_posts.py # GenerateSocialPostsFunction
├── integration/
│ └── oauth/
│ ├── __init__.py
│ ├── base_oauth.py # BaseOAuthService
│ ├── linkedin_oauth.py
│ ├── twitter_oauth.py
│ ├── facebook_oauth.py
│ ├── instagram_oauth.py # Routes through Facebook
│ └── tiktok_oauth.py
├── business/
│ ├── social_publisher.py # SocialPublisherService + platform publishers
│ └── social_image_resize.py # SocialImageResizeService
├── tasks/
│ └── social_tasks.py # Celery tasks
├── urls/
│ └── social.py # Social endpoints
└── migrations/
└── XXXX_add_social_models.py
Conventions
- PKs: BigAutoField (integer) — do NOT use UUIDs
- Table prefix:
igny8_on all new tables - App label:
social(new app) - Celery app name:
igny8_core - URL pattern:
/api/v1/social/... - Permissions: Use
SiteSectorModelViewSetpermission pattern - Token encryption: Same Fernet pattern as 02C GSC tokens — NEVER expose raw tokens in API responses
- AI functions: Extend
BaseAIFunction; register asgenerate_social_posts - Frontend:
.tsxfiles with Zustand stores
Cross-References
| Doc | Relationship |
|---|---|
| 01E | Pipeline Stage 8 integration — hooks after Stage 7 publish |
| 02I | Video creator shares social posting for video content; reuses SocialAccount OAuth for YouTube/TikTok |
| 02C | GSC token encryption pattern reused for social OAuth tokens |
| 03A | WP plugin standalone has share buttons — different from this (posting FROM IGNY8) |
| 04A | Managed services include social media management as a service tier |
Key Decisions
- New
socialapp — Separate from integration because social media is a distinct domain with its own models and business logic - SocialEngagement extends models.Model, not SiteSectorBaseModel — Engagement records are tied to posts, not directly to sites/sectors
- Denormalized
platformon SocialPost — Avoids join to SocialAccount for platform-based queries and filtering - OAuth tokens encrypted at rest — Same Fernet encryption as 02C; tokens never returned in API responses
- AutomationConfig.settings extension — Social pipeline toggles added to existing settings JSONField rather than new model fields