# IGNY8 Phase 3: WordPress Plugin — Connected Mode (03B) ## IGNY8 SaaS ↔ WordPress Bidirectional Integration **Document Version:** 1.0 **Date:** 2026-03-23 **Phase:** IGNY8 Phase 3 — WordPress Ecosystem **Status:** Build Ready **Source of Truth:** Codebase at `/data/app/igny8/` **Audience:** Claude Code, WordPress Developers, Architects --- ## 1. CURRENT STATE ### Existing WordPress Bridge The IGNY8 WordPress Bridge Plugin v1.5.2 at `/data/app/igny8/plugins/wordpress/source/igny8-wp-bridge/` provides basic content sync: - Content push from IGNY8 → WordPress via `POST /wp-json/igny8/v1/sync/push` - SAG blueprint sync via `POST /wp-json/igny8/v1/sag/sync-blueprint` - Taxonomy creation via `POST /wp-json/igny8/v1/sag/create-taxonomies` - API key authentication using `X-IGNY8-API-KEY` header ### What v2 Connected Mode Replaces Phase 3 connected mode is part of the new v2 plugin (03A). It replaces and extends all v1.5.2 capabilities with: - Full content queue + review workflow (not just raw push) - Image downloading and attachment creation - SAG blueprint → taxonomy + term → cluster hub page mapping - Bidirectional sync events and confirmation callbacks - Schema bulk push from IGNY8 backend (02G) - GSC status sync from IGNY8 backend (02C) - Auto-linking from IGNY8 backend (02D) - Social auto-post from IGNY8 backend (02H) ### Prerequisites - Standalone plugin (03A) must be installed and active - IGNY8 SaaS subscription with valid API key - Connection established via Setup Wizard Step 5 or Settings → Connect ### IGNY8 Backend Models That WordPress Interacts With - `Content` (writer app, db_table=`igny8_content`) — content_type: post/page/product/taxonomy; content_structure choices - `Tasks` (writer app, db_table=`igny8_tasks`) — writing tasks, status: queued/completed - `Images` (writer app) — generated images for content - `Clusters` (planner app, db_table=`igny8_clusters`) — keyword clusters - `SAGBlueprint`, `SAGAttribute`, `SAGCluster` (sag app) — Phase 1 SAG models - `SiteIntegration` (integration app) — WordPress connection details - `SyncEvent` (integration app) — sync operation logs - `PublishingSettings`, `PublishingRecord` (publishing app) - All IDs are integers (BigAutoField) — never UUIDs --- ## 2. WHAT TO BUILD ### Overview Two connected-mode modules that extend the standalone plugin (03A) when an IGNY8 SaaS subscription is active. These modules are only loaded when `igny8_api_connected` option is `true`. ### Module 11: Content Sync **Purpose:** Receive content from IGNY8 SaaS, queue it, download images, map types, create/update WordPress posts, and confirm back. **Classes:** | Class | File | Purpose | |-------|------|---------| | `IGNY8_Sync_Module` | `modules/content-sync/class-sync-module.php` | Module entry, webhook receiver | | `IGNY8_Content_Puller` | `modules/content-sync/class-content-puller.php` | Fetch content from IGNY8 SaaS API | | `IGNY8_Content_Mapper` | `modules/content-sync/class-content-mapper.php` | Map IGNY8 content_type → WP post_type | | `IGNY8_Image_Downloader` | `modules/content-sync/class-image-downloader.php` | Download images → `wp_upload_dir()`, `wp_insert_attachment()` | | `IGNY8_Sync_Queue` | `modules/content-sync/class-sync-queue.php` | Sync queue DB table management | | `IGNY8_Publish_Scheduler` | `modules/content-sync/class-publish-scheduler.php` | Review queue, scheduled publishing | **Content Type Mapping:** | IGNY8 content_type | WP post_type | Notes | |----|----|----| | `post` | `post` | Default blog post | | `page` | `page` | Standard page | | `product` | `product` | Requires WooCommerce active | | `service` | `service` | Requires companion theme CPT (03C) | | `company_page` | `page` | Uses `page-company` template | | `taxonomy_landing` | — | Written to term description (not a post) | | `cluster_hub` | `page` | Landing page preset applied | | `comparison` | `post` | Standard post with comparison structure | | `brand_page` | `page` | Standard page | **Content Sync Flow:** ``` 1. IGNY8 SaaS publishes content 2. → POST /wp-json/igny8/v1/sync/push (validated via X-IGNY8-API-KEY) Payload: {igny8_content_id, content_type, title, content_html, excerpt, featured_image, meta: {focus_keyword, secondary_keywords, cluster_id, taxonomies: {service_category: [...], cluster: [...]}}} 3. → Plugin creates {prefix}igny8_sync_queue entry (status: pending) 4. → WP Cron job (igny8_process_sync_queue) processes queue: a. Download images → wp_upload_dir(), wp_insert_attachment() b. Map content_type → WP post_type via IGNY8_Content_Mapper c. wp_insert_post() or wp_update_post() d. Set post meta (_igny8_content_id, _igny8_cluster_id, _igny8_sync_status) e. wp_set_object_terms() for taxonomy assignment f. Status: 'review' (manual mode) or 'synced' (auto-publish mode) 5. → Plugin POSTs confirmation to IGNY8: POST /api/v1/integration/sync-events/ ``` **Methods:** - `push_content_to_queue($data)` — validate payload, insert into sync queue - `process_sync_queue()` — WP Cron handler, batch process pending items - `map_content_type($igny8_type)` — return WP post_type + template - `create_post_from_content($data)` — `wp_insert_post()` + meta + terms - `download_images($content)` — download to `wp_upload_dir()`, create attachments - `schedule_publish($post_id, $date)` — set future publish date - `get_review_queue()` — list items with status `review` **Post Meta (Connected Mode):** ``` _igny8_content_id -- int: links WP post to IGNY8 Content record _igny8_cluster_id -- int: links WP post to IGNY8 SAGCluster _igny8_sync_status -- string: pending/synced/failed/modified _igny8_last_sync -- string: ISO timestamp _igny8_related_links -- JSON: [{post_id, anchor_text, priority}] _igny8_link_suggestions -- JSON: AI-generated link suggestions from 02D ``` **Hooks:** - Actions: `wp_scheduled_event` (`igny8_process_sync_queue`), `rest_api_init` - Filters: `igny8_content_before_sync`, `igny8_post_mapped`, `igny8_sync_status_updated` ### Module 12: SAG Structure (Blueprint Sync) **Purpose:** Receive SAG blueprint from IGNY8, create WordPress taxonomies and terms matching SAG dimensions and attributes, map clusters to hub pages, and provide structure visualization. **Classes:** | Class | File | Purpose | |-------|------|---------| | `IGNY8_SAG_Module` | `modules/sag/class-sag-module.php` | Module entry | | `IGNY8_Blueprint_Sync` | `modules/sag/class-blueprint-sync.php` | Receive + cache blueprint | | `IGNY8_Taxonomy_Builder` | `modules/sag/class-taxonomy-builder.php` | `register_taxonomy()` from SAG attributes | | `IGNY8_Term_Builder` | `modules/sag/class-term-builder.php` | `wp_insert_term()` with hierarchy | | `IGNY8_Cluster_Manager` | `modules/sag/class-cluster-manager.php` | Map clusters → hub pages/terms | | `IGNY8_Structure_Visualizer` | `modules/sag/class-structure-visualizer.php` | Visual site structure admin page | **SAG Blueprint Sync Flow:** ``` 1. IGNY8 confirms blueprint (01D setup wizard) 2. → POST /wp-json/igny8/v1/sag/sync-blueprint (validated via X-IGNY8-API-KEY) Payload: {blueprint: {dimensions: [{id, name, slug, attributes: [{id, name, slug}]}], clusters: [{id, name, dimension_ids, hub_page_id, recommended_content_count}]}} 3. → Plugin stores in option igny8_sag_blueprint 4. → Admin sees "Blueprint Review" notice 5. → User clicks "Approve Blueprint": a. Loop dimensions → register_taxonomy() if not exists b. Loop attribute values → wp_insert_term() with parent hierarchy c. Set term meta (_igny8_term_sag_attribute, _igny8_term_sag_level) d. Loop clusters → create/link hub pages 6. → POST confirmation back: {term_ids_map, hub_page_ids_map} ``` **Methods:** - `sync_blueprint($json)` — validate, store in option, trigger admin notice - `create_taxonomies_from_blueprint($bp)` — `register_taxonomy()` per dimension - `create_terms_from_blueprint($bp)` — `wp_insert_term()` per attribute value - `map_cluster_to_page($cluster_id, $page_id)` — link SAGCluster to WP hub page - `get_blueprint()` — return cached blueprint from option - `get_structure_overview()` — summary for structure visualizer admin page **Term Meta (Connected Mode):** ``` _igny8_term_content -- string: rich HTML for term landing page _igny8_term_faq -- JSON: [{question, answer}] _igny8_term_related_terms -- JSON: [term_id, term_id, ...] _igny8_term_sag_attribute -- string: attribute name from SAG _igny8_term_sag_level -- string: primary/secondary/tertiary _igny8_term_cluster_id -- int: linked SAGCluster ID _igny8_term_igny8_id -- int: ID in IGNY8 platform _igny8_term_seo_title -- string: SEO title for term archive _igny8_term_meta_description -- string: meta description for term archive _igny8_term_robots_index -- string: index/noindex _igny8_term_og_image -- int: OG image attachment ID ``` --- ## 3. DATA MODELS & APIS ### Database Table The sync queue table is created by 03A's `class-installer.php` (table 6), used here: **`{prefix}igny8_sync_queue`** ```sql CREATE TABLE {prefix}igny8_sync_queue ( id BIGINT AUTO_INCREMENT PRIMARY KEY, igny8_content_id VARCHAR(100) NOT NULL, content_type VARCHAR(50) NOT NULL, sync_status VARCHAR(20) NOT NULL DEFAULT 'pending', wp_post_id BIGINT NULL, wp_term_id BIGINT NULL, data LONGTEXT NULL, created_at DATETIME NOT NULL, synced_at DATETIME NULL, error_message TEXT NULL, INDEX idx_status (sync_status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` ### Options (Connected Mode) | Option Key | Purpose | |------------|---------| | `igny8_api_key` | API key for IGNY8 SaaS (encrypted via `wp_encrypt()` or openssl) | | `igny8_api_connected` | Boolean — is connected mode active | | `igny8_api_url` | Base URL (default: `https://api.igny8.com/api/v1/`) | | `igny8_site_id` | IGNY8 Site record ID (integer) | | `igny8_last_sync` | ISO timestamp of last successful sync | | `igny8_sag_blueprint` | JSON: full blueprint data from IGNY8 | ### API Client (`class-api-client.php`) Outbound requests from WordPress to IGNY8 SaaS API: | Property | Value | |----------|-------| | Base URL | From `igny8_api_url` option (default: `https://api.igny8.com/api/v1/`) | | Auth Header | `Authorization: Bearer {igny8_api_key}` | | Methods | `get($endpoint, $params)`, `post($endpoint, $data)`, `put($endpoint, $data)`, `delete($endpoint)` | | Error Handling | `WP_Error` wrapping, log failures via `error_log()` | | Rate Limiting | Respect 429 responses with exponential backoff (1s → 2s → 4s → 8s) | | Connection Test | `GET /api/v1/system/status/` | **IGNY8 Backend Endpoints Consumed by Plugin:** ``` GET /api/v1/writer/content/?site_id=X&status=approved -- content ready for sync GET /api/v1/writer/content/{id}/ -- single content with HTML, images, taxonomy data GET /api/v1/planner/clusters/?site_id=X -- cluster data GET /api/v1/sag/blueprints/?site_id=X -- SAG blueprint (Phase 1) POST /api/v1/integration/sync-events/ -- log sync results POST /api/v1/publishing/records/ -- log publishing ``` ### REST Endpoints (Inbound from IGNY8) All endpoints validate `X-IGNY8-API-KEY` header. Under `/wp-json/igny8/v1/`: | Method | Endpoint | Purpose | Payload | |--------|----------|---------|---------| | POST | `/sync/push` | Content push from IGNY8 | `{igny8_content_id, content_type, title, content, excerpt, featured_image, meta: {...}}` | | POST | `/sag/sync-blueprint` | Blueprint sync | `{blueprint: {dimensions: [...], clusters: [...]}}` | | POST | `/sag/create-taxonomies` | Taxonomy creation | `{taxonomies: [{name, slug, hierarchical, post_types}]}` | | POST | `/terms/{id}/content` | Term content push | `{term_id, content, faq, related_terms, meta: {...}}` | | POST | `/schema/bulk-update` | Schema JSON-LD bulk push | `{schemas: [{post_id, schema_type, schema_data}]}` | | POST | `/gsc/status-sync` | GSC index statuses | `{statuses: [{url, index_status, coverage_state, last_checked}]}` | | POST | `/event` | Webhook events | `{event_type, data: {...}}` | ### Endpoint Payload Examples **POST `/sync/push`:** ```json { "igny8_content_id": 12345, "content_type": "post", "title": "Content Title", "content": "
HTML content...
", "excerpt": "Short excerpt", "featured_image": "https://cdn.igny8.com/image.jpg", "meta": { "focus_keyword": "main keyword", "secondary_keywords": ["kw2", "kw3"], "cluster_id": 123, "taxonomies": { "service_category": ["emergency-plumbing"], "cluster": ["drain-services"] } } } ``` **POST `/sag/sync-blueprint`:** ```json { "blueprint": { "dimensions": [ { "id": 1, "name": "Service Type", "slug": "service_type", "attributes": [ {"id": 1, "name": "Emergency Plumbing", "slug": "emergency-plumbing"} ] } ], "clusters": [ { "id": 1, "name": "Emergency Drain Cleaning", "dimension_ids": [1, 2], "hub_page_id": null, "recommended_content_count": 12 } ] } } ``` **POST `/terms/{id}/content`:** ```json { "term_id": 1001, "content": "HTML content for term landing page
", "faq": [{"question": "Q1?", "answer": "A1"}], "related_terms": [1002, 1003], "meta": { "seo_title": "Custom title", "meta_description": "Custom description" } } ``` **POST `/schema/bulk-update`:** ```json { "schemas": [ { "post_id": 123, "schema_type": "Article", "schema_data": {"@context": "https://schema.org", "@type": "Article"} } ] } ``` **POST `/gsc/status-sync`:** ```json { "statuses": [ { "url": "https://example.com/post", "index_status": "Indexed", "coverage_state": "Submitted and indexed", "last_checked": "2026-03-22T12:00:00Z" } ] } ``` --- ## 4. IMPLEMENTATION STEPS ### Admin UI Additions (Connected Mode) When connected, two new submenus appear: ``` IGNY8 (top-level) ├── ... (all standalone modules from 03A) ├── Content Sync — Sync queue, review queue, settings, sync log └── SAG Structure — Blueprint review, cluster manager, structure visualizer ``` ### Build Sequence **Phase 8: Content Sync Module (Days 21-24)** 1. Build `IGNY8_Sync_Module` — module entry, register REST endpoints 2. Build `IGNY8_Content_Mapper` — content_type → post_type mapping table 3. Build `IGNY8_Image_Downloader` — download remote images, create WP attachments 4. Build `IGNY8_Sync_Queue` — queue management, status transitions 5. Build `IGNY8_Content_Puller` — fetch content from IGNY8 API 6. Build `IGNY8_Publish_Scheduler` — review queue UI, scheduled publishing 7. Register WP Cron event: `igny8_process_sync_queue` (every 5 minutes) **Phase 9: SAG Structure Module (Days 25-27)** 1. Build `IGNY8_SAG_Module` — module entry, register REST endpoints 2. Build `IGNY8_Blueprint_Sync` — receive, validate, store blueprint 3. Build `IGNY8_Taxonomy_Builder` — `register_taxonomy()` from blueprint dimensions 4. Build `IGNY8_Term_Builder` — `wp_insert_term()` from attribute values 5. Build `IGNY8_Cluster_Manager` — map clusters → hub pages 6. Build `IGNY8_Structure_Visualizer` — admin page with visual site structure 7. Build `class-api-client.php` — outbound IGNY8 API client with rate limiting ### Security Considerations 1. **API Key Validation**: Every inbound REST endpoint validates `X-IGNY8-API-KEY` header against stored `igny8_api_key` option using `hash_equals()` for timing-safe comparison 2. **Payload Sanitization**: All incoming content HTML sanitized via `wp_kses_post()`. All text fields via `sanitize_text_field()`. All URLs via `sanitize_url()`. All integers via `absint()` 3. **API Key Storage**: Encrypted at rest. Never exposed in admin UI after initial entry. Use `wp_options` with obfuscated display (`****...last4`) 4. **Rate Limiting**: API client respects 429 responses. Exponential backoff: 1s → 2s → 4s → 8s max 5. **Nonce Bypass for REST**: Inbound webhooks use API key auth, not nonce. WordPress REST permission callbacks enforce `X-IGNY8-API-KEY` validation --- ## 5. ACCEPTANCE CRITERIA ### Content Sync (Module 11) - [ ] `POST /wp-json/igny8/v1/sync/push` receives content and creates sync queue entry - [ ] WP Cron processes sync queue — creates/updates WordPress posts - [ ] Content type mapping works for all 9 IGNY8 → WP type combinations - [ ] Images downloaded from IGNY8 CDN, created as WP attachments, set as featured - [ ] Post meta set: `_igny8_content_id`, `_igny8_cluster_id`, `_igny8_sync_status` - [ ] Taxonomy terms assigned via `wp_set_object_terms()` - [ ] Review mode: items land in review queue (status `review`) for manual approval - [ ] Auto-publish mode: items publish directly (status `synced`) - [ ] Confirmation callback POSTs to `POST /api/v1/integration/sync-events/` - [ ] Sync queue admin page shows all items with status, errors, retry button ### SAG Structure (Module 12) - [ ] `POST /wp-json/igny8/v1/sag/sync-blueprint` receives and stores blueprint - [ ] Admin sees "Blueprint Review" notice after blueprint sync - [ ] "Approve Blueprint" creates taxonomies from SAG dimensions - [ ] Terms created from attribute values with correct hierarchy - [ ] Term meta set: `_igny8_term_sag_attribute`, `_igny8_term_sag_level`, `_igny8_term_cluster_id` - [ ] Clusters mapped to hub pages - [ ] `POST /wp-json/igny8/v1/terms/{id}/content` pushes rich content to term meta - [ ] `POST /wp-json/igny8/v1/schema/bulk-update` updates schema meta on posts - [ ] `POST /wp-json/igny8/v1/gsc/status-sync` updates GSC status display - [ ] Structure visualizer admin page renders site taxonomy/cluster hierarchy ### API Client - [ ] Connection test via `GET /api/v1/system/status/` returns success - [ ] All outbound requests include `Authorization: Bearer {api_key}` header - [ ] 429 responses trigger exponential backoff (1s → 2s → 4s → 8s) - [ ] Failed requests wrapped in `WP_Error` and logged ### Security - [ ] All inbound REST endpoints validate `X-IGNY8-API-KEY` via `hash_equals()` - [ ] Incoming HTML sanitized via `wp_kses_post()` - [ ] API key stored encrypted, displayed as `****...last4` in admin - [ ] No API key exposed in client-side JavaScript or HTML source --- ## 6. CLAUDE CODE INSTRUCTIONS ### Context Requirements Before starting implementation: 1. Read 03A (standalone plugin) — understand module manager, admin menu, DB tables 2. Read existing WP Bridge v1.5.2 at `/data/app/igny8/plugins/wordpress/source/igny8-wp-bridge/` 3. Read 01A (SAG Data Foundation) for SAGBlueprint, SAGAttribute, SAGCluster models 4. Read 01D (Setup Wizard) for blueprint generation flow that triggers sync 5. Read 02B (Taxonomy Term Content) for term content that pushes via `/terms/{id}/content` 6. Read 01G (SAG Health Monitoring) for health scoring context ### Execution Order ``` Content Sync module (6 files) → SAG Structure module (6 files) → API Client → Admin UI views ``` ### Critical Rules 1. **All IGNY8 IDs are integers** — `igny8_content_id`, `cluster_id`, `term_id` are all integer (BigAutoField), never UUIDs 2. **IGNY8 model names are PLURAL** — Clusters, Keywords, Tasks, ContentIdeas, Images, Content (stays singular) 3. **Connected modules only load when `igny8_api_connected` is `true`** 4. **REST security** — every inbound endpoint validates `X-IGNY8-API-KEY` with `hash_equals()` 5. **No blocking** — all API calls and sync processing via WP Cron, never on page load 6. **Image downloads** — use `wp_remote_get()` for fetching, `wp_handle_sideload()` for attachment creation 7. **Taxonomy registration** — use `register_taxonomy()` with proper `show_in_rest => true` for block editor support 8. **Term creation** — use `wp_insert_term()`, not direct DB insert ### File Creation Order ``` 1. includes/modules/content-sync/class-sync-module.php 2. includes/modules/content-sync/class-content-mapper.php 3. includes/modules/content-sync/class-image-downloader.php 4. includes/modules/content-sync/class-sync-queue.php 5. includes/modules/content-sync/class-content-puller.php 6. includes/modules/content-sync/class-publish-scheduler.php 7. includes/modules/sag/class-sag-module.php 8. includes/modules/sag/class-blueprint-sync.php 9. includes/modules/sag/class-taxonomy-builder.php 10. includes/modules/sag/class-term-builder.php 11. includes/modules/sag/class-cluster-manager.php 12. includes/modules/sag/class-structure-visualizer.php 13. includes/class-api-client.php 14. includes/admin/views/sync-queue.php 15. includes/admin/views/blueprint-review.php 16. includes/admin/views/structure-visualizer.php ``` ### Cross-References | Doc | Relationship | |-----|-------------| | 03A WP Plugin Standalone | Base plugin — module manager, DB tables, admin menu, `igny8()` singleton | | 01A SAG Data Foundation | SAGBlueprint, SAGAttribute, SAGCluster models — data source for Module 12 | | 01B SAG Blueprint Engine | Blueprint generation that produces data synced to WordPress | | 01D Setup Wizard | Blueprint generation triggers `POST /sag/sync-blueprint` to WordPress | | 01G SAG Health Monitoring | Health scoring data available in connected mode intelligence module | | 02B Taxonomy Term Content | Generates term content pushed via `POST /terms/{id}/content` | | 02C GSC Integration | GSC metrics pushed via `POST /gsc/status-sync` | | 02D Linker Internal | SAGLink data feeds connected mode auto-linker in Module 6 | | 02G Rich Schema SERP | Schema data pushed via `POST /schema/bulk-update` | | 02H Socializer | SocialAccount OAuth feeds connected mode auto-poster in Module 8 | | 03C Companion Theme | Theme consumes blueprint data, term meta, cluster navigation |