# WordPress Integration & Publishing Flow - Complete Technical Documentation **Last Updated:** January 10, 2026 **Version:** 1.7.0 **Status:** Production Active --- ## Table of Contents 1. [System Overview](#1-system-overview) 2. [Integration Setup Flow](#2-integration-setup-flow) 3. [Manual Publishing Flow](#3-manual-publishing-flow) 4. [Automation Publishing Flow](#4-automation-publishing-flow) 5. [Webhook Sync Flow (WordPress → IGNY8)](#5-webhook-sync-flow-wordpress--igny8) 6. [Metadata Sync Flow](#6-metadata-sync-flow) 7. [Data Models & Storage](#7-data-models--storage) 8. [Plugin Distribution System](#8-plugin-distribution-system) 9. [Current Implementation](#9-current-implementation) 10. [Flow Diagrams](#10-flow-diagrams) --- ## 1. System Overview ### Architecture Summary IGNY8 integrates with WordPress sites through a **custom WordPress plugin** (`igny8-wp-bridge`) that: - Receives content from IGNY8 via a custom REST endpoint (`/wp-json/igny8/v1/publish`) - Sends status updates back to IGNY8 via webhooks - Authenticates using API keys stored in both systems - Auto-updates via IGNY8 plugin distribution system (v1.7.0+) - Supports advanced template rendering with image layouts ### Communication Pattern ``` IGNY8 App ←→ WordPress Site │ │ │ HTTP POST │ ├─────────────→│ Publish content via /wp-json/igny8/v1/publish │ │ │ HTTP POST │ │←─────────────┤ Webhook status updates via /api/v1/integration/webhooks/wordpress/status/ │ │ │ HTTP GET │ ├─────────────→│ Check plugin updates via /wp-json/igny8/v1/check-update │ │ ``` ### Key Components | Component | Location | Purpose | |-----------|----------|---------| | SiteIntegration Model | `business/integration/models.py` | Stores WordPress credentials & config | | SyncEvent Model | `business/integration/models.py` | Logs all sync operations | | Plugin Models | `plugins/models.py` | Plugin versioning and distribution | | Celery Task | `tasks/wordpress_publishing.py` | Background publishing worker | | Webhook Handler | `modules/integration/webhooks.py` | Receives WordPress status updates | | Frontend Form | `components/sites/WordPressIntegrationForm.tsx` | User setup UI | ### Recent Updates (v1.7.0) **Plugin Distribution System:** - ✅ Automated plugin distribution and updates - ✅ WordPress plugin v1.3.3 with template improvements - ✅ Image layout fixes (square/landscape positioning) - ✅ Auto-update mechanism via WordPress hooks - ✅ Health check and monitoring endpoints --- ## 2. Integration Setup Flow ### 2.1 Pre-requisites (WordPress Side) 1. WordPress 5.6+ with REST API enabled 2. Pretty permalinks enabled (Settings → Permalinks) 3. IGNY8 WordPress Bridge plugin v1.3.0+ installed and activated 4. No security plugins blocking REST API ### 2.2 Plugin Installation (Updated v1.7.0) **Option 1: Manual Download & Install** - User downloads latest plugin from IGNY8 app - Frontend: Site Settings → WordPress Integration → Download Plugin - Download endpoint: `https://api.igny8.com/api/plugins/igny8-wp-bridge/download/` - Installs in WordPress: Plugins → Add New → Upload ZIP **Option 2: Direct URL Install** - WordPress admin → Plugins → Add New → Upload Plugin - Paste ZIP URL with signed download token - Plugin installs and auto-registers with IGNY8 **Plugin Auto-Update:** - WordPress checks for updates every 12 hours - Plugin hooks into `pre_set_site_transient_update_plugins` - Compares current version with latest from IGNY8 API - Notifies admin if update available - Can be updated via WordPress admin (one-click) ### 2.3 Setup Steps (User Flow) **Step 1: User navigates to Site Settings** - Frontend: `/sites/{id}/settings` → WordPress Integration section - Component: `WordPressIntegrationForm.tsx` **Step 2: User clicks "Generate API Key"** - Frontend calls: `POST /v1/integration/integrations/generate-api-key/` - Body: `{ "site_id": 123 }` - Backend creates/updates `SiteIntegration` record with new API key **Step 3: User configures WordPress plugin** - Configures plugin with: - IGNY8 API URL: `https://api.igny8.com` - Site API Key: (copied from IGNY8) - Site ID: (shown in IGNY8) **Step 4: Test Connection** - User clicks "Test Connection" in either app - IGNY8 calls: `GET {wordpress_url}/wp-json/wp/v2/users/me` - Uses API key in `X-IGNY8-API-KEY` header - Success: Connection verified, `is_active` set to true, plugin registers installation - Failure: Error message displayed ### 2.4 Data Created During Setup **SiteIntegration Record:** ```json { "id": 1, "site_id": 123, "account_id": 456, "platform": "wordpress", "platform_type": "cms", "config_json": { "site_url": "https://example.com" }, "credentials_json": { "api_key": "igny8_xxxxxxxxxxxxxxxxxxxx" }, "is_active": true, "sync_enabled": true, "sync_status": "pending" } ``` --- ## 3. Manual Publishing Flow ### 3.1 Trigger Points 1. **Content Review Page** - "Publish to WordPress" button 2. **Content Approved Page** - Publish action in context menu 3. **Bulk Actions** - Select multiple, publish all ### 3.2 Detailed Flow **Step 1: User clicks "Publish to WordPress"** - Frontend: `ContentViewSet.publish` action called - Endpoint: `POST /api/v1/writer/content/{id}/publish/` - Optional body: `{ "site_integration_id": 123 }` **Step 2: Backend validates content** - Checks content exists and belongs to user's site - Checks content not already published (`external_id` must be null) - Finds active WordPress integration for the site - Error if no integration found **Step 3: Optimistic status update** - Content status changed to `published` immediately - User sees success message - Actual WordPress publishing happens async **Step 4: Celery task queued** - Task: `publish_content_to_wordpress.delay(content_id, site_integration_id)` - Task name: `igny8_core.tasks.wordpress_publishing` **Step 5: Celery task execution (Background)** The task performs these steps: 1. **Load data** - Get Content and SiteIntegration from database 2. **Check if already published** - Skip if `external_id` exists 3. **Generate excerpt** - Strip HTML, take first 150 chars 4. **Load taxonomy terms** - Categories from `taxonomy_terms` M2M field, fallback to cluster name 5. **Load tags** - From `taxonomy_terms` + primary/secondary keywords 6. **Load images** - Featured image and gallery images, convert paths to URLs 7. **Build payload:** ```json { "content_id": 123, "title": "Post Title", "content_html": "
Full HTML content...
", "excerpt": "Short excerpt...", "status": "publish", "seo_title": "SEO Title", "seo_description": "Meta description", "primary_keyword": "main keyword", "secondary_keywords": ["kw1", "kw2"], "featured_image_url": "https://app.igny8.com/images/...", "gallery_images": [{ "url": "...", "alt": "", "caption": "" }], "categories": ["Category Name"], "tags": ["tag1", "tag2"] } ``` 8. **Send to WordPress:** - URL: `{site_url}/wp-json/igny8/v1/publish` - Headers: `X-IGNY8-API-Key: {api_key}` - Method: POST - Timeout: 30 seconds **Step 6: Process WordPress response** | HTTP Code | Meaning | Action | |-----------|---------|--------| | 201 | Created | Update content with `external_id`, `external_url`, log success | | 409 | Already exists | Update content with existing WordPress post data | | 4xx/5xx | Error | Log failure, retry up to 3 times with exponential backoff | **Step 7: Update IGNY8 content record** ```python content.external_id = str(wp_post_id) content.external_url = wp_post_url content.status = 'published' content.metadata['wordpress_status'] = 'publish' content.save() ``` **Step 8: Create SyncEvent record** ```python SyncEvent.objects.create( integration=site_integration, site=content.site, account=content.account, event_type='publish', action='content_publish', description=f"Published content '{title}' to WordPress", success=True, content_id=content.id, external_id=external_id, details={...} ) ``` ### 3.3 Error Handling - **Timeout**: Retry after 60 seconds (first attempt), then exponentially - **Connection Error**: Same retry logic - **Max Retries**: 3 attempts total - **Final Failure**: SyncEvent created with `success=False`, error logged --- ## 4. Automation Publishing Flow ### 4.1 Automation Stage 7: Review → Published Automation Stage 7 handles auto-approval and publishing: **Current Implementation:** - Content in `review` status is changed to `published` status - Status change only - **NO automatic WordPress push currently implemented** - Lock released, automation run marked complete **What DOES NOT happen automatically:** - No `publish_content_to_wordpress` task is triggered - Content sits in `published` status waiting for manual publish or scheduled task ### 4.2 Scheduled Publishing Task (NOT CURRENTLY SCHEDULED) There exists a task `process_pending_wordpress_publications()` that: - Finds content with `status='published'` and `external_id=NULL` - Queues each item to `publish_content_to_wordpress` task - Processes max 50 items per run **CURRENT STATUS: This task is NOT in Celery Beat schedule!** Looking at `celery.py`, the beat schedule includes: - `check-scheduled-automations` (hourly) - `replenish-monthly-credits` (monthly) - Various maintenance tasks **Missing:** - `process_pending_wordpress_publications` is NOT scheduled ### 4.3 To Enable Auto-Publishing After Automation Two options: **Option A: Add to Celery Beat Schedule** ```python 'process-pending-wordpress-publications': { 'task': 'igny8_core.tasks.wordpress_publishing.process_pending_wordpress_publications', 'schedule': crontab(minute='*/5'), # Every 5 minutes }, ``` **Option B: Call directly from Stage 7** Modify `run_stage_7()` to queue publish tasks for each approved content item. --- ## 5. Webhook Sync Flow (WordPress → IGNY8) ### 5.1 Purpose Keeps IGNY8 in sync when content is modified directly in WordPress: - Post status changed (published → draft, etc.) - Post deleted/trashed - Post metadata updated ### 5.2 Webhook Endpoints | Endpoint | Purpose | |----------|---------| | `POST /api/v1/integration/webhooks/wordpress/status/` | Status changes | | `POST /api/v1/integration/webhooks/wordpress/metadata/` | Metadata updates | ### 5.3 Status Webhook Flow **Step 1: WordPress plugin detects status change** - Hooks into `transition_post_status` action - Collects: post_id, content_id, new_status, post_url **Step 2: Plugin sends webhook to IGNY8** ``` POST https://app.igny8.com/api/v1/integration/webhooks/wordpress/status/ Headers: X-IGNY8-API-KEY: {api_key} Body: { "post_id": 123, "content_id": 456, "post_status": "publish", "post_url": "https://example.com/my-post/", "post_title": "My Post Title", "site_url": "https://example.com" } ``` **Step 3: IGNY8 validates and processes** 1. Extract API key from header 2. Find Content by `content_id` 3. Find SiteIntegration by site_url + platform 4. Verify API key matches stored key 5. Map WordPress status to IGNY8 status: | WordPress | IGNY8 | |-----------|-------| | publish | published | | draft | draft | | pending | review | | private | published | | trash | draft | | future | review | 6. Update Content record: ```python content.external_id = str(post_id) content.external_url = post_url content.status = mapped_status content.metadata['wordpress_status'] = post_status content.metadata['last_wp_sync'] = now content.save() ``` 7. Create SyncEvent log ### 5.4 Metadata Webhook Flow Similar to status webhook but updates `content.metadata['wp_metadata']` with: - Categories - Tags - Author info - Modified date --- ## 6. Metadata Sync Flow ### 6.1 Manual Sync (User-Initiated) **Trigger:** User clicks "Sync Now" in Site Settings **Endpoint:** `POST /api/v1/integration/integrations/{id}/sync/` **What it does:** - Calls `SyncMetadataService.sync_wordpress_structure()` - Fetches from WordPress: - Post types and counts - Categories list - Tags list - Site metadata - Updates integration's `last_sync_at` - Does NOT push/pull content ### 6.2 Structure Update **Endpoint:** `POST /api/v1/integration/integrations/{id}/update_structure/` Refreshes understanding of WordPress site: - Available post types - Taxonomy structures - Capabilities --- ## 7. Data Models & Storage ### 7.1 SiteIntegration | Field | Type | Purpose | |-------|------|---------| | id | AutoField | Primary key | | account | FK(Account) | Owner account | | site | FK(Site) | IGNY8 site | | platform | CharField | 'wordpress' | | platform_type | CharField | 'cms' | | config_json | JSONField | `{ "site_url": "https://..." }` | | credentials_json | JSONField | `{ "api_key": "igny8_xxx" }` | | is_active | Boolean | Connection enabled | | sync_enabled | Boolean | Two-way sync enabled | | last_sync_at | DateTime | Last successful sync | | sync_status | CharField | pending/success/failed/syncing | | sync_error | TextField | Last error message | ### 7.2 SyncEvent | Field | Type | Purpose | |-------|------|---------| | id | AutoField | Primary key | | integration | FK(SiteIntegration) | Parent integration | | site | FK(Site) | Related site | | account | FK(Account) | Owner account | | event_type | CharField | publish/sync/error/webhook/metadata_sync | | action | CharField | content_publish/status_update/etc | | description | TextField | Human-readable event description | | success | Boolean | Event outcome | | content_id | Integer | IGNY8 content ID | | external_id | CharField | WordPress post ID | | error_message | TextField | Error details if failed | | details | JSONField | Additional event data | | duration_ms | Integer | Operation duration | | created_at | DateTime | Event timestamp | ### 7.3 Content Fields (Publishing Related) | Field | Purpose | |-------|---------| | external_id | WordPress post ID (string) | | external_url | WordPress post URL | | status | IGNY8 status (draft/review/published) | | metadata['wordpress_status'] | WordPress status (publish/draft/etc) | | metadata['last_wp_sync'] | Last webhook sync timestamp | --- ## 8. Current Implementation Gaps ### 8.1 Missing: Scheduled Auto-Publishing **Problem:** Content approved by Automation Stage 7 is not automatically pushed to WordPress. **Current State:** - `process_pending_wordpress_publications()` task exists - Task is NOT in Celery Beat schedule - Content remains in `published` status with `external_id=NULL` **Impact:** Users must manually publish each content item even after automation completes. ### 8.2 Missing: Pull Sync (WordPress → IGNY8 Content) **Problem:** No way to import existing WordPress posts into IGNY8. **Current State:** - Webhooks only handle status updates for content that originated from IGNY8 - No "Import from WordPress" feature - No scheduled pull sync ### 8.3 Missing: Content Update Sync **Problem:** Editing content in IGNY8 after publishing doesn't update WordPress. **Current State:** - Only initial publish is supported - No "republish" or "update" functionality - `external_id` check prevents re-publishing ### 8.4 Missing: Image Upload to WordPress **Problem:** Featured images are passed as URLs, not uploaded to WordPress media library. **Current State:** - Image URL is sent in payload - WordPress plugin must download and create attachment - If plugin doesn't handle this, no featured image --- ## 8. Plugin Distribution System ### 8.1 Architecture (v1.7.0) **Plugin Distribution Components:** - `Plugin` model - Multi-platform plugin registry - `PluginVersion` model - Version tracking with files and checksums - `PluginInstallation` model - Installation tracking per site - `PluginDownload` model - Download analytics **Distribution Endpoints:** ``` GET /api/plugins/igny8-wp-bridge/download/ - Download latest ZIP POST /api/plugins/igny8-wp-bridge/check-update/ - Check for updates GET /api/plugins/igny8-wp-bridge/info/ - Plugin metadata POST /api/plugins/igny8-wp-bridge/register/ - Register installation POST /api/plugins/igny8-wp-bridge/health-check/ - Health monitoring ``` ### 8.2 Auto-Update Mechanism **WordPress Side:** 1. Plugin hooks into `pre_set_site_transient_update_plugins` 2. Calls `/api/plugins/igny8-wp-bridge/check-update/` with current version 3. Receives update info if newer version available 4. WordPress displays update notification 5. User clicks "Update Now" (or auto-update runs) 6. WordPress downloads ZIP from `/download/` endpoint 7. Installs and activates new version **IGNY8 Side:** 1. Developer updates plugin source code 2. Creates new `PluginVersion` in Django admin 3. Changes status to "released" 4. Signal automatically builds ZIP with checksums 5. Files stored in `/plugins/wordpress/dist/` 6. WordPress sites can now download/update ### 8.3 Version History (Recent) | Version | Date | Changes | |---------|------|---------| | 1.3.3 | Jan 10, 2026 | Template design: Square image grid fixes, landscape positioning, direct styling for images without captions | | 1.3.2 | Jan 9, 2026 | Template rendering improvements, image layout enhancements | | 1.3.1 | Jan 9, 2026 | Plugin versioning updates | | 1.3.0 | Jan 8, 2026 | Distribution system release, auto-update mechanism | ### 8.4 Security Features - **Signed URLs:** Download links expire after configurable time - **Checksums:** MD5 and SHA256 verification - **Rate Limiting:** Per IP/site download limits - **API Authentication:** Required for sensitive operations - **Version Verification:** WordPress validates plugin before update ### 8.5 Monitoring **Installation Tracking:** - Total installations per version - Active installations by site - Version distribution analytics **Download Analytics:** - Download counts per version - Geographic distribution - Failed download attempts **Health Checks:** - Plugin health status per installation - Error reporting from WordPress sites - Version compatibility tracking --- ## 9. Current Implementation ### 9.1 Production Status (v1.7.0) **✅ Fully Operational:** - WordPress plugin distribution system - Auto-update mechanism - Template rendering with advanced layouts - Image positioning (square/landscape) - Content publishing (manual + automation) - Webhook status sync - API authentication - Health monitoring **✅ Template Features (v1.3.3):** - Square images: Side-by-side layout (left/right aligned) - Landscape images: Full-width with max 1024px - Images appear after first paragraph in sections - Direct border-radius/shadow on images without captions - Responsive design for mobile/tablet ### 9.2 Known Limitations **Missing Features:** - Bi-directional content sync (WordPress → IGNY8 editing) - Conflict resolution for dual edits - Advanced metadata sync beyond basics - Multi-site WordPress network support - Custom post type publishing ### 9.3 Future Enhancements **Planned:** - Image regeneration feature (Phase 9) - Advanced template customization - Custom field mapping - Bulk publishing operations - Real-time sync notifications --- ## 10. Flow Diagrams ### 9.1 Integration Setup ``` ┌──────────┐ ┌──────────────┐ ┌───────────────┐ │ User │ │ IGNY8 App │ │ WordPress │ └────┬─────┘ └──────┬───────┘ └───────┬───────┘ │ │ │ │ 1. Open Site Settings │ ├─────────────────>│ │ │ │ │ │ 2. Download Plugin │ ├─────────────────>│ │ │ │ │ │<─────────────────┤ │ │ 3. Plugin ZIP │ │ │ │ │ │ 4. Install Plugin──────────────────────┼──────────> │ │ │ │ 5. Generate API Key │ ├─────────────────>│ │ │<─────────────────┤ │ │ 6. Display API Key │ │ │ │ │ 7. Enter API Key in Plugin─────────────┼──────────> │ │ │ │ 8. Test Connection │ ├─────────────────>│ │ │ │ 9. GET /wp-json/... │ │ ├────────────────────>│ │ │<────────────────────┤ │<─────────────────┤ 10. Success │ │ │ │ │ │ 11. Register Install│ │ │<────────────────────┤ ``` ### 10.2 Manual Publishing ``` ┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌───────────────┐ │ User │ │ IGNY8 API │ │ Celery │ │ WordPress │ └────┬─────┘ └──────┬───────┘ └────┬─────┘ └───────┬───────┘ │ │ │ │ │ 1. Click Publish │ │ │ ├─────────────────>│ │ │ │ │ │ │ │ │ 2. Validate │ │ │ │ 3. Update status │ │ │ │ 4. Queue task │ │ │ ├─────────────────>│ │ │<─────────────────┤ │ │ │ 5. "Publishing..." │ │ │ │ │ │ │ │ │ 6. POST /publish │ │ │ ├──────────────────>│ │ │ │ │ │ │ │<──────────────────┤ │ │ │ 7. 201 Created │ │ │ │ │ │ │ 8. Update Content│ │ │ │<─────────────────┤ │ │ │ 9. Create SyncEvent │ │ │ │ │ ``` ### 10.3 Plugin Auto-Update Flow ``` ┌───────────────┐ ┌──────────────┐ │ WordPress │ │ IGNY8 API │ └───────┬───────┘ └──────┬───────┘ │ │ │ 1. Check for updates (cron) │ POST /check-update/│ ├───────────────────>│ │ (current: 1.3.2) │ │ │ │<───────────────────┤ │ 2. Update available│ │ (latest: 1.3.3) │ │ │ │ 3. User clicks │ │ "Update Now" │ │ │ │ GET /download/ │ ├───────────────────>│ │ │ │<───────────────────┤ │ 4. ZIP file │ │ │ │ 5. Install & Activate │ │ │ POST /register/ │ ├───────────────────>│ │ (new version info) │ │ │ │<───────────────────┤ │ 6. Registration OK │ │ │ ``` ### 10.4 Webhook Status Sync ``` ┌───────────────┐ ┌──────────────┐ │ WordPress │ │ IGNY8 API │ └───────┬───────┘ └──────┬───────┘ │ │ │ 1. User changes status in WP │ │ │ 2. POST /webhooks/wordpress/status/ ├───────────────────>│ │ │ │ │ 3. Validate API key │ │ 4. Find Content │ │ 5. Map status │ │ 6. Update Content │ │ 7. Create SyncEvent │ │ │<───────────────────┤ │ 8. 200 OK │ │ │ ``` --- ## Summary | Flow | Status | Notes | |------|--------|-------| | Integration Setup | ✅ Working | API key based | | Manual Publish | ✅ Working | Via Celery task | | Automation Publish | ⚠️ Partial | Stage 7 sets status but doesn't trigger WordPress push | | Webhook Status Sync | ✅ Working | WordPress → IGNY8 status updates | | Webhook Metadata Sync | ✅ Working | WordPress → IGNY8 metadata | | Scheduled Auto-Publish | ❌ Not Active | Task exists but not scheduled | | Content Pull Sync | ❌ Not Implemented | No import from WordPress | | Content Update Sync | ❌ Not Implemented | No republish capability |