How to Build an AI Agent That Edits WordPress/Elementor Pages Automatically with n8n (Free Template)

How to Build an AI Agent That Edits WordPress/Elementor Pages Automatically with n8n (Free Template)

Managing routine WordPress updates eats developer time. Clients send requests for text changes, image swaps, and alignment fixes. Each takes 10-15 minutes. Multiply that by dozens of requests per week, and you're spending 20+ hours on work that could be automated.

This article teaches you how to build an AI-powered n8n agent that reads client requests, logs into WordPress/Elementor, executes changes automatically, and documents everything with before/after screenshots. You'll get a working n8n workflow JSON template at the end.

The Problem: Manual WordPress Updates Kill Productivity

Every agency and freelancer faces the same bottleneck. Clients send simple requests through email or WhatsApp. "Change this headline." "Swap out that hero image." "Fix the spacing on the pricing section."

Current challenges:

  • Each request requires manual login to WordPress dashboard
  • Finding the correct page and Elementor section takes time
  • Context switching destroys focus (average 23 minutes to regain concentration)
  • No automated documentation of changes made
  • Clients ask "what changed?" and you scramble to remember

Business impact:

  • Time spent: 15-25 hours per week on routine updates
  • Opportunity cost: Lost revenue from high-value work you can't do
  • Client satisfaction: Delays from batching small requests
  • Error rate: Manual work introduces typos and wrong-page edits

The solution isn't hiring more developers. It's building an AI agent that handles routine updates automatically while you focus on complex development work.

The Solution Overview

This n8n workflow creates an AI agent that processes client requests, executes WordPress/Elementor changes through browser automation, and logs everything in a database. The agent uses Claude (Anthropic) to interpret natural language requests, Playwright for browser control, and Supabase for change tracking.

The workflow triggers from email or webhook. Claude extracts exact action steps from client messages. Playwright logs into WordPress, navigates to the correct page, opens Elementor, performs the edit, saves changes, and captures screenshots. Supabase stores before/after images with timestamps.

This approach works because it separates reasoning (Claude) from execution (Playwright). The LLM doesn't control the browser directly. It generates structured instructions that deterministic automation executes. This prevents hallucinations from breaking your production sites.

What You'll Build

This n8n agent automates the complete WordPress update workflow from client request to documented completion.

Component Technology Purpose
Request Intake Webhook/Email Trigger Receives client requests from any source
Request Parser Claude 3.5 Sonnet Extracts page URL, element selector, action type, new content
Browser Automation Playwright via Code Node Logs into WordPress, navigates Elementor, performs edits
Screenshot Capture Playwright Screenshot API Documents before/after states
Change Logging Supabase Stores request details, timestamps, image URLs
Credential Management n8n Credentials Store Securely stores WordPress login, API keys
Error Handling Try/Catch Nodes Retries failed actions, alerts on persistent errors

Key capabilities:

  • Processes text updates, image replacements, spacing adjustments
  • Handles multiple WordPress sites with different credentials
  • Captures before/after screenshots automatically
  • Logs every change with timestamp and requester info
  • Clears WordPress cache after updates
  • Sends confirmation with screenshot links
  • Scales to 50+ requests per day without manual intervention

Prerequisites

Before starting, ensure you have:

  • n8n instance (cloud or self-hosted version 1.0+)
  • Anthropic API key with Claude 3.5 Sonnet access
  • Supabase account with database created
  • WordPress site with admin credentials
  • Elementor Pro installed and activated
  • Basic JavaScript knowledge for Playwright code
  • Understanding of CSS selectors for element targeting

Recommended but optional:

  • Browserless.io account for cloud browser automation (easier than self-hosting)
  • Cloudflare or WP Rocket for cache clearing API
  • Slack or email configured for error notifications

Step 1: Set Up Request Intake and Parsing

The workflow starts when a client sends a request. This phase captures the message and extracts actionable instructions.

Configure Webhook Trigger

  1. Add a Webhook node as your workflow trigger
  2. Set HTTP Method to POST
  3. Set Path to /wordpress-update-request
  4. Enable "Respond Immediately" to prevent timeout issues
  5. Copy the webhook URL for later integration

Node configuration:

{
  "httpMethod": "POST",
  "path": "wordpress-update-request",
  "responseMode": "responseNode",
  "options": {
    "rawBody": true
  }
}

