Webhooks allow IndexPilot to automatically send your generated articles to any custom platform or service in real time. Think of it as a notification system that delivers article data to your server whenever an article is created, published, or updated.
When Should You Use Webhooks?
Custom CMS Integration: Connect to platforms not natively supported (Ghost, Medium, Strapi, etc.)
Workflow Automation: Trigger custom workflows when articles are ready
Multi-Platform Publishing: Send articles to multiple destinations simultaneously
Data Processing: Process article data through your own systems
Notifications: Alert your team when new content is available
🚀 Quick Start Guide
Step 1: Create Your Webhook Endpoint
You’ll need a publicly accessible HTTPS endpoint that can receive POST
requests from IndexPilot.
Example Node.js/Express Endpoint:
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
app.post('/webhooks/indexpilot', (req, res) => {
const signature = req.headers['x-indexpilot-signature'];
const timestamp = req.headers['x-indexpilot-timestamp'];
const webhookSecret = process.env.INDEXPILOT_WEBHOOK_SECRET;
// Verify the webhook
if (!verifySignature(req.body, signature, webhookSecret, timestamp)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process the article
console.log('New article:', req.body.article.title);
console.log('Event type:', req.body.event);
// Add your custom logic here:
// - Save to your database
// - Publish to your CMS
// - Send notifications
// - etc.
res.status(200).json({ success: true });
});
app.listen(3000);
Key Requirements:
✅ Must use HTTPS (in production)
✅ Must be publicly accessible
✅ Must respond within 30 seconds
✅ Must return a
2xx
status code for success✅ Should verify webhook signatures for security
Step 2: Configure Your Webhook in IndexPilot
Navigate to Integrations
Open your IndexPilot dashboard
Click Integrations in the sidebar
Select Custom Webhook
Enter Your Webhook URL
Enter your endpoint URL (e.g.,
https://yourdomain.com/webhooks/indexpilot
)Click Test Connection to verify it’s reachable
Save Configuration
Click Save Configuration
IMPORTANT: Copy your webhook secret immediately—it’s only shown once!
This secret is required for signature verification
Configure Event Triggers (Settings Tab)
Choose which events trigger webhooks:
article.ready
– Article generation completed ✅ (Recommended)article.published
– Article published to CMS ✅ (Recommended)article.updated
– Article content updated ⚠️ (Optional)
Adjust Delivery Settings (Settings Tab)
Verify SSL: Keep enabled for security (disable only for testing)
Max Retries: 3 (recommended) – automatic retries on failure
Timeout: 30 seconds (recommended) – wait time for your server
Step 3: Verify Webhook Signatures (Critical!)
Never trust incoming webhooks without signature verification. This ensures the request is from IndexPilot and prevents spoofed requests.
Node.js Verification Example:
const crypto = require('crypto');
function verifySignature(payload, signature, secret, timestamp) {
const now = Date.now();
const requestTime = parseInt(timestamp, 10);
const fiveMinutes = 5 * 60 * 1000;
if (Math.abs(now - requestTime) > fiveMinutes) {
console.error('Webhook timestamp too old or in future');
return false;
}
const payloadString = JSON.stringify(payload);
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payloadString)
.digest('hex');
const providedSignature = signature.replace('sha256=', '');
try {
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(providedSignature, 'hex')
);
} catch (e) {
return false;
}
}
Python Verification Example:
import hmac
import hashlib
import time
import json
def verify_signature(payload, signature, secret, timestamp):
now = int(time.time() * 1000)
request_time = int(timestamp)
five_minutes = 5 * 60 * 1000
if abs(now - request_time) > five_minutes:
return False
payload_string = json.dumps(payload, separators=(',', ':'))
expected_signature = hmac.new(
secret.encode('utf-8'),
payload_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
provided_signature = signature.replace('sha256=', '')
return hmac.compare_digest(expected_signature, provided_signature)
📦 Webhook Payload Structure
Every webhook request includes structured JSON:
{
"event_id": "9d4e3f9dfb04a7c2...",
"event": "article.ready",
"timestamp": "2025-09-30T12:00:00Z",
"article": {
"id": "abc123xyz",
"slug": "how-to-optimize-seo",
"title": "How To Optimize SEO In 2025",
"content": "<p>Full HTML content...</p>",
"summary": "Learn the latest SEO optimization techniques...",
"seo_title": "How To Optimize SEO In 2025: Complete Guide",
"seo_meta_description": "Discover proven SEO optimization strategies...",
"target_keyword": "optimize seo",
"main_image_url": "https://storage.indexpilot.ai/...",
"thumbnail_image_url": "https://storage.indexpilot.ai/...",
"article_type": "explainer",
"category": "SEO",
"author_name": "John Doe",
"read_time_minutes": 8,
"is_featured": false,
"published_at": "2025-09-30T12:00:00Z",
"created_at": "2025-09-30T11:00:00Z",
"updated_at": "2025-09-30T11:55:00Z"
},
"site": {
"id": "site_abc123",
"name": "My Awesome Blog",
"host": "blog.example.com"
}
}
Webhook Headers
Each request contains important headers:
x-indexpilot-signature
: HMAC-SHA256 signature for verificationx-indexpilot-timestamp
: Unix timestamp of the requestcontent-type
: Alwaysapplication/json
Next Steps:
Test your webhook endpoint with the Test Connection button
Monitor your logs to confirm delivery
Implement retries and idempotency using
event_id