Skip to main content

Webhook Integration

Learn how to setup a Webhook to send new articles from IndexPilot to your custom website CMS. You can also use our webhook integration for N8N, Zapier and Make.

Updated over a week ago

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

  1. Navigate to Integrations

    • Open your IndexPilot dashboard

    • Click Integrations in the sidebar

    • Select Custom Webhook

  2. Enter Your Webhook URL

    • Enter your endpoint URL (e.g., https://yourdomain.com/webhooks/indexpilot)

    • Click Test Connection to verify it’s reachable

  3. Save Configuration

    • Click Save Configuration

    • IMPORTANT: Copy your webhook secret immediately—it’s only shown once!

    • This secret is required for signature verification

  4. 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)

  5. 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 verification

  • x-indexpilot-timestamp: Unix timestamp of the request

  • content-type: Always application/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

Did this answer your question?