Connect Claude for Request Parsing

  1. Add an Anthropic Chat Model node
  2. Select Claude 3.5 Sonnet model
  3. Set temperature to 0.1 (low randomness for consistent parsing)
  4. Configure the system prompt:
You are a WordPress update assistant. Extract structured information from client requests.

Return JSON with this exact structure:
{
  "page_url": "full WordPress page URL",
  "action_type": "text_update|image_replace|spacing_adjust",
  "element_selector": "CSS selector for target element",
  "new_content": "exact new text or image URL",
  "section_name": "human-readable section description"
}

If information is missing or ambiguous, set field to null and include "clarification_needed" field.

Why this works:
Claude excels at extracting structured data from unstructured text. Setting temperature to 0.1 ensures consistent JSON output. The system prompt defines exact field names that match your Playwright automation code, eliminating translation errors.

Variables to customize:

  • action_type: Add more types like button_update or form_field_change
  • element_selector: Train Claude with your site's common selector patterns
  • Add priority field for request queuing

Step 2: Implement Browser Automation Logic

This phase handles the actual WordPress interaction. Playwright controls a headless browser to perform edits exactly as a human would.

Set Up Playwright Code Node

  1. Add a Code node set to "Run Once for All Items"
  2. Install required npm packages: playwright, playwright-core
  3. Configure browser launch options for headless operation

Core automation script:

const { chromium } = require('playwright');

// Get parsed request data from Claude
const requestData = $input.first().json;

const browser = await chromium.launch({
  headless: true,
  args: ['--no-sandbox', '--disable-setuid-sandbox']
});

const context = await browser.newContext({
  viewport: { width: 1920, height: 1080 }
});

const page = await context.newPage();

// Login to WordPress
await page.goto('https://yoursite.com/wp-admin');
await page.fill('#user_login', $credentials.wordpress.username);
await page.fill('#user_pass', $credentials.wordpress.password);
await page.click('#wp-submit');
await page.waitForNavigation();

// Navigate to target page
await page.goto(requestData.page_url);

// Open Elementor editor
await page.click('#elementor-editor-button');
await page.waitForSelector('.elementor-editor-active');

// Take before screenshot
const beforeScreenshot = await page.screenshot({ 
  fullPage: true,
  path: `/tmp/before_${Date.now()}.png`
});

// Perform the edit based on action_type
if (requestData.action_type === 'text_update') {
  await page.click(requestData.element_selector);
  await page.fill('.elementor-inline-editing', requestData.new_content);
} else if (requestData.action_type === 'image_replace') {
  await page.click(requestData.element_selector);
  await page.click('.elementor-control-media__preview');
  await page.fill('#media-search-input', requestData.new_content);
  await page.click('.attachment:first-child');
  await page.click('.media-button-select');
}

// Save changes
await page.click('#elementor-panel-saver-button-publish');
await page.waitForSelector('.elementor-panel-saver-button-published');

// Take after screenshot
const afterScreenshot = await page.screenshot({ 
  fullPage: true,
  path: `/tmp/after_${Date.now()}.png`
});

await browser.close();

return {
  success: true,
  before_screenshot: beforeScreenshot,
  after_screenshot: afterScreenshot,
  timestamp: new Date().toISOString()
};

Why this approach:
Playwright provides reliable browser automation with built-in waiting mechanisms. Using CSS selectors from Claude's parsed output ensures the script targets the correct elements. Taking screenshots before and after creates an audit trail without manual documentation.

Critical configuration:

  • headless: true: Runs without visible browser (faster, uses less resources)
  • viewport: { width: 1920, height: 1080 }: Ensures consistent rendering
  • waitForSelector: Prevents race conditions where script runs before page loads
  • Error handling: Wrap in try/catch to capture failures without crashing workflow

Step 3: Log Changes to Supabase

Every edit needs documentation. This phase stores request details, screenshots, and timestamps in Supabase for complete change tracking.

Configure Supabase Connection

  1. Add a Supabase node
  2. Set operation to "Insert"
  3. Select your wordpress_changes table
  4. Map fields from previous nodes

Database schema:

