54 KiB
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
- Integration Overview
- Architecture
- WordPress Plugin Structure
- IGNY8 Backend Integration Points
- Authentication & Security
- Bidirectional Sync: Data Elements
- Publishing Flow (IGNY8 → WordPress)
- Sync Flow (WordPress → IGNY8)
- REST API Endpoints
- Data Models & Meta Fields
- Status Mapping
- Troubleshooting Guide
- 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 tokensrefresh_token()- Refresh expired access tokenget($endpoint)- GET request with auto-retry on 401post($endpoint, $data)- POST requestput($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-KEYheader 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)
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)
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)
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:
- Lookup Content by ID
- For each destination → call
_publish_to_destination() - Create PublishingRecord
- Get adapter (WordPressAdapter)
- Get SiteIntegration config
- Call adapter.publish()
- 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:
{ '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_keyoption (encrypted if available) - Header:
X-IGNY8-API-KEY: {api_key}orAuthorization: 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']andpassword - Flow: Uses standard WordPress REST API authentication
- Limitation: Requires Application Password or JWT plugin
WordPress → IGNY8 Authentication
JWT Token Flow
- Login:
POST /api/v1/auth/login/with{email, password} - Response:
{access, refresh}tokens - Storage:
igny8_access_tokenandigny8_refresh_tokenWordPress options - Usage:
Authorization: Bearer {access_token}header - Refresh: On 401, call
POST /api/v1/auth/refresh/with{refresh} - Auto-retry:
Igny8APIclass automatically refreshes and retries on 401
Credentials Required (WordPress Settings)
- Email:
igny8_emailoption - Password: Stored temporarily for token refresh
- API Key:
igny8_api_keyoption (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_idigny8_clusters taxonomy |
Both meta and taxonomy term |
sector.id |
_igny8_sector_idigny8_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:
// 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_idmeta (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:
{
"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:
- Load Content from database
- For each destination, call
_publish_to_destination() - Create
PublishingRecord(status='pending') - Get adapter for destination
- Lookup
SiteIntegrationfor site - Merge config:
{**config_json, **credentials_json, 'site_url': ...} - Call
adapter.publish(content, config) - 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:
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:
{ "content_id": 123, "title": "Post Title", "content_html": "<h2>Introduction</h2><p>...</p>", "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:
- Resolve post type from content_type
- Map author (via
igny8_map_content_author()) - Prepare post data
wp_insert_post()- Create WordPress post- Import SEO metadata (
igny8_import_seo_metadata()) - Import featured image (
igny8_import_featured_image()) - Import taxonomies (
igny8_import_taxonomies()) - Set cluster/sector taxonomy terms
- Set categories/tags
- Store IGNY8 meta fields
- 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:
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:
- Load Content and SiteIntegration from DB
- Check if already published (has external_id)
- Prepare payload
- POST to WordPress API
- Update Content.external_id and external_url
- 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:
- Check if connection enabled
- Skip autosaves and revisions
- Check if IGNY8-managed (has
_igny8_task_idmeta) - Get
$task_idfrom post meta - Get WordPress
post_status - Map to IGNY8 status:
igny8_map_wp_status_to_igny8($post_status) - Prepare update payload:
[ '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 ] PUT /writer/tasks/{task_id}/via Igny8API- Update
_igny8_wordpress_statusand_igny8_last_syncedmeta
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:
- Get task_id from post meta
- Get task details:
GET /writer/tasks/{task_id}/ - Extract cluster_id and keyword_ids
- Update keyword statuses:
PUT /planner/keywords/bulk-update/ - 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-KEYheader orAuthorization: Bearer {api_key} - Validation:
Igny8RestAPI::check_permission()compares withigny8_api_keyoption
Data Models & Meta Fields
WordPress Post Meta Taxonomy
All IGNY8 meta fields use _igny8_ prefix
// 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:
// 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
// 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:
-
Check browser console:
[Review.handlePublishSingle] Starting publish for content: {id, title} [Review.handlePublishSingle] Calling API endpoint /v1/publisher/publish/ -
Check Django logs:
[PublisherViewSet.publish] 🚀 Publish request received: content_id=X [PublisherService.publish_content] 📄 Content found: title='...' [WordPressAdapter.publish] 🎬 Starting WordPress publish -
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_urlnot set → CheckSiteIntegration.config_json['site_url'] - ❌ Content missing
content_html→ Content.content_html is empty
Fixes:
- Ensure SiteIntegration has both
config_json['site_url']andcredentials_json['api_key'] - Verify WordPress endpoint accessible:
GET {site_url}/wp-json/igny8/v1/status - Check Content model has
content_htmlfield 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:
- Check if post has
_igny8_task_id:get_post_meta($post_id, '_igny8_task_id', true) - Check connection enabled:
get_option('igny8_connection_enabled') - 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_tokenandigny8_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:
- Check if generic meta exists:
get_post_meta($post_id, '_igny8_meta_title') - Check SEO plugin-specific meta:
get_post_meta($post_id, '_yoast_wpseo_title')
Common Causes:
- Content model missing
meta_titleormeta_description - SEO plugin not detected during import
Fixes:
- Ensure Content has
meta_titleandmeta_descriptionpopulated - Function
igny8_import_seo_metadata()writes to all major SEO plugins - Manually copy from
_igny8_meta_titleto SEO plugin field
4. Images Not Importing
Symptoms:
- Featured image not set
- Images in content not downloaded
Diagnosis:
- Check if
featured_imagein payload - Check WordPress upload permissions
- Check
igny8_import_featured_image()logs
Common Causes:
- Image URL not accessible (403, 404)
- WordPress upload directory not writable
allow_url_fopendisabled in PHP
Fixes:
- Verify image URLs publicly accessible
- Check
wp-content/uploads/permissions (775 or 755) - Enable
allow_url_fopeninphp.ini
5. Duplicate Posts Created
Symptoms:
- Same content published multiple times in WordPress
- Multiple posts with same
_igny8_content_id
Diagnosis:
- Check if Celery task has duplicate check
- Check if
external_idset 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_idafter successful publish - Disable publish button after first click (frontend)
Debug Mode
Enable WordPress Debug Logging:
// 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:
# 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:
GET https://yoursite.com/wp-json/igny8/v1/status
Response:
{
"success": true,
"data": {
"plugin_version": "1.0.0",
"connected": true,
"api_key_present": true,
"token_present": true,
"connection_enabled": true
}
}
IGNY8 API Health:
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_iddoesn'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.mdfor plugin details
2. Setup Local Environment
WordPress Plugin:
# Clone plugin
cd wp-content/plugins/
git clone <repo> igny8-bridge
# Activate plugin in WordPress admin
# Go to Settings → IGNY8 API
# Enter credentials: Email, Password, API Key
IGNY8 Backend:
cd igny8/backend
pip install -r requirements.txt
python manage.py migrate
python manage.py runserver
# Start Celery (for async tasks)
celery -A igny8_core worker -l info
3. Test Publishing Flow
# 1. Create Content in IGNY8
# 2. Click Publish in Review page
# 3. Check browser console for logs
# 4. Check Django logs for backend flow
# 5. Check WordPress for created post
# 6. Edit post in WordPress
# 7. Check IGNY8 for updated status
Key Files to Modify
To Change Publishing Payload:
igny8_core/business/publishing/services/adapters/wordpress_adapter.py(IGNY8)sync/igny8-to-wp.php→igny8_create_wordpress_post_from_task()(WordPress)
To Add New Meta Field:
wordpress_adapter.py→ Add to payloadigny8-to-wp.php→ Add tometa_inputarray- Update this document's meta fields table
To Add New REST Endpoint:
includes/class-igny8-rest-api.php→register_routes()- Add permission callback
- Add handler method
To Change Status Mapping:
sync/igny8-to-wp.php→igny8_map_igny8_status_to_wp()sync/post-sync.php→igny8_map_wp_status_to_igny8()
Testing Checklist
IGNY8 → WordPress Publishing
- Create content in IGNY8 Writer
- Content has title, content_html, cluster
- Click Publish button
- Verify post created in WordPress
- Check post has correct title and content
- Verify SEO meta fields populated
- Check featured image attached
- Verify categories/tags assigned
- Confirm cluster/sector taxonomies set
- Check all
_igny8_*meta fields present
WordPress → IGNY8 sync
- Create IGNY8 content and publish to WP
- Edit post in WordPress (change status to draft)
- Check IGNY8 task status updated to 'draft'
- Publish post in WordPress
- Check IGNY8 task status updated to 'completed'
- Verify
wordpress_statusfield shows 'publish'
Site Metadata Discovery
- Call
GET /wp-json/igny8/v1/site-metadata/ - Verify post types listed
- Check taxonomies included
- Confirm counts accurate
- Check WooCommerce detected (if installed)
Code Snippets
WordPress: Get IGNY8 Content ID from Post
$content_id = get_post_meta($post_id, '_igny8_content_id', true);
if ($content_id) {
echo "This post is IGNY8 Content ID: " . $content_id;
}
WordPress: Check if Post is IGNY8-Managed
function is_igny8_post($post_id) {
$task_id = get_post_meta($post_id, '_igny8_task_id', true);
return !empty($task_id);
}
IGNY8: Get WordPress Post ID from Content
from igny8_core.business.content.models import Content
content = Content.objects.get(id=123)
if content.external_id:
print(f"WordPress Post ID: {content.external_id}")
print(f"WordPress URL: {content.external_url}")
IGNY8: Manually Trigger Publish
from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress
# Queue Celery task
publish_content_to_wordpress.delay(
content_id=123,
site_integration_id=1
)
WordPress: Call IGNY8 API
$api = new Igny8API();
$response = $api->get('/writer/tasks/123/');
if ($response['success']) {
$task = $response['data'];
echo $task['title'];
}
Useful Queries
WordPress: Find all IGNY8-managed posts
SELECT p.ID, p.post_title, pm.meta_value as content_id
FROM wp_posts p
INNER JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE pm.meta_key = '_igny8_content_id'
ORDER BY p.ID DESC;
IGNY8: Find all content published to WordPress
SELECT id, title, external_id, external_url, sync_status
FROM igny8_content
WHERE external_id IS NOT NULL
ORDER BY updated_at DESC;
WordPress: Find posts missing IGNY8 sync
SELECT p.ID, p.post_title
FROM wp_posts p
LEFT JOIN wp_postmeta pm ON p.ID = pm.post_id AND pm.meta_key = '_igny8_content_id'
WHERE p.post_type = 'post'
AND p.post_status = 'publish'
AND pm.meta_value IS NULL;
Integration Roadmap
Phase 1: Core Publishing (✅ Complete)
- IGNY8 → WordPress content publishing
- WordPress → IGNY8 status sync
- Authentication (JWT + API Key)
- SEO metadata sync
- Featured images
- Taxonomies (categories, tags, clusters, sectors)
Phase 2: Enhanced Sync (🔄 In Progress)
- Site metadata discovery
- Automated content import from WordPress
- Bulk publishing
- Scheduled publishing
- Content update sync (detect WordPress edits)
Phase 3: Advanced Features (📋 Planned)
- Internal linking recommendations
- Content optimization scoring
- Link graph sync
- Automated interlinking
- Content analytics sync
Phase 4: Automation (📋 Planned)
- Webhooks (IGNY8 → WordPress events)
- WP-CLI commands
- Automated content refresh
- SEO score monitoring
- Broken link detection
Appendix: Data Flow Diagrams
Complete Publishing Architecture
┌────────────────────────────────────────────────────────────────┐
│ IGNY8 SaaS Platform │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Frontend (React) │ │
│ │ ──────────────── │ │
│ │ Review.tsx → Publish Button │ │
│ │ POST /api/v1/publisher/publish/ │ │
│ │ {content_id: 123, destinations: ['wordpress']} │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ API Layer (Django REST Framework) │ │
│ │ ────────────────────────────────── │ │
│ │ PublisherViewSet.publish() │ │
│ │ - Validate request │ │
│ │ - Call PublisherService │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Business Logic (Services) │ │
│ │ ───────────────────────────── │ │
│ │ PublisherService.publish_content() │ │
│ │ 1. Load Content from DB │ │
│ │ 2. Create PublishingRecord │ │
│ │ 3. Get SiteIntegration config │ │
│ │ 4. Call WordPressAdapter │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Adapter Layer (Platform-Specific) │ │
│ │ ────────────────────────────────── │ │
│ │ WordPressAdapter.publish() │ │
│ │ - Detect auth method (API key vs user/pass) │ │
│ │ - Prepare payload │ │
│ │ - POST to WordPress REST API │ │
│ │ - Handle response │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ │ HTTPS Request │
│ │ POST /wp-json/igny8/v1/publish-content/ │
│ │ X-IGNY8-API-KEY: *** │
└───────────────────────┼─────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ WordPress Site (yoursite.com) │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ WordPress REST API │ │
│ │ ────────────────── │ │
│ │ /wp-json/igny8/v1/publish-content/ │ │
│ │ - Validate API key │ │
│ │ - Parse request │ │
│ │ - Call content creation handler │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Content Handler (PHP) │ │
│ │ ───────────────────────── │ │
│ │ igny8_create_wordpress_post_from_task() │ │
│ │ 1. Resolve post type │ │
│ │ 2. Prepare post data │ │
│ │ 3. wp_insert_post() │ │
│ │ 4. Import SEO metadata │ │
│ │ 5. Import featured image │ │
│ │ 6. Set taxonomies │ │
│ │ 7. Store IGNY8 meta fields │ │
│ └────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ WordPress Database │ │
│ │ ────────────────── │ │
│ │ wp_posts │ │
│ │ - ID, post_title, post_content, post_status │ │
│ │ │ │
│ │ wp_postmeta │ │
│ │ - _igny8_content_id, _igny8_task_id, _igny8_cluster_id │ │
│ │ - _igny8_meta_title, _igny8_meta_description │ │
│ │ │ │
│ │ wp_term_relationships │ │
│ │ - Categories, Tags, Clusters, Sectors │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
End of Document
For questions or issues, consult:
- Main documentation:
/docs/MASTER_REFERENCE.md - Plugin docs:
/igny8-wp-integration/docs/WORDPRESS-PLUGIN-INTEGRATION.md - Publishing fixes:
/igny8/FIXES-PUBLISH-FAILURE.md - GitHub Issues: Repository Issues