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
- Add a Webhook node as your workflow trigger
- Set HTTP Method to POST
- Set Path to
/wordpress-update-request - Enable "Respond Immediately" to prevent timeout issues
- 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
- Add an Anthropic Chat Model node
- Select Claude 3.5 Sonnet model
- Set temperature to 0.1 (low randomness for consistent parsing)
- 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 likebutton_updateorform_field_changeelement_selector: Train Claude with your site's common selector patterns- Add
priorityfield 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
- Add a Code node set to "Run Once for All Items"
- Install required npm packages:
playwright,playwright-core - 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 renderingwaitForSelector: 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
- Add a Supabase node
- Set operation to "Insert"
- Select your
wordpress_changestable - 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:
- Request intake (Nodes 1-3): Webhook receives request, validates format, extracts email/message content
- AI processing (Nodes 4-5): Claude parses request into structured JSON, validates required fields present
- Browser automation (Nodes 6-9): Playwright logs in, navigates, performs edit, captures screenshots
- 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:
- Custom CSS class you added
- Elementor widget type + position (
.elementor-widget-heading:nth-child(2)) - 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:
- 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"}'
- Claude parsing: Review JSON output, verify all fields populated correctly
- Playwright automation: Run with
headless: falsefirst to watch browser actions visually - Screenshot capture: Verify images saved to correct paths and accessible
- 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?
- Download the template: Scroll to the bottom of this article to copy the n8n workflow JSON
- Import to n8n: Go to Workflows → Import from File, paste the JSON
- Configure your services: Add WordPress credentials, Anthropic API key, Supabase connection
- Test with sample data: Send a test webhook request with a simple text update
- 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.