CREATE TABLE wordpress_changes (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  request_text TEXT NOT NULL,
  page_url TEXT NOT NULL,
  action_type TEXT NOT NULL,
  element_selector TEXT,
  new_content TEXT,
  before_screenshot_url TEXT,
  after_screenshot_url TEXT,
  requester_email TEXT,
  timestamp TIMESTAMPTZ DEFAULT NOW(),
  status TEXT DEFAULT 'completed'
);

Node field mapping:

  • request_text: {{ $json.webhook_body }}
  • page_url: {{ $json.claude_output.page_url }}
  • action_type: {{ $json.claude_output.action_type }}
  • before_screenshot_url: {{ $json.playwright_output.before_screenshot }}
  • after_screenshot_url: {{ $json.playwright_output.after_screenshot }}
  • requester_email: {{ $json.webhook_body.email }}

Screenshot storage:
Upload screenshots to Supabase Storage before inserting database record. Add a Supabase Storage node before the database insert:

// In Code node before Supabase insert
const fs = require('fs');
const beforeImage = fs.readFileSync('/tmp/before_' + timestamp + '.png');
const afterImage = fs.readFileSync('/tmp/after_' + timestamp + '.png');

// Upload to Supabase Storage via HTTP Request node
// Then reference public URLs in database insert

Workflow Architecture Overview

This workflow consists of 12 nodes organized into 4 main sections:

  1. Request intake (Nodes 1-3): Webhook receives request, validates format, extracts email/message content
  2. AI processing (Nodes 4-5): Claude parses request into structured JSON, validates required fields present
  3. Browser automation (Nodes 6-9): Playwright logs in, navigates, performs edit, captures screenshots
  4. Logging and notification (Nodes 10-12): Supabase stores change record, sends confirmation email with screenshot links

Execution flow:

  • Trigger: Webhook POST request with client message
  • Average run time: 45-90 seconds (depends on page complexity)
  • Key dependencies: WordPress admin access, Elementor editor availability, Supabase database configured

Critical nodes:

  • Anthropic Chat Model: Converts natural language to structured actions - must return valid JSON or workflow fails
  • Code (Playwright): Executes browser automation - handles 80% of workflow complexity
  • Supabase Insert: Creates audit trail - enables compliance and client transparency

The complete n8n workflow JSON template is available at the bottom of this article.

Key Configuration Details

WordPress Credential Security

Store credentials in n8n's built-in credential manager, never in workflow code.

Required fields:

  • Username: WordPress admin username
  • Password: WordPress admin password
  • Site URL: Base WordPress installation URL

Common issues:

  • Two-factor authentication enabled → Disable 2FA for automation account or use application passwords
  • IP restrictions on wp-admin → Whitelist n8n server IP in security plugin
  • Session timeout too short → Increase WordPress session length to 24 hours

Elementor Selector Strategy

Elementor generates dynamic CSS classes. Use stable selectors to prevent breakage.

Best practices:

  • Add custom CSS classes to frequently edited elements (class="editable-hero-headline")
  • Use data attributes for targeting (data-edit-zone="pricing-section")
  • Avoid .elementor-element-[random-id] selectors (change on every edit)

Preferred selector hierarchy:

  1. Custom CSS class you added
  2. Elementor widget type + position (.elementor-widget-heading:nth-child(2))
  3. Text content matching (text="Get Started Today")

Error Handling Configuration

Add a Try/Catch wrapper around the Playwright node:

{
  "try": {
    "nodes": ["Playwright Automation"]
  },
  "catch": {
    "nodes": ["Log Error to Supabase", "Send Alert Email"]
  },
  "options": {
    "maxRetries": 2,
    "retryDelay": 5000
  }
}

Why this approach:
WordPress sites have variable load times. Network issues happen. Elementor occasionally shows loading spinners. Retrying with 5-second delays handles 90% of transient failures without manual intervention.

Testing & Validation

Test each component independently before running end-to-end:

  1. Webhook intake: Send test POST request with curl
curl -X POST https://your-n8n.com/webhook/wordpress-update-request \
  -H "Content-Type: application/json" \
  -d '{"message": "Change homepage headline to New Headline", "email": "client@example.com"}'
  1. Claude parsing: Review JSON output, verify all fields populated correctly
  2. Playwright automation: Run with headless: false first to watch browser actions visually
  3. Screenshot capture: Verify images saved to correct paths and accessible
  4. Supabase logging: Check database records appear with correct data

Common troubleshooting:

Issue Cause Solution
"Element not found" error Selector changed or page not loaded Add await page.waitForTimeout(2000) before selector
Screenshots are blank Viewport too small or element off-screen Increase viewport size to 1920x1080
Login fails silently Credentials wrong or 2FA enabled Check credential values, disable 2FA
Changes don't save Cache not cleared Add cache clearing step after save

Deployment Considerations

Production Deployment Checklist

Area Requirement Why It Matters
Error Handling Retry logic with 2 attempts, 5-second delay Prevents data loss from transient WordPress slowness
Monitoring Supabase query for failed requests every 15 min Detect stuck workflows before clients complain
Credentials Separate WordPress user for automation Isolates automation from human admin actions
Rate Limiting Max 10 requests per hour per site Prevents overwhelming WordPress server
Backup Daily Supabase database export Enables recovery if change log corrupted

Scaling to multiple WordPress sites:

Create a lookup table in Supabase mapping site domains to credentials:

CREATE TABLE wordpress_sites (
  id UUID PRIMARY KEY,
  domain TEXT UNIQUE NOT NULL,
  wp_username TEXT NOT NULL,
  wp_password_encrypted TEXT NOT NULL,
  elementor_version TEXT,
  last_successful_update TIMESTAMPTZ
);

Modify workflow to query this table based on page_url domain, then use retrieved credentials for that specific site.

Real-World Use Cases

Use Case 1: Agency Managing 20+ Client Sites

  • Industry: Digital marketing agency
  • Scale: 200-300 update requests per month
  • Modifications needed: Add client identifier field, multi-site credential lookup, per-client Supabase tables for data isolation

Use Case 2: SaaS Company Updating Product Pages

  • Industry: B2B SaaS
  • Scale: Daily pricing updates, weekly feature announcements
  • Modifications needed: Schedule trigger instead of webhook, batch processing for multiple pages, integration with product database for automatic content sync

Use Case 3: E-commerce Store Seasonal Updates

  • Industry: Online retail
  • Scale: 50+ product pages updated quarterly
  • Modifications needed: CSV import of bulk changes, image optimization before upload, automatic mobile preview screenshots

Customizations & Extensions

Alternative Integrations

Instead of Supabase:

  • Airtable: Better for non-technical team visibility - requires changing Insert node to Airtable API
  • Google Sheets: Simplest option for small teams - use Google Sheets node, slower for >1000 records
  • PostgreSQL: Best for high-volume production - direct SQL queries, requires database credentials

Instead of Claude:

  • GPT-4: Comparable parsing accuracy - change to OpenAI node, costs 2x more per request
  • GPT-3.5 Turbo: 80% accuracy, 10x cheaper - acceptable for simple text updates only
  • Local LLM (Llama 3): Zero API costs - requires self-hosted Ollama, slower response times

Workflow Extensions

Add automated quality checks:

  • After edit, run Lighthouse accessibility scan
  • Check for broken links with HTTP Request nodes
  • Validate image alt text present
  • Nodes needed: +4 (HTTP Request, Function for validation, Conditional, Notification)

Add approval workflow:

  • Before executing edit, send preview to manager
  • Manager clicks approve/reject link
  • Webhook receives response, conditionally executes or cancels
  • Nodes needed: +6 (Email, Webhook Wait, Switch, Update Supabase status)

Scale to handle bulk updates:

  • Accept CSV with 50+ changes
  • Process in batches of 5 to avoid rate limits
  • Add queue management with Redis
  • Performance improvement: Process 50 updates in 15 minutes vs 12+ hours manually

Integration possibilities:

Add This To Get This Complexity
Slack notifications Real-time team alerts with screenshot previews Easy (2 nodes)
Google Analytics Track which pages get edited most frequently Medium (4 nodes)
Figma integration Pull design specs directly into update requests Advanced (8 nodes)
WooCommerce sync Auto-update product pages from inventory system Medium (6 nodes)

Get Started Today

Ready to automate your WordPress update workflow?

  1. Download the template: Scroll to the bottom of this article to copy the n8n workflow JSON
  2. Import to n8n: Go to Workflows → Import from File, paste the JSON
  3. Configure your services: Add WordPress credentials, Anthropic API key, Supabase connection
  4. Test with sample data: Send a test webhook request with a simple text update
  5. Deploy to production: Activate the workflow and share webhook URL with your team

