Phase 1
Infrastructure Setup
Spin up a server and install n8n. This is a one-time setup that takes about 30–45 minutes.
1
Spin up your VPS server (Hetzner CX21)
▾
Cost
Hetzner CX21: €3.79/month (~USD $4.10). DigitalOcean Basic Droplet is $6/month if you prefer.1.1 — Create Hetzner account
- Go to hetzner.com → create account → verify email → add payment method
- Go to Cloud → Projects → New Project → name it "WordPress Automation"
1.2 — Create the server
- Click "Add Server"
- Location: nearest to you (Ash = US, FSN = Europe, SGP = Asia/Middle East)
- Image: Ubuntu 24.04
- Type: CX21 (2 vCPU, 4 GB RAM)
- Add your SSH key or set a root password → click "Create & Buy Now"
1.3 — Connect to your server
ssh root@YOUR_SERVER_IP
1.4 — Install Docker
apt-get update && apt-get upgrade -y apt-get install -y docker.io docker-compose-plugin systemctl enable docker && systemctl start docker
2
Install n8n with Docker
▾
Cost
Free — n8n self-hosted is open source. You only pay for the VPS server in Step 1.- Create the n8n directory:
mkdir -p /opt/n8n && cd /opt/n8n nano docker-compose.yml
- Paste this into the file (replace the password and your domain):
version: '3.8'
services:
n8n:
image: n8nio/n8n
restart: always
ports:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=YourStrongPassword123
- WEBHOOK_URL=https://YOUR_DOMAIN_OR_IP:5678
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:- Press Ctrl+O to save, Ctrl+X to exit, then start n8n:
docker compose up -d
- Wait 30 seconds, then open:
http://YOUR_SERVER_IP:5678 - Log in with the username/password you set above — you should see the n8n editor
Tip
For production, point a domain at your server and set up Nginx + Certbot (free SSL). WhatsApp requires HTTPS webhook URLs. This takes ~20 minutes extra and there are many free guides online for "n8n nginx ssl certbot".
Phase 2
WhatsApp Business API Setup
Connect 360dialog as your WhatsApp provider. This is the bridge between WhatsApp and n8n.
3
Set up 360dialog WhatsApp Business API
▾
Cost
360dialog Starter: USD $7/month flat. Receiving messages is free. Sending the confirmation reply costs ~$0.005 per message under Meta's pricing.Important
The phone number you register must NOT already be active on regular WhatsApp. Use a new SIM card or a landline/VoIP number. UAE numbers work fine.3.1 — Register
- Go to 360dialog.com → click "Get Started" → create business account
- You need a Facebook Business Manager account. If you don't have one, create it at business.facebook.com first
- Connect your Facebook Business Manager to 360dialog when prompted
3.2 — Add your phone number
- In the 360dialog dashboard → Channels → Add Channel
- Enter your phone number → complete OTP verification
- Wait for Meta approval (1–3 business days)
3.3 — Get API key and set webhook
- Once approved, go to "API Key" → copy your key (save it securely)
- Go to "Webhooks" → set the URL to:
https://YOUR_DOMAIN:5678/webhook/whatsapp
- Save the webhook configuration
Phase 3
API Keys & Credentials
Gather the three credentials you'll need inside n8n: Claude, OpenAI, and your WordPress Application Password.
4
Get your Claude API key
▾
Cost
Claude Sonnet 4: $3/1M input tokens + $15/1M output tokens. One blog enrichment call uses ~2,000 tokens = under $0.04 per post. No monthly minimum — pay only what you use.- Go to console.anthropic.com → create account → add payment method
- Go to API Keys → Create Key
- Name it "n8n-blog-automation" → copy the key immediately (you cannot view it again)
- Save it somewhere secure — you'll paste it into n8n in Phase 4
5
Get your OpenAI API key (for DALL·E 3)
▾
Cost
DALL·E 3 Standard quality (1792×1024): USD $0.04 per image. HD quality: $0.08. Standard is sufficient for blog featured images. No monthly minimum.- Go to platform.openai.com → create account → add billing under "Billing"
- Go to API Keys → Create new secret key
- Name it "n8n-image-gen" → copy and save the key
6
Generate WordPress Application Password
▾
Cost
Free — WordPress REST API is built into every WordPress installation v4.7+. No plugin needed.- Log into your WordPress admin panel
- Go to Users → Profile (or Users → All Users → click your admin)
- Scroll down to "Application Passwords"
- In the "New Application Password Name" field type:
n8n-automation - Click "Add New Application Password"
- Copy the password shown immediately — it won't be shown again
Note
Your n8n credentials will be: Username = your WordPress admin username, Password = this application password (copy it exactly, spaces included).
Phase 4
Build the n8n Workflow
Log into your n8n dashboard → click "New Workflow" → name it "WhatsApp Blog Publisher". Then add each node below in order.
7
Node 1 — WhatsApp Webhook Trigger
▾
- Click "+" in the workflow editor → search "Webhook" → select it
- Set HTTP Method to:
POST - Set Path to:
whatsapp - Set Authentication to:
None(360dialog authenticates via API key header) - Copy the generated webhook URL → paste it into your 360dialog Webhooks settings
- Click "Test Webhook" in n8n → send any WhatsApp message to your business number → you'll see the raw payload captured
Tip
After capturing a test payload, click on the Webhook node's output to explore the exact JSON paths. This is what you'll reference in the next node.8
Node 2 — Filter: Validate Sender & File Type
▾
- Add an "IF" node → connect from Webhook node
- Add Condition 1 — Sender phone number:
- Left value:
{{ $json.body.entry[0].changes[0].value.messages[0].from }} - Operation: Equal
- Right value:
971501234567(your authorised sender's number, no + sign) - Add Condition 2 — Message type:
- Left value:
{{ $json.body.entry[0].changes[0].value.messages[0].type }} - Operation: Equal
- Right value:
document - Add Condition 3 — MIME type:
- Left value:
{{ $json.body.entry[0].changes[0].value.messages[0].document.mime_type }} - Operation: Contains
- Right value:
openxmlformats - Set Combine Conditions to:
AND(all three must match) - Connect the True output → next node. Connect False → Error Handler (Step 17)
9
Node 3 — Download the .docx File
▾
- Add an "HTTP Request" node
- Method:
GET - URL:
https://waba.360dialog.io/v1/media/{{ $json.body.entry[0].changes[0].value.messages[0].document.id }}- Under Headers, add:
D360-API-KEY→YOUR_360DIALOG_API_KEY - Under Options, set Response Format to:
File
Important
Setting Response Format to "File" is critical. Without it, the binary content won't be passed correctly to the DOCX parser in the next step.10
Node 4 — Parse the Word Document (mammoth.js)
▾
- Add a "Code" node → set Language to JavaScript
- Paste the following code exactly:
const mammoth = require('mammoth');
const fileData = $input.first().binary.data;
const buffer = Buffer.from(fileData, 'base64');
const htmlResult = await mammoth.convertToHtml({ buffer });
const textResult = await mammoth.extractRawText({ buffer });
return [{
json: {
html_content: htmlResult.value,
plain_text: textResult.value.trim(),
char_count: textResult.value.trim().length
}
}];Cost
Free — mammoth.js is built into n8n's Node.js runtime. No external API call or service needed.11
Node 5 — AI Enrichment via Claude API
▾
5a — The Claude API call
- Add an "HTTP Request" node
- Method:
POST· URL:https://api.anthropic.com/v1/messages - Headers:
Content-Type:application/jsonx-api-key:YOUR_CLAUDE_API_KEYanthropic-version:2023-06-01- Body (JSON mode):
{
"model": "claude-sonnet-4-20250514",
"max_tokens": 800,
"messages": [{
"role": "user",
"content": "Analyse this blog post and return ONLY a valid JSON object with no markdown, no explanation:\n\nBlog content:\n{{ $json.plain_text.substring(0, 3000) }}\n\nReturn this exact JSON structure:\n{\"seo_title\": \"60-char max SEO title\",\"meta_description\": \"155-char max meta description\",\"slug\": \"url-friendly-slug\",\"tags\": [\"tag1\", \"tag2\", \"tag3\"],\"category\": \"most relevant single category\",\"image_prompt\": \"A professional blog featured image showing [describe visual], photorealistic, clean background, suitable for a business blog\",\"excerpt\": \"2-sentence blog excerpt for readers\"}"
}]
}5b — Parse the JSON response
- Add a "Code" node immediately after → paste this:
const raw = $input.first().json.content[0].text;
const cleaned = raw.replace(/```json|```/g, '').trim();
const parsed = JSON.parse(cleaned);
return [{ json: parsed }];12
Node 6 — Generate Featured Image (DALL·E 3)
▾
- Add an "HTTP Request" node
- Method:
POST· URL:https://api.openai.com/v1/images/generations - Headers:
Authorization:Bearer YOUR_OPENAI_API_KEY - Body:
{
"model": "dall-e-3",
"prompt": "{{ $json.image_prompt }}",
"n": 1,
"size": "1792x1024",
"quality": "standard"
}Cost
1792×1024 standard quality = $0.04 per image. This is the ideal aspect ratio for WordPress featured images (landscape 16:9 ish). HD quality would be $0.08 — standard is sufficient.13
Node 7 — Download the Generated Image
▾
- Add an "HTTP Request" node
- Method:
GET - URL:
{{ $json.data[0].url }} - Response Format:
File
Note
OpenAI image URLs expire after 1 hour — download immediately in this node rather than storing the URL for later use.14
Node 8 — Upload Image to WordPress Media Library
▾
- Add an "HTTP Request" node
- Method:
POST - URL:
https://YOURSITE.com/wp-json/wp/v2/media - Authentication: Basic Auth → Username: your WP admin username · Password: your Application Password
- Headers:
Content-Disposition:attachment; filename="featured-{{ $now.toISO() }}.jpg"Content-Type:image/jpeg- Body: Binary → set to the file data from the previous node
Key output
The response contains an id field. This media ID is what you'll reference as featured_media in the next step to attach this image to your post.15
Node 9 — Publish the Blog Post to WordPress
▾
- Add an "HTTP Request" node
- Method:
POST - URL:
https://YOURSITE.com/wp-json/wp/v2/posts - Authentication: Basic Auth (same credentials as Step 14)
- Body (JSON):
{
"title": "{{ $('Parse Claude JSON').item.json.seo_title }}",
"content": "{{ $('Parse DOCX').item.json.html_content }}",
"excerpt": "{{ $('Parse Claude JSON').item.json.excerpt }}",
"slug": "{{ $('Parse Claude JSON').item.json.slug }}",
"status": "publish",
"featured_media": {{ $('Upload Image').item.json.id }},
"meta": {
"_yoast_wpseo_metadesc": "{{ $('Parse Claude JSON').item.json.meta_description }}",
"_yoast_wpseo_title": "{{ $('Parse Claude JSON').item.json.seo_title }}"
}
}Draft vs Publish
Change "status": "publish" to "status": "draft" if you want to review each post before it goes live. You'll get a WhatsApp link to the draft preview instead of the live URL.16
Node 10 — Send WhatsApp Confirmation Reply
▾
- Add an "HTTP Request" node
- Method:
POST· URL:https://waba.360dialog.io/v1/messages - Headers:
D360-API-KEY: your 360dialog API key ·Content-Type:application/json - Body:
{
"messaging_product": "whatsapp",
"to": "{{ $('Webhook').item.json.body.entry[0].changes[0].value.messages[0].from }}",
"type": "text",
"text": {
"body": "✅ Blog published successfully!\n\nTitle: {{ $('Parse Claude JSON').item.json.seo_title }}\n\nURL: {{ $('Publish Post').item.json.link }}"
}
}17
Error Handler Node
▾
- Add a final "HTTP Request" node — this is your error catcher
- Connect the False branch from the Filter node (Step 8) to this node
- In n8n, click each node → look for "On Error" in the settings panel → route all errors to this node
- Configure it the same as Step 16 but send the error message to your admin number:
{
"messaging_product": "whatsapp",
"to": "YOUR_ADMIN_PHONE_NUMBER",
"type": "text",
"text": {
"body": "❌ Blog automation failed!\n\nError: {{ $execution.error.message }}\n\nNode: {{ $execution.error.node.name }}"
}
}- Click "Activate" (toggle top right) to enable the workflow
Testing
Send a .docx file from your authorised WhatsApp number to your business number. Watch the n8n Executions panel — each node turns green as it completes. Full run takes 30–60 seconds. Check WordPress for the new post.
Cost Reference
Full Cost Breakdown
Every cost itemised per blog post and per month at 30 posts. All prices in USD.
Cost per single blog post
Based on typical 800–1,500 word blog post
| Service | Per blog | 30 posts/mo | Notes |
|---|---|---|---|
| Claude API (SEO metadata + image prompt) | $0.04 | $1.20 | ~2,000 tokens per call |
| DALL·E 3 (featured image, 1792×1024) | $0.04 | $1.20 | Standard quality |
| 360dialog (outgoing confirmation message) | $0.005 | $0.15 | Per outgoing WhatsApp msg |
| Hetzner CX21 VPS (amortised) | $0.14 | $4.10 | Fixed cost ÷ 30 posts |
| 360dialog base plan (amortised) | $0.23 | $7.00 | Fixed monthly fee ÷ 30 |
| WordPress REST API | $0.00 | $0.00 | Built into WordPress |
| mammoth.js (.docx parsing) | $0.00 | $0.00 | Runs inside n8n, free |
| TOTAL ESTIMATE | ~$0.46 / blog | ~$13.65 / mo | Self-hosted n8n |
Monthly cost at different volumes
10 posts/mo
$12
per month
$1.20 / post
30 posts/mo
$14
per month
$0.47 / post
60 posts/mo
$16
per month
$0.27 / post
100 posts/mo
$20
per month
$0.20 / post
Key Insight
Fixed costs (VPS + 360dialog base = ~$11/month) dominate at low volume. At 100 posts/month your per-post cost drops to $0.20. The more you publish, the cheaper each post becomes.Platform comparison (30 posts/month)
| Platform | Monthly total | Per post | Verdict |
|---|---|---|---|
| n8n self-hosted (this guide) | $14–20 | $0.47 | Best value |
| n8n Cloud (hosted) | $24–34 | $0.80 | No server mgmt |
| Make (Integromat) | $26–42 | $0.87 | Easier UI |
| Zapier | $50–70 | $1.67 | Most expensive |
Quick Reference
All 17 Steps at a Glance
Complete setup checklist
1
Spin up Hetzner VPS
Ubuntu 24.04 · CX21 · €3.79/mo
2
Install n8n via Docker
docker compose up -d3
Register 360dialog
Connect Facebook Business Manager · get API key
4
Get Claude API key
console.anthropic.com · pay-as-you-go
5
Get OpenAI API key
platform.openai.com · for DALL·E 3
6
WP Application Password
Users → Profile → Application Passwords
7
Webhook Trigger node
Path:
/webhook/whatsapp · Method: POST8
IF Filter node
Validate sender phone + document type + mime
9
Download .docx node
GET from 360dialog media endpoint · Response: File
10
Parse DOCX node
Code node · mammoth.js · outputs html + plain_text
11
Claude API node
POST api.anthropic.com/v1/messages · parse JSON response
12
DALL·E 3 image node
POST api.openai.com/v1/images/generations
13
Download image node
GET from DALL·E returned URL · Response: File
14
Upload to WP Media
POST /wp-json/wp/v2/media · Basic Auth · save ID
15
Publish WP Post
POST /wp-json/wp/v2/posts · status: publish or draft
16
WhatsApp confirmation
POST to 360dialog · send post URL to sender
17
Error handler
Alert admin number with error details
Setup Time
Estimated 4–6 hours total on first run. Most time is waiting for 360dialog WhatsApp approval (1–3 days).Ongoing Work
Near-zero maintenance. Review n8n execution logs monthly. Update n8n Docker image quarterly.