diff --git a/docs/WORDPRESS_INTEGRATION_REFERENCE.md b/docs/WORDPRESS_INTEGRATION_REFERENCE.md
new file mode 100644
index 00000000..30f37a9f
--- /dev/null
+++ b/docs/WORDPRESS_INTEGRATION_REFERENCE.md
@@ -0,0 +1,1367 @@
+# IGNY8 WordPress Integration Reference
+
+**Version:** 1.0.0
+**Last Updated:** November 29, 2025
+**Purpose:** Complete reference for the bidirectional WordPress ↔ IGNY8 integration, covering architecture, data flows, sync mechanisms, and troubleshooting.
+
+---
+
+## Table of Contents
+
+1. [Integration Overview](#integration-overview)
+2. [Architecture](#architecture)
+3. [WordPress Plugin Structure](#wordpress-plugin-structure)
+4. [IGNY8 Backend Integration Points](#igny8-backend-integration-points)
+5. [Authentication & Security](#authentication--security)
+6. [Bidirectional Sync: Data Elements](#bidirectional-sync-data-elements)
+7. [Publishing Flow (IGNY8 → WordPress)](#publishing-flow-igny8--wordpress)
+8. [Sync Flow (WordPress → IGNY8)](#sync-flow-wordpress--igny8)
+9. [REST API Endpoints](#rest-api-endpoints)
+10. [Data Models & Meta Fields](#data-models--meta-fields)
+11. [Status Mapping](#status-mapping)
+12. [Troubleshooting Guide](#troubleshooting-guide)
+13. [Developer Quick Reference](#developer-quick-reference)
+
+---
+
+## Integration Overview
+
+The IGNY8 WordPress Bridge enables **bidirectional synchronization** between IGNY8 (SaaS platform) and WordPress sites, allowing content created in IGNY8 to be published to WordPress and WordPress content/metadata to be synced back to IGNY8.
+
+### Core Capabilities
+
+| Feature | Direction | Status |
+|---------|-----------|--------|
+| **Content Publishing** | IGNY8 → WP | ✅ Live |
+| **Post Status Sync** | WP → IGNY8 | ✅ Live |
+| **Taxonomy Sync** | WP ↔ IGNY8 | ✅ Live |
+| **SEO Metadata** | IGNY8 → WP | ✅ Live |
+| **Featured Images** | IGNY8 → WP | ✅ Live |
+| **Site Discovery** | WP → IGNY8 | ✅ Live |
+| **Keyword Tracking** | IGNY8 → WP | ✅ Live |
+| **Internal Linking** | IGNY8 → WP | 🔄 Pending |
+| **Content Optimization** | WP ↔ IGNY8 | 🔄 Pending |
+
+### Integration Model
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ IGNY8 SaaS Platform │
+│ ┌──────────────────────────────────────────────────────┐ │
+│ │ Django Backend (api.igny8.com) │ │
+│ │ - Publisher Service │ │
+│ │ - WordPress Adapter │ │
+│ │ - Content Models │ │
+│ │ - Integration Models │ │
+│ │ - Celery Background Tasks │ │
+│ └──────────────────────────────────────────────────────┘ │
+└───────────────────────┬─────────────────────────────────────┘
+ │
+ │ REST API (JSON)
+ │ - JWT Auth + API Key
+ │ - Unified Response Format
+ │
+┌───────────────────────▼─────────────────────────────────────┐
+│ WordPress Site (client.com) │
+│ ┌──────────────────────────────────────────────────────┐ │
+│ │ IGNY8 Bridge Plugin │ │
+│ │ - API Client (class-igny8-api.php) │ │
+│ │ - REST Endpoints (/wp-json/igny8/v1/) │ │
+│ │ - Sync Handlers (sync/*.php) │ │
+│ │ - Post Meta Storage (_igny8_* fields) │ │
+│ │ - Custom Taxonomies (igny8_clusters, igny8_sectors)│ │
+│ └──────────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Architecture
+
+### High-Level Data Flow
+
+```
+IGNY8 → WordPress Publishing Flow:
+┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌──────────────┐
+│ Frontend │ -> │ Publisher │ -> │ Publisher │ -> │ WordPress │ -> │ WordPress │
+│ (Review.tsx)│ │ ViewSet │ │ Service │ │ Adapter │ │ REST API │
+│ Publish │ │ /publish/ │ │publish_content()│ │ publish() │ │ /igny8/v1/ │
+│ Button │ │ │ │ │ │ │ │publish-content│
+└─────────────┘ └──────────────┘ └─────────────────┘ └──────────────┘ └──────────────┘
+ │
+ ▼
+ ┌──────────────┐
+ │ WordPress │
+ │ Post │
+ │ Created │
+ └──────────────┘
+
+WordPress → IGNY8 Sync Flow:
+┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
+│ WordPress │ -> │ WP Hook │ -> │ Igny8API │ -> │ IGNY8 API │
+│ save_post │ │ post-sync.php│ │ PUT/POST │ │ /writer/tasks│
+│ Event │ │ │ │ │ │ │
+└─────────────┘ └──────────────┘ └─────────────────┘ └──────────────┘
+ │
+ ▼
+ ┌──────────────┐
+ │ Content │
+ │ Updated │
+ └──────────────┘
+```
+
+### Component Interaction
+
+**IGNY8 Backend Components:**
+- **PublisherViewSet** (`igny8_core/modules/publisher/views.py`) - REST API endpoint
+- **PublisherService** (`igny8_core/business/publishing/services/publisher_service.py`) - Business logic
+- **WordPressAdapter** (`igny8_core/business/publishing/services/adapters/wordpress_adapter.py`) - WP integration
+- **Celery Tasks** (`igny8_core/tasks/wordpress_publishing.py`) - Async background publishing
+- **Content Model** (`igny8_core/business/content/models.py`) - Content data
+- **SiteIntegration Model** (`igny8_core/business/integration/models.py`) - WordPress connection config
+
+**WordPress Plugin Components:**
+- **Igny8API** (`includes/class-igny8-api.php`) - API client for IGNY8
+- **Igny8RestAPI** (`includes/class-igny8-rest-api.php`) - REST endpoints for IGNY8 to call
+- **Post Sync** (`sync/post-sync.php`) - WP → IGNY8 synchronization
+- **IGNY8 to WP** (`sync/igny8-to-wp.php`) - IGNY8 → WP content creation
+- **Hooks** (`sync/hooks.php`) - WordPress action/filter hooks
+- **Admin** (`admin/settings.php`) - Plugin settings UI
+
+---
+
+## WordPress Plugin Structure
+
+```
+igny8-wp-integration/
+├── igny8-bridge.php # Main plugin file
+│ - Plugin bootstrap
+│ - Registers all classes
+│ - Defines constants
+│ - Version: 1.0.0
+│
+├── includes/ # Core functionality
+│ ├── class-igny8-api.php # API Client
+│ │ - login() - JWT authentication
+│ │ - refresh_token() - Token refresh
+│ │ - get/post/put/delete() - HTTP methods
+│ │ - parse_response() - Unified response handling
+│ │
+│ ├── class-igny8-rest-api.php # WordPress REST Endpoints
+│ │ Endpoints:
+│ │ - GET /wp-json/igny8/v1/post-by-content-id/{id}
+│ │ - GET /wp-json/igny8/v1/post-by-task-id/{id}
+│ │ - GET /wp-json/igny8/v1/post-status/{content_id}
+│ │ - GET /wp-json/igny8/v1/site-metadata/
+│ │ - GET /wp-json/igny8/v1/status
+│ │ - POST /wp-json/igny8/v1/publish-content/
+│ │
+│ ├── class-igny8-site.php # Site data collection
+│ ├── class-igny8-webhooks.php # Webhook handlers
+│ ├── class-igny8-link-queue.php # Link processing
+│ └── functions.php # Helper functions
+│
+├── sync/ # Synchronization
+│ ├── hooks.php # WP hooks registration
+│ │ - save_post
+│ │ - publish_post
+│ │ - transition_post_status
+│ │
+│ ├── post-sync.php # WP → IGNY8 sync
+│ │ - igny8_sync_post_status_to_igny8()
+│ │ - igny8_update_keywords_on_post_publish()
+│ │ - igny8_map_wp_status_to_igny8()
+│ │
+│ ├── taxonomy-sync.php # Taxonomy sync
+│ │ - Clusters (igny8_clusters)
+│ │ - Sectors (igny8_sectors)
+│ │ - Categories/Tags mapping
+│ │
+│ └── igny8-to-wp.php # IGNY8 → WP content
+│ - igny8_create_wordpress_post_from_task()
+│ - igny8_import_seo_metadata()
+│ - igny8_import_featured_image()
+│ - igny8_import_taxonomies()
+│
+├── admin/ # Admin interface
+│ ├── settings.php # Settings page UI
+│ │ - API credentials
+│ │ - Connection status
+│ │ - Post type toggles
+│ │
+│ ├── class-admin.php # Settings controller
+│ ├── class-admin-columns.php # Custom post columns
+│ └── class-post-meta-boxes.php # Post editor meta boxes
+│
+├── data/ # Data collection
+│ ├── site-collection.php # Full site scan
+│ ├── semantic-mapping.php # Semantic metadata
+│ ├── link-graph.php # Internal link graph
+│ └── woocommerce.php # WooCommerce integration
+│
+└── docs/ # Documentation
+ ├── WORDPRESS-PLUGIN-INTEGRATION.md
+ ├── AUTHENTICATION-AUDIT.md
+ └── SYNC-DATA-FLOW-DIAGRAM.md
+```
+
+### Key Plugin Classes
+
+**Igny8API**
+- **Purpose:** Communication with IGNY8 REST API
+- **Location:** `includes/class-igny8-api.php`
+- **Key Methods:**
+ - `login($email, $password)` - Authenticate and get JWT tokens
+ - `refresh_token()` - Refresh expired access token
+ - `get($endpoint)` - GET request with auto-retry on 401
+ - `post($endpoint, $data)` - POST request
+ - `put($endpoint, $data)` - PUT request (for updates)
+ - `parse_response($response)` - Parse unified response format
+
+**Igny8RestAPI**
+- **Purpose:** Provide REST endpoints for IGNY8 to call
+- **Location:** `includes/class-igny8-rest-api.php`
+- **Authentication:** API key via `X-IGNY8-API-KEY` header or Bearer token
+- **Response Format:** Unified `{success, data, message, request_id}`
+
+---
+
+## IGNY8 Backend Integration Points
+
+### Database Models
+
+**Content Model** (`igny8_core/business/content/models.py`)
+```python
+class Content(SiteSectorBaseModel):
+ """Main content storage in IGNY8"""
+
+ # Core fields
+ title = CharField(max_length=255)
+ content_html = TextField() # Final HTML content
+ word_count = IntegerField()
+
+ # SEO fields
+ meta_title = CharField(max_length=255)
+ meta_description = TextField()
+ primary_keyword = CharField(max_length=255)
+ secondary_keywords = JSONField(default=list)
+
+ # Content classification
+ cluster = ForeignKey('planner.Clusters') # Required
+ content_type = CharField(choices=['post', 'page', 'product', 'taxonomy'])
+ content_structure = CharField(choices=['article', 'guide', 'comparison', ...])
+
+ # WordPress sync fields
+ external_id = CharField() # WordPress post ID
+ external_url = URLField() # WordPress post URL
+ external_type = CharField() # WordPress post type
+ sync_status = CharField() # Sync status
+
+ # Status
+ status = CharField(choices=['draft', 'review', 'published'])
+ source = CharField(choices=['igny8', 'wordpress'])
+```
+
+**SiteIntegration Model** (`igny8_core/business/integration/models.py`)
+```python
+class SiteIntegration(AccountBaseModel):
+ """WordPress connection configuration"""
+
+ site = ForeignKey('Site')
+ platform = CharField(choices=['wordpress', 'shopify', 'custom'])
+
+ # Configuration
+ config_json = JSONField() # {site_url, endpoints, ...}
+ credentials_json = JSONField() # {api_key, username, password}
+
+ # Sync tracking
+ is_active = BooleanField(default=True)
+ sync_enabled = BooleanField(default=False)
+ last_sync_at = DateTimeField()
+ sync_status = CharField(choices=['success', 'failed', 'pending', 'syncing'])
+```
+
+**PublishingRecord Model** (`igny8_core/business/publishing/models.py`)
+```python
+class PublishingRecord(SiteSectorBaseModel):
+ """Track content publishing to destinations"""
+
+ content = ForeignKey('writer.Content')
+ destination = CharField() # 'wordpress', 'sites', 'shopify'
+ destination_id = CharField() # External platform ID
+ destination_url = URLField() # Published URL
+
+ status = CharField(choices=['pending', 'publishing', 'published', 'failed'])
+ published_at = DateTimeField()
+ error_message = TextField()
+ metadata = JSONField()
+```
+
+### Backend Services
+
+**PublisherService** (`igny8_core/business/publishing/services/publisher_service.py`)
+- **Purpose:** Main publishing orchestrator
+- **Key Method:** `publish_content(content_id, destinations, account)`
+- **Flow:**
+ 1. Lookup Content by ID
+ 2. For each destination → call `_publish_to_destination()`
+ 3. Create PublishingRecord
+ 4. Get adapter (WordPressAdapter)
+ 5. Get SiteIntegration config
+ 6. Call adapter.publish()
+ 7. Update PublishingRecord status
+
+**WordPressAdapter** (`igny8_core/business/publishing/services/adapters/wordpress_adapter.py`)
+- **Purpose:** WordPress-specific publishing logic
+- **Authentication:** Auto-detects API key vs username/password
+- **Key Methods:**
+ - `publish(content, destination_config)` - Main publish method
+ - `_publish_via_api_key()` - Uses `/wp-json/igny8/v1/publish-content/`
+ - `_publish_via_username_password()` - Uses standard WP REST API
+- **Payload:**
+ ```python
+ {
+ 'content_id': content.id,
+ 'title': content.title,
+ 'content_html': content.content_html,
+ 'content_type': content.content_type,
+ 'content_structure': content.content_structure,
+ 'meta_title': content.meta_title,
+ 'meta_description': content.meta_description,
+ 'status': content.status,
+ 'cluster_id': content.cluster.id,
+ 'sector_id': content.sector.id,
+ 'featured_image': {...},
+ 'categories': [...],
+ 'tags': [...]
+ }
+ ```
+
+**Celery Tasks** (`igny8_core/tasks/wordpress_publishing.py`)
+- **Purpose:** Async background publishing
+- **Task:** `publish_content_to_wordpress(content_id, site_integration_id)`
+- **Features:**
+ - Retry logic (3 retries, exponential backoff)
+ - Duplicate check (prevents re-publishing)
+ - Updates Content.external_id and external_url
+ - Comprehensive logging
+
+---
+
+## Authentication & Security
+
+### IGNY8 → WordPress Authentication
+
+**Method 1: API Key (Preferred)**
+- **Storage:** `SiteIntegration.credentials_json['api_key']`
+- **WordPress Storage:** `igny8_api_key` option (encrypted if available)
+- **Header:** `X-IGNY8-API-KEY: {api_key}` or `Authorization: Bearer {api_key}`
+- **Endpoint:** `/wp-json/igny8/v1/publish-content/`
+- **Security:** API key validated in `Igny8RestAPI::check_permission()`
+
+**Method 2: Username/Password (Legacy)**
+- **Storage:** `SiteIntegration.credentials_json['username']` and `password`
+- **Flow:** Uses standard WordPress REST API authentication
+- **Limitation:** Requires Application Password or JWT plugin
+
+### WordPress → IGNY8 Authentication
+
+**JWT Token Flow**
+1. **Login:** `POST /api/v1/auth/login/` with `{email, password}`
+2. **Response:** `{access, refresh}` tokens
+3. **Storage:** `igny8_access_token` and `igny8_refresh_token` WordPress options
+4. **Usage:** `Authorization: Bearer {access_token}` header
+5. **Refresh:** On 401, call `POST /api/v1/auth/refresh/` with `{refresh}`
+6. **Auto-retry:** `Igny8API` class automatically refreshes and retries on 401
+
+**Credentials Required (WordPress Settings)**
+- **Email:** `igny8_email` option
+- **Password:** Stored temporarily for token refresh
+- **API Key:** `igny8_api_key` option (for incoming requests from IGNY8)
+
+---
+
+## Bidirectional Sync: Data Elements
+
+### IGNY8 → WordPress (Publishing)
+
+**Content Fields Synced:**
+
+| IGNY8 Field | WordPress Destination | Notes |
+|-------------|----------------------|-------|
+| `title` | `post_title` | Sanitized |
+| `content_html` | `post_content` | Passed through `wp_kses_post()` |
+| `meta_title` | `_yoast_wpseo_title`
`_seopress_titles_title`
`_aioseo_title`
`_igny8_meta_title` | Multi-SEO plugin support |
+| `meta_description` | `_yoast_wpseo_metadesc`
`_seopress_titles_desc`
`_aioseo_description`
`_igny8_meta_description` | Multi-SEO plugin support |
+| `status` | `post_status` | Mapped via `igny8_map_igny8_status_to_wp()` |
+| `content_type` | `post_type` | `post`, `page`, `product` |
+| `content_structure` | `_igny8_content_structure` | Meta field for tracking |
+| `cluster.id` | `_igny8_cluster_id`
`igny8_clusters` taxonomy | Both meta and taxonomy term |
+| `sector.id` | `_igny8_sector_id`
`igny8_sectors` taxonomy | Both meta and taxonomy term |
+| `task_id` | `_igny8_task_id` | Task reference |
+| `id` (Content) | `_igny8_content_id` | Content reference |
+| `primary_keyword` | `_igny8_primary_keyword` | SEO keyword |
+| `secondary_keywords` | `_igny8_secondary_keywords` | JSON array |
+| `featured_image.url` | Featured Image (attachment) | Downloaded and attached |
+| `gallery_images[]` | `_igny8_gallery_images` | Image attachments |
+| `categories[]` | `category` taxonomy | Standard WP categories |
+| `tags[]` | `post_tag` taxonomy | Standard WP tags |
+
+**Post Meta Fields Created:**
+
+```php
+// IGNY8 identification
+_igny8_task_id // Task ID from IGNY8
+_igny8_content_id // Content ID from IGNY8
+_igny8_cluster_id // Cluster ID
+_igny8_sector_id // Sector ID
+_igny8_source // 'igny8' or 'wordpress'
+
+// Content metadata
+_igny8_content_type // 'post', 'page', 'product'
+_igny8_content_structure // 'article', 'guide', etc.
+
+// SEO fields (generic)
+_igny8_meta_title // SEO title
+_igny8_meta_description // SEO description
+_igny8_primary_keyword // Primary keyword
+_igny8_secondary_keywords // JSON array
+
+// Sync tracking
+_igny8_wordpress_status // WordPress post_status
+_igny8_last_synced // Last sync timestamp
+_igny8_brief_cached_at // Brief cache timestamp
+_igny8_task_brief // Cached task brief
+```
+
+### WordPress → IGNY8 (Status Sync)
+
+**Fields Synced on Post Save:**
+
+| WordPress Field | IGNY8 Endpoint | IGNY8 Field | Trigger |
+|----------------|----------------|-------------|---------|
+| `post_status` | `PUT /writer/tasks/{id}/` | `status` (mapped)
`wordpress_status` (raw) | `save_post` hook |
+| `ID` | `PUT /writer/tasks/{id}/` | `assigned_post_id` | `save_post` hook |
+| `permalink` | `PUT /writer/tasks/{id}/` | `post_url` | `save_post` hook |
+| `post_type` | `PUT /writer/tasks/{id}/` | `post_type` | `save_post` hook |
+| Post updated | `PUT /writer/tasks/{id}/` | `synced_at` | `save_post` hook |
+
+**Sync Conditions:**
+- Only syncs if post has `_igny8_task_id` meta (IGNY8-managed posts)
+- Skips autosaves and revisions
+- Only runs if connection is enabled (`igny8_is_connection_enabled()`)
+- Implemented in `sync/post-sync.php`
+
+### Site Metadata Discovery (WordPress → IGNY8)
+
+**Endpoint:** `GET /wp-json/igny8/v1/site-metadata/`
+
+**Data Collected:**
+
+```json
+{
+ "success": true,
+ "data": {
+ "site_info": {
+ "name": "Site Name",
+ "url": "https://example.com",
+ "description": "Site description",
+ "language": "en_US",
+ "timezone": "America/New_York"
+ },
+ "post_types": [
+ {
+ "name": "post",
+ "label": "Posts",
+ "count": 150,
+ "supports": ["title", "editor", "thumbnail", "excerpt"]
+ },
+ {
+ "name": "page",
+ "label": "Pages",
+ "count": 25
+ },
+ {
+ "name": "product",
+ "label": "Products",
+ "count": 80
+ }
+ ],
+ "taxonomies": [
+ {
+ "name": "category",
+ "label": "Categories",
+ "count": 20,
+ "terms": [...]
+ },
+ {
+ "name": "post_tag",
+ "label": "Tags",
+ "count": 150
+ }
+ ],
+ "plugins": {
+ "woocommerce": true,
+ "yoast_seo": true,
+ "advanced_custom_fields": false
+ }
+ }
+}
+```
+
+**Cache:** Transient `igny8_site_metadata_v1` (5 minutes)
+
+---
+
+## Publishing Flow (IGNY8 → WordPress)
+
+### Complete Flow (Frontend to WordPress)
+
+**Step 1: User Clicks Publish Button**
+- **File:** `frontend/src/pages/Writer/Review.tsx`
+- **Method:** `handlePublishSingle()`
+- **Action:** `POST /api/v1/publisher/publish/`
+- **Payload:** `{content_id, destinations: ['wordpress']}`
+
+**Step 2: Publisher API Endpoint**
+- **File:** `igny8_core/modules/publisher/views.py`
+- **Class:** `PublisherViewSet`
+- **Method:** `publish()` action
+- **Logging:** `🚀 Publish request received`
+- **Action:** Call `PublisherService.publish_content()`
+
+**Step 3: Publisher Service**
+- **File:** `igny8_core/business/publishing/services/publisher_service.py`
+- **Method:** `publish_content(content_id, destinations, account)`
+- **Steps:**
+ 1. Load Content from database
+ 2. For each destination, call `_publish_to_destination()`
+ 3. Create `PublishingRecord` (status='pending')
+ 4. Get adapter for destination
+ 5. Lookup `SiteIntegration` for site
+ 6. Merge config: `{**config_json, **credentials_json, 'site_url': ...}`
+ 7. Call `adapter.publish(content, config)`
+ 8. Update `PublishingRecord` (status='published' or 'failed')
+
+**Step 4: WordPress Adapter**
+- **File:** `igny8_core/business/publishing/services/adapters/wordpress_adapter.py`
+- **Method:** `publish(content, destination_config)`
+- **Auto-detection:**
+ ```python
+ if config.get('api_key'):
+ return _publish_via_api_key(...)
+ else:
+ return _publish_via_username_password(...)
+ ```
+
+**Step 4a: API Key Method (Preferred)**
+- **Method:** `_publish_via_api_key()`
+- **Endpoint:** `POST {site_url}/wp-json/igny8/v1/publish-content/`
+- **Headers:** `X-IGNY8-API-KEY: {api_key}`
+- **Payload:**
+ ```json
+ {
+ "content_id": 123,
+ "title": "Post Title",
+ "content_html": "
...
", + "content_type": "post", + "content_structure": "article", + "meta_title": "SEO Title", + "meta_description": "SEO Description", + "status": "published", + "cluster_id": 45, + "sector_id": 12, + "featured_image": {...}, + "categories": [...], + "tags": [...] + } + ``` +- **Response:** `{success, data: {post_id, url}}` + +**Step 5: WordPress REST Endpoint** +- **File:** `includes/class-igny8-rest-api.php` +- **Method:** `publish_content_to_wordpress()` +- **Endpoint:** `/wp-json/igny8/v1/publish-content/` +- **Permission:** `check_permission()` validates API key +- **Action:** Call `igny8_create_wordpress_post_from_task($content_data)` + +**Step 6: WordPress Post Creation** +- **File:** `sync/igny8-to-wp.php` +- **Function:** `igny8_create_wordpress_post_from_task()` +- **Steps:** + 1. Resolve post type from content_type + 2. Map author (via `igny8_map_content_author()`) + 3. Prepare post data + 4. `wp_insert_post()` - Create WordPress post + 5. Import SEO metadata (`igny8_import_seo_metadata()`) + 6. Import featured image (`igny8_import_featured_image()`) + 7. Import taxonomies (`igny8_import_taxonomies()`) + 8. Set cluster/sector taxonomy terms + 9. Set categories/tags + 10. Store IGNY8 meta fields + 11. Update IGNY8 task via API (`PUT /writer/tasks/{id}/`) +- **Return:** WordPress post ID or WP_Error + +**Step 7: Response Propagation** +- WordPress → WordPress Adapter → Publisher Service → Publisher ViewSet → Frontend +- **Frontend Success:** + ```javascript + console.log('[Review.handlePublishSingle] ✅ Publish successful:', response) + toast.success('Published to WordPress!') + ``` + +### Alternative: Celery Background Task + +**When:** If using async publishing (currently available but not default) + +**Trigger:** `publish_content_to_wordpress.delay(content_id, site_integration_id)` + +**File:** `igny8_core/tasks/wordpress_publishing.py` + +**Benefits:** +- Non-blocking (user doesn't wait) +- Retry logic (3 attempts with exponential backoff) +- Duplicate check (prevents re-publishing same content) + +**Flow:** +1. Load Content and SiteIntegration from DB +2. Check if already published (has external_id) +3. Prepare payload +4. POST to WordPress API +5. Update Content.external_id and external_url +6. Log success/failure + +--- + +## Sync Flow (WordPress → IGNY8) + +### Post Status Sync + +**Trigger:** WordPress `save_post`, `publish_post`, `transition_post_status` hooks + +**File:** `sync/post-sync.php` + +**Function:** `igny8_sync_post_status_to_igny8($post_id, $post, $update)` + +**Flow:** +1. Check if connection enabled +2. Skip autosaves and revisions +3. Check if IGNY8-managed (has `_igny8_task_id` meta) +4. Get `$task_id` from post meta +5. Get WordPress `post_status` +6. Map to IGNY8 status: `igny8_map_wp_status_to_igny8($post_status)` +7. Prepare update payload: + ```php + [ + 'status' => $task_status, // IGNY8 mapped status + 'assigned_post_id' => $post_id, + 'post_url' => get_permalink($post_id), + 'wordpress_status' => $post_status, // Raw WP status + 'synced_at' => current_time('mysql'), + 'content_id' => $content_id // If available + ] + ``` +8. `PUT /writer/tasks/{task_id}/` via Igny8API +9. Update `_igny8_wordpress_status` and `_igny8_last_synced` meta + +**Logged in WordPress:** +``` +IGNY8: Synced post 123 status (publish) to task 456 +``` + +### Keyword Status Update on Publish + +**Trigger:** Post published + +**Function:** `igny8_update_keywords_on_post_publish($post_id)` + +**Flow:** +1. Get task_id from post meta +2. Get task details: `GET /writer/tasks/{task_id}/` +3. Extract cluster_id and keyword_ids +4. Update keyword statuses: `PUT /planner/keywords/bulk-update/` +5. Mark keywords as "published" or "in_content" + +--- + +## REST API Endpoints + +### IGNY8 Backend Endpoints (Called by WordPress) + +| Method | Endpoint | Purpose | Request | Response | +|--------|----------|---------|---------|----------| +| POST | `/api/v1/auth/login/` | Get JWT tokens | `{email, password}` | `{access, refresh}` | +| POST | `/api/v1/auth/refresh/` | Refresh access token | `{refresh}` | `{access, refresh}` | +| GET | `/api/v1/writer/tasks/{id}/` | Get task details | - | Task object | +| PUT | `/api/v1/writer/tasks/{id}/` | Update task status | `{status, assigned_post_id, post_url, wordpress_status}` | Updated task | +| GET | `/api/v1/writer/tasks/{id}/brief/` | Get task brief | - | Brief content | +| PUT | `/api/v1/planner/keywords/bulk-update/` | Update keyword statuses | `{keyword_ids, status}` | Updated keywords | +| POST | `/api/v1/system/sites/{id}/import/` | Import site data | Site metadata payload | Import result | + +### WordPress REST Endpoints (Called by IGNY8) + +| Method | Endpoint | Purpose | Auth | Response | +|--------|----------|---------|------|----------| +| GET | `/wp-json/igny8/v1/post-by-content-id/{id}` | Get post by Content ID | API key | `{success, data: {post_id, title, status, url}}` | +| GET | `/wp-json/igny8/v1/post-by-task-id/{id}` | Get post by Task ID | API key | `{success, data: {post_id, title, status, url}}` | +| GET | `/wp-json/igny8/v1/post-status/{content_id}` | Get post status | API key | `{success, data: {wordpress_status, igny8_status}}` | +| GET | `/wp-json/igny8/v1/site-metadata/` | Get site structure | API key | `{success, data: {site_info, post_types, taxonomies}}` | +| GET | `/wp-json/igny8/v1/status` | Plugin health check | Public | `{success, data: {connected, api_key_present}}` | +| POST | `/wp-json/igny8/v1/publish-content/` | Publish content from IGNY8 | API key | `{success, data: {post_id, url}}` | + +**Authentication:** +- **API Key:** `X-IGNY8-API-KEY` header or `Authorization: Bearer {api_key}` +- **Validation:** `Igny8RestAPI::check_permission()` compares with `igny8_api_key` option + +--- + +## Data Models & Meta Fields + +### WordPress Post Meta Taxonomy + +**All IGNY8 meta fields use `_igny8_` prefix** + +```php +// IGNY8 References +_igny8_task_id // INT - IGNY8 Task ID +_igny8_content_id // INT - IGNY8 Content ID +_igny8_cluster_id // INT - IGNY8 Cluster ID +_igny8_sector_id // INT - IGNY8 Sector ID + +// Content Classification +_igny8_content_type // STRING - 'post', 'page', 'product', 'taxonomy' +_igny8_content_structure // STRING - 'article', 'guide', 'comparison', 'review', etc. +_igny8_source // STRING - 'igny8' or 'wordpress' + +// SEO Fields (Generic - not plugin-specific) +_igny8_meta_title // STRING - SEO meta title +_igny8_meta_description // TEXT - SEO meta description +_igny8_primary_keyword // STRING - Primary SEO keyword +_igny8_secondary_keywords // JSON - ["keyword1", "keyword2"] + +// Keywords +_igny8_keyword_ids // JSON - [1, 2, 3] + +// Sync Tracking +_igny8_wordpress_status // STRING - WordPress post_status at last sync +_igny8_last_synced // DATETIME - Last sync timestamp +_igny8_brief_cached_at // DATETIME - Brief cache timestamp +_igny8_task_brief // JSON - Cached task brief data + +// Images +_igny8_gallery_images // JSON - Gallery image IDs +``` + +### WordPress Custom Taxonomies + +**Created by Plugin:** + +```php +// IGNY8 Clusters +register_taxonomy('igny8_clusters', ['post', 'page'], [ + 'label' => 'IGNY8 Clusters', + 'hierarchical' => true, + 'show_in_rest' => true +]); + +// IGNY8 Sectors +register_taxonomy('igny8_sectors', ['post', 'page'], [ + 'label' => 'IGNY8 Sectors', + 'hierarchical' => true, + 'show_in_rest' => true +]); +``` + +**Term Meta:** +- `_igny8_cluster_id` - IGNY8 cluster ID +- `_igny8_sector_id` - IGNY8 sector ID + +### WordPress Options + +```php +// Authentication +igny8_email // User email for IGNY8 account +igny8_access_token // JWT access token +igny8_refresh_token // JWT refresh token +igny8_api_key // API key (encrypted if available) + +// Connection +igny8_connection_enabled // BOOL - Enable/disable sync +igny8_connection_status // STRING - 'connected', 'disconnected', 'error' + +// Settings +igny8_allowed_post_types // JSON - ['post', 'page', 'product'] +igny8_auto_import_enabled // BOOL - Auto-import content from IGNY8 +igny8_sync_mode // STRING - 'manual', 'auto' + +// Cache +igny8_site_metadata_v1 // Transient (5 min) - Cached site metadata +``` + +### IGNY8 Database Tables + +**Content Table** (`igny8_content`) +- Primary table for all content +- Fields: title, content_html, meta_title, meta_description, cluster_id, sector_id, status, external_id, external_url + +**Tasks Table** (`igny8_tasks`) +- Content generation tasks +- Fields: title, cluster_id, status, assigned_post_id, post_url + +**SiteIntegration Table** (`igny8_site_integrations`) +- WordPress connection configs +- Fields: site_id, platform, config_json, credentials_json, is_active, sync_status + +**PublishingRecord Table** (`igny8_publishing_records`) +- Track all publish operations +- Fields: content_id, destination, destination_id, destination_url, status, published_at + +--- + +## Status Mapping + +### IGNY8 Status → WordPress Status + +**Function:** `igny8_map_igny8_status_to_wp($igny8_status)` (WordPress) + +| IGNY8 Status | WordPress Status | Notes | +|--------------|------------------|-------| +| `completed` | `publish` | Published and live | +| `draft` | `draft` | Draft/unpublished | +| `pending` | `pending` | Pending review | +| `scheduled` | `future` | Scheduled for future | +| `archived` | `trash` | Trashed | +| *(other)* | `draft` | Default fallback | + +### WordPress Status → IGNY8 Status + +**Function:** `igny8_map_wp_status_to_igny8($wp_status)` (WordPress) + +| WordPress Status | IGNY8 Status | Notes | +|-----------------|--------------|-------| +| `publish` | `completed` | Published | +| `future` | `completed` | Scheduled (counts as completed) | +| `private` | `completed` | Private but complete | +| `pending` | `pending` | Pending review | +| `draft` | `draft` | Draft | +| `auto-draft` | `draft` | Auto-draft | +| `trash` | `archived` | Trashed | +| *(other)* | `draft` | Default fallback | + +**Important:** WordPress sends raw `wordpress_status` AND mapped IGNY8 `status` to backend + +--- + +## Troubleshooting Guide + +### Common Issues + +#### 1. "Failed to publish: undefined" + +**Symptoms:** +- Error message in frontend when clicking publish +- No post created in WordPress + +**Diagnosis Steps:** +1. **Check browser console:** + ```javascript + [Review.handlePublishSingle] Starting publish for content: {id, title} + [Review.handlePublishSingle] Calling API endpoint /v1/publisher/publish/ + ``` + +2. **Check Django logs:** + ``` + [PublisherViewSet.publish] 🚀 Publish request received: content_id=X + [PublisherService.publish_content] 📄 Content found: title='...' + [WordPressAdapter.publish] 🎬 Starting WordPress publish + ``` + +3. **Check WordPress logs:** + ``` + IGNY8: Creating WordPress post from IGNY8 content + IGNY8: Post created with ID 123 + ``` + +**Common Causes:** +- ❌ SiteIntegration missing API key → Check `credentials_json['api_key']` +- ❌ WordPress plugin not active → Verify plugin activated +- ❌ API key mismatch → Compare IGNY8 and WordPress `api_key` +- ❌ `site_url` not set → Check `SiteIntegration.config_json['site_url']` +- ❌ Content missing `content_html` → Content.content_html is empty + +**Fixes:** +- Ensure SiteIntegration has both `config_json['site_url']` and `credentials_json['api_key']` +- Verify WordPress endpoint accessible: `GET {site_url}/wp-json/igny8/v1/status` +- Check Content model has `content_html` field populated + +#### 2. Posts Not Syncing Back to IGNY8 + +**Symptoms:** +- Edit post in WordPress, status not updated in IGNY8 +- Post published in WordPress, keyword status not changing in IGNY8 + +**Diagnosis:** +1. Check if post has `_igny8_task_id`: `get_post_meta($post_id, '_igny8_task_id', true)` +2. Check connection enabled: `get_option('igny8_connection_enabled')` +3. Check WordPress error logs for API failures + +**Common Causes:** +- Post not IGNY8-managed (missing `_igny8_task_id`) +- Connection disabled in settings +- Access token expired and refresh failed +- Network/firewall blocking outbound requests + +**Fixes:** +- Only IGNY8-created posts sync (have `_igny8_task_id`) +- Enable connection in Settings → IGNY8 API +- Re-authenticate: delete `igny8_access_token` and `igny8_refresh_token`, save settings again +- Check server can reach `api.igny8.com` + +#### 3. SEO Metadata Not Appearing + +**Symptoms:** +- Meta title/description not in Yoast/RankMath/SEOPress + +**Diagnosis:** +1. Check if generic meta exists: `get_post_meta($post_id, '_igny8_meta_title')` +2. Check SEO plugin-specific meta: `get_post_meta($post_id, '_yoast_wpseo_title')` + +**Common Causes:** +- Content model missing `meta_title` or `meta_description` +- SEO plugin not detected during import + +**Fixes:** +- Ensure Content has `meta_title` and `meta_description` populated +- Function `igny8_import_seo_metadata()` writes to all major SEO plugins +- Manually copy from `_igny8_meta_title` to SEO plugin field + +#### 4. Images Not Importing + +**Symptoms:** +- Featured image not set +- Images in content not downloaded + +**Diagnosis:** +1. Check if `featured_image` in payload +2. Check WordPress upload permissions +3. Check `igny8_import_featured_image()` logs + +**Common Causes:** +- Image URL not accessible (403, 404) +- WordPress upload directory not writable +- `allow_url_fopen` disabled in PHP + +**Fixes:** +- Verify image URLs publicly accessible +- Check `wp-content/uploads/` permissions (775 or 755) +- Enable `allow_url_fopen` in `php.ini` + +#### 5. Duplicate Posts Created + +**Symptoms:** +- Same content published multiple times in WordPress +- Multiple posts with same `_igny8_content_id` + +**Diagnosis:** +1. Check if Celery task has duplicate check +2. Check if `external_id` set on Content after first publish + +**Common Causes:** +- Celery task retrying after network error +- Content.external_id not being saved +- Multiple publish button clicks + +**Fixes:** +- Celery task has duplicate check: `if content.external_id: return` +- Ensure WordPressAdapter saves `external_id` after successful publish +- Disable publish button after first click (frontend) + +### Debug Mode + +**Enable WordPress Debug Logging:** +```php +// wp-config.php +define('WP_DEBUG', true); +define('WP_DEBUG_LOG', true); +define('WP_DEBUG_DISPLAY', false); +define('IGNY8_DEBUG', true); // Plugin-specific +``` + +**Enable Django Logging:** +```python +# settings.py +LOGGING = { + 'version': 1, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + }, + }, + 'loggers': { + 'igny8_core.business.publishing': { + 'handlers': ['console'], + 'level': 'DEBUG', + }, + }, +} +``` + +**Check Logs:** +- WordPress: `wp-content/debug.log` +- Django: Console output or configured log file +- Browser: Console (F12 → Console tab) + +### Health Check Endpoints + +**WordPress Plugin Status:** +```bash +GET https://yoursite.com/wp-json/igny8/v1/status +``` +Response: +```json +{ + "success": true, + "data": { + "plugin_version": "1.0.0", + "connected": true, + "api_key_present": true, + "token_present": true, + "connection_enabled": true + } +} +``` + +**IGNY8 API Health:** +```bash +GET https://api.igny8.com/api/v1/health/ +``` + +### Common WordPress Errors + +**Error:** `IGNY8 API key not authenticated` +- **Cause:** API key missing or invalid +- **Fix:** Settings → IGNY8 API → Enter API key + +**Error:** `IGNY8 connection is disabled` +- **Cause:** Connection toggle disabled in settings +- **Fix:** Settings → IGNY8 API → Enable "Enable Connection" + +**Error:** `Post not found for this content ID` +- **Cause:** WordPress post with `_igny8_content_id` doesn't exist +- **Fix:** Content not yet published to WordPress or meta field missing + +--- + +## Developer Quick Reference + +### Quick Start: New Developer Onboarding + +**1. Understand the Architecture** +- Read this document (WORDPRESS_INTEGRATION_REFERENCE.md) +- Read MASTER_REFERENCE.md for overall IGNY8 architecture +- Review `docs/WORDPRESS-PLUGIN-INTEGRATION.md` for plugin details + +**2. Setup Local Environment** + +**WordPress Plugin:** +```bash +# Clone plugin +cd wp-content/plugins/ +git clone