First test request:

{
  "message": "Change the homepage hero headline to 'Welcome to Our New Site'",
  "email": "you@example.com",
  "page_url": "https://yoursite.com/homepage"
}

Need help customizing this workflow for your specific WordPress setup or want to add advanced features like multi-site management? Schedule an intro call with Atherial.

Complete N8N Workflow Template

Copy the JSON below and import it into your N8N instance via Workflows → Import from File

{
  "name": "AI WordPress Elementor Auto-Editor",
  "tags": [],
  "nodes": [
    {
      "id": "webhook-trigger-001",
      "name": "Webhook - Multi-Channel Intake",
      "type": "n8n-nodes-base.webhook",
      "position": [
        240,
        300
      ],
      "webhookId": "elementor-editor-webhook",
      "parameters": {
        "path": "elementor-editor",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "whatsapp-trigger-001",
      "name": "WhatsApp Message Trigger",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "position": [
        240,
        500
      ],
      "webhookId": "whatsapp-elementor",
      "parameters": {
        "options": {},
        "updates": [
          "messages"
        ]
      },
      "typeVersion": 1
    },
    {
      "id": "email-trigger-001",
      "name": "Email Trigger - Client Requests",
      "type": "n8n-nodes-base.emailReadImap",
      "position": [
        240,
        700
      ],
      "parameters": {
        "format": "simple",
        "mailbox": "INBOX",
        "options": {
          "customEmailConfig": "imap.gmail.com:993:true"
        },
        "postProcessAction": "read"
      },
      "credentials": {
        "imap": {
          "id": "1",
          "name": "IMAP account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "normalize-input-001",
      "name": "Normalize Multi-Channel Input",
      "type": "n8n-nodes-base.code",
      "position": [
        460,
        500
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Normalize incoming data from different channels\nconst items = $input.all();\nconst normalizedItems = [];\n\nfor (const item of items) {\n  let clientRequest = {};\n  \n  // Detect source and extract data\n  if (item.json.body) {\n    // Webhook source\n    clientRequest = {\n      source: 'webhook',\n      clientName: item.json.body.clientName || 'Unknown',\n      clientEmail: item.json.body.clientEmail || '',\n      siteUrl: item.json.body.siteUrl || '',\n      requestText: item.json.body.request || '',\n      priority: item.json.body.priority || 'medium',\n      timestamp: new Date().toISOString()\n    };\n  } else if (item.json.messages) {\n    // WhatsApp source\n    const message = item.json.messages[0];\n    clientRequest = {\n      source: 'whatsapp',\n      clientName: item.json.contacts?.[0]?.profile?.name || 'WhatsApp User',\n      clientPhone: message.from,\n      requestText: message.text?.body || '',\n      priority: 'high',\n      timestamp: new Date().toISOString()\n    };\n  } else if (item.json.subject) {\n    // Email source\n    clientRequest = {\n      source: 'email',\n      clientName: item.json.from?.name || 'Email User',\n      clientEmail: item.json.from?.address || '',\n      subject: item.json.subject || '',\n      requestText: item.json.text || item.json.html || '',\n      priority: 'medium',\n      timestamp: new Date().toISOString()\n    };\n  }\n  \n  // Extract site URL from text if not provided\n  if (!clientRequest.siteUrl) {\n    const urlMatch = clientRequest.requestText.match(/https?:\\/\\/[^\\s]+/);\n    clientRequest.siteUrl = urlMatch ? urlMatch[0] : '';\n  }\n  \n  normalizedItems.push({ json: clientRequest });\n}\n\nreturn normalizedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "ai-agent-parser-001",
      "name": "AI Agent - Request Parser",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        680,
        500
      ],
      "parameters": {
        "text": "={{ $json.requestText }}",
        "options": {
          "systemMessage": "You are an expert WordPress Elementor editor assistant. Your task is to analyze client change requests and extract structured information needed to automate the edits.\n\nAnalyze the request and extract:\n1. **target_element**: The specific Elementor widget or section to modify (e.g., 'heading', 'button', 'image', 'text-editor', 'spacer')\n2. **action_type**: What to do ('update_text', 'change_image', 'update_color', 'modify_styling', 'add_element', 'delete_element', 'duplicate_element')\n3. **target_page**: Which page to edit (homepage, about, contact, specific URL)\n4. **changes**: Detailed object describing the exact changes\n5. **selectors**: CSS selectors or Elementor IDs to locate elements\n6. **validation**: What to check to confirm success\n\nBe precise and technical. Return valid JSON format."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "openai-model-001",
      "name": "OpenAI GPT-4O Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        680,
        700
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "GPT-4O"
        },
        "options": {
          "maxTokens": 2000,
          "temperature": 0.3
        }
      },
      "credentials": {
        "openAiApi": {
          "id": "1",
          "name": "OpenAI API"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "structured-parser-001",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        680,
        900
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"target_page\": \"homepage\",\n  \"target_element\": \"heading\",\n  \"action_type\": \"update_text\",\n  \"changes\": {\n    \"new_text\": \"Welcome to Our Site\",\n    \"color\": \"#333333\",\n    \"font_size\": \"48px\"\n  },\n  \"selectors\": {\n    \"element_id\": \"elementor-heading-1234\",\n    \"css_selector\": \".elementor-widget-heading h2\"\n  },\n  \"validation\": \"Text should display 'Welcome to Our Site' in #333333 color\"\n}"
      },
      "typeVersion": 1
    },
    {
      "id": "enrich-data-001",
      "name": "Enrich with WP Credentials",
      "type": "n8n-nodes-base.code",
      "position": [
        900,
        500
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Store WordPress credentials securely from environment or workflow static data\n// In production, use n8n credentials or secure vault\nconst items = $input.all();\nconst enrichedItems = [];\n\nfor (const item of items) {\n  const parsedRequest = typeof item.json.output === 'string' \n    ? JSON.parse(item.json.output) \n    : item.json.output;\n  \n  // Get WordPress credentials for the site\n  // In production: Fetch from secure credential store based on siteUrl\n  const wpCredentials = {\n    username: $env.WP_USERNAME || 'admin',\n    password: $env.WP_PASSWORD || 'secure-password',\n    loginUrl: item.json.siteUrl + '/wp-admin'\n  };\n  \n  // Enrich with metadata\n  const enrichedData = {\n    requestId: `REQ-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,\n    originalRequest: item.json,\n    parsedAction: parsedRequest,\n    wpCredentials: wpCredentials,\n    executionTimestamp: new Date().toISOString(),\n    status: 'pending'\n  };\n  \n  enrichedItems.push({ json: enrichedData });\n}\n\nreturn enrichedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "browser-tool-001",
      "name": "Browser Automation Tool",
      "type": "@n8n/n8n-nodes-langchain.toolCode",
      "position": [
        900,
        700
      ],
      "parameters": {
        "name": "wordpress_browser_automation",
        "jsCode": "// WordPress & Elementor Browser Automation Tool\nconst wpUrl = $input.item.json.originalRequest.siteUrl;\nconst credentials = $input.item.json.wpCredentials;\nconst action = $input.item.json.parsedAction;\n\n// This tool would integrate with Playwright/Puppeteer via HTTP Request\n// or use a dedicated browser automation service like Browserbase\n\nconst automationScript = {\n  task: 'elementor_edit',\n  steps: [\n    {\n      action: 'navigate',\n      url: credentials.loginUrl\n    },\n    {\n      action: 'fill',\n      selector: '#user_login',\n      value: credentials.username\n    },\n    {\n      action: 'fill',\n      selector: '#user_pass',\n      value: credentials.password\n    },\n    {\n      action: 'click',\n      selector: '#wp-submit'\n    },\n    {\n      action: 'screenshot',\n      name: 'before_edit',\n      fullPage: true\n    },\n    {\n      action: 'navigate',\n      url: `${wpUrl}/${action.target_page}?elementor`\n    },\n    {\n      action: 'wait',\n      selector: '.elementor-editor-active',\n      timeout: 10000\n    },\n    {\n      action: 'elementor_edit',\n      elementId: action.selectors.element_id,\n      changes: action.changes\n    },\n    {\n      action: 'screenshot',\n      name: 'after_edit',\n      fullPage: true\n    },\n    {\n      action: 'click',\n      selector: '#elementor-panel-saver-button-publish'\n    }\n  ]\n};\n\nreturn JSON.stringify({\n  automation_ready: true,\n  script: automationScript,\n  estimated_duration: '45 seconds'\n});",
        "language": "javaScript",
        "description": "Automates WordPress login, Elementor navigation, and executes content changes with screenshot capture"
      },
      "typeVersion": 1.3
    },
    {
      "id": "http-browser-001",
      "name": "Execute Browser Automation",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1120,
        500
      ],
      "parameters": {
        "url": "={{ $json.browserAutomationEndpoint || 'https://api.browserbase.com/v1/sessions' }}",
        "method": "POST",
        "options": {
          "timeout": 120000
        },
        "jsonBody": "={{ JSON.stringify($json.parsedAction) }}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.BROWSERBASE_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "process-results-001",
      "name": "Process Automation Results",
      "type": "n8n-nodes-base.code",
      "position": [
        1340,
        500
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Process automation results and prepare for logging\nconst items = $input.all();\nconst processedResults = [];\n\nfor (const item of items) {\n  const automationResult = item.json;\n  \n  const result = {\n    requestId: item.json.requestId,\n    executionStatus: automationResult.success ? 'completed' : 'failed',\n    beforeScreenshot: automationResult.screenshots?.before || null,\n    afterScreenshot: automationResult.screenshots?.after || null,\n    changesApplied: automationResult.changes_applied || [],\n    errorMessage: automationResult.error || null,\n    executionDuration: automationResult.duration_ms || 0,\n    validationPassed: automationResult.validation_passed || false,\n    completedAt: new Date().toISOString(),\n    auditTrail: {\n      clientSource: item.json.originalRequest?.source,\n      clientIdentifier: item.json.originalRequest?.clientEmail || item.json.originalRequest?.clientPhone,\n      originalRequest: item.json.originalRequest?.requestText,\n      aiParsedAction: item.json.parsedAction,\n      wpSite: item.json.originalRequest?.siteUrl,\n      executedBy: 'n8n-automation',\n      ipAddress: automationResult.ip_address || 'n8n-internal'\n    }\n  };\n  \n  processedResults.push({ json: result });\n}\n\nreturn processedResults;"
      },
      "typeVersion": 2
    },
    {
      "id": "postgres-log-001",
      "name": "Log to Database - Audit Trail",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1560,
        500
      ],
      "parameters": {
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "elementor_change_logs"
        },
        "columns": {
          "value": {
            "ip_address": "={{ $json.auditTrail.ipAddress }}",
            "request_id": "={{ $json.requestId }}",
            "executed_by": "={{ $json.auditTrail.executedBy }}",
            "wp_site_url": "={{ $json.auditTrail.wpSite }}",
            "completed_at": "={{ $json.completedAt }}",
            "client_source": "={{ $json.auditTrail.clientSource }}",
            "error_message": "={{ $json.errorMessage }}",
            "parsed_action": "={{ JSON.stringify($json.auditTrail.aiParsedAction) }}",
            "changes_applied": "={{ JSON.stringify($json.changesApplied) }}",
            "execution_status": "={{ $json.executionStatus }}",
            "original_request": "={{ $json.auditTrail.originalRequest }}",
            "client_identifier": "={{ $json.auditTrail.clientIdentifier }}",
            "validation_passed": "={{ $json.validationPassed }}",
            "after_screenshot_url": "={{ $json.afterScreenshot }}",
            "before_screenshot_url": "={{ $json.beforeScreenshot }}",
            "execution_duration_ms": "={{ $json.executionDuration }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "insert"
      },
      "credentials": {
        "postgres": {
          "id": "1",
          "name": "Postgres Audit DB"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "if-success-001",
      "name": "If Execution Successful",
      "type": "n8n-nodes-base.if",
      "position": [
        1780,
        500
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition-001",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.executionStatus }}",
              "rightValue": "completed"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "success-message-001",
      "name": "Generate Success Message",
      "type": "n8n-nodes-base.code",
      "position": [
        2000,
        400
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Generate success notification message\nconst item = $input.item.json;\n\nconst successMessage = {\n  to: item.auditTrail.clientIdentifier,\n  subject: `✅ Elementor Changes Completed - Request ${item.requestId}`,\n  message: `Hello!\n\nYour WordPress Elementor changes have been successfully completed.\n\n📋 Request ID: ${item.requestId}\n🌐 Site: ${item.auditTrail.wpSite}\n⏱️ Completed in: ${(item.executionDuration / 1000).toFixed(2)} seconds\n✅ Validation: ${item.validationPassed ? 'Passed' : 'Review Needed'}\n\n📸 Screenshots:\n- Before: ${item.beforeScreenshot}\n- After: ${item.afterScreenshot}\n\n🔍 Changes Applied:\n${item.changesApplied.map(c => `  - ${c}`).join('\\n')}\n\nThank you for using our automated service!\n\nBest regards,\nAutomated Elementor Editor`,\n  sourceChannel: item.auditTrail.clientSource\n};\n\nreturn { json: successMessage };"
      },
      "typeVersion": 2
    },
    {
      "id": "failure-message-001",
      "name": "Generate Failure Alert",
      "type": "n8n-nodes-base.code",
      "position": [
        2000,
        600
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Generate failure notification message\nconst item = $input.item.json;\n\nconst failureMessage = {\n  to: item.auditTrail.clientIdentifier,\n  subject: `⚠️ Elementor Changes Failed - Request ${item.requestId}`,\n  message: `Hello,\n\nWe encountered an issue while processing your WordPress Elementor change request.\n\n📋 Request ID: ${item.requestId}\n🌐 Site: ${item.auditTrail.wpSite}\n❌ Error: ${item.errorMessage || 'Unknown error occurred'}\n\n📸 Screenshot at failure:\n${item.beforeScreenshot || 'Not available'}\n\nOur team has been notified and will review this request manually.\nWe'll get back to you shortly.\n\nApologies for the inconvenience.\n\nBest regards,\nAutomated Elementor Editor`,\n  sourceChannel: item.auditTrail.clientSource,\n  alertTeam: true\n};\n\nreturn { json: failureMessage };"
      },
      "typeVersion": 2
    },
    {
      "id": "send-notification-001",
      "name": "Send Client Notification",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2220,
        500
      ],
      "parameters": {
        "url": "={{ $json.sourceChannel === 'whatsapp' ? 'https://graph.facebook.com/v18.0/PHONE_ID/messages' : 'https://api.sendgrid.com/v3/mail/send' }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  to: $json.to,\n  message: $json.message,\n  subject: $json.subject\n}) }}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ $json.sourceChannel === 'whatsapp' ? 'Bearer ' + $env.WHATSAPP_TOKEN : 'Bearer ' + $env.SENDGRID_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "webhook-response-001",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2440,
        300
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({\n  success: true,\n  requestId: $('Process Automation Results').item.json.requestId,\n  status: $('Process Automation Results').item.json.executionStatus,\n  message: 'Your Elementor change request has been processed',\n  screenshots: {\n    before: $('Process Automation Results').item.json.beforeScreenshot,\n    after: $('Process Automation Results').item.json.afterScreenshot\n  },\n  timestamp: new Date().toISOString()\n}) }}"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "",
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "saveExecutionProgress": true
  },
  "versionId": "",
  "connections": {
    "OpenAI GPT-4O Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent - Request Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Generate Failure Alert": {
      "main": [
        [
          {
            "node": "Send Client Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Browser Automation Tool": {
      "ai_tool": [
        [
          {
            "node": "AI Agent - Request Parser",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "If Execution Successful": {
      "main": [
        [
          {
            "node": "Generate Success Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Generate Failure Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Success Message": {
      "main": [
        [
          {
            "node": "Send Client Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Client Notification": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent - Request Parser",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "WhatsApp Message Trigger": {
      "main": [
        [
          {
            "node": "Normalize Multi-Channel Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent - Request Parser": {
      "main": [
        [
          {
            "node": "Enrich with WP Credentials",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enrich with WP Credentials": {
      "main": [
        [
          {
            "node": "Execute Browser Automation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Browser Automation": {
      "main": [
        [
          {
            "node": "Process Automation Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Automation Results": {
      "main": [
        [
          {
            "node": "Log to Database - Audit Trail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Database - Audit Trail": {
      "main": [
        [
          {
            "node": "If Execution Successful",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Multi-Channel Input": {
      "main": [
        [
          {
            "node": "AI Agent - Request Parser",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Multi-Channel Intake": {
      "main": [
        [
          {
            "node": "Normalize Multi-Channel Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Trigger - Client Requests": {
      "main": [
        [
          {
            "node": "Normalize Multi-Channel Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "triggerCount": 0
}