Webhooks allow you to connect RankPill to any website or service by sending real-time article data to a URL of your choice whenever you publish an article. This is perfect for connecting to platforms we don't natively support, using automation tools like Zapier or Make.com, or building custom integrations with your own code. You can receive article data including title, content (HTML and Markdown), images, meta descriptions, and more.
Common use cases include:
- Custom-built CMS or static site generators (Next.js, Hugo, Jekyll)
- Automation platforms (Zapier, Make.com, n8n)
- Serverless functions (AWS Lambda, Vercel Functions, Cloudflare Workers)
- Notification channels (Discord, Slack, Email)
- Any service that can receive HTTP POST requests
Before you begin, you'll need a public URL endpoint that can receive POST requests with JSON data. This could be a webhook URL from Zapier, a serverless function you've deployed, or an API route in your application.
Setting Up Your Webhook
Navigate to Settings → Integrations in your RankPill dashboard and click on the Webhooks integration.
Configuration
- Enter Your Webhook URL - Paste the public URL of your endpoint that will receive the article data
- Choose Authentication Type:
- Signature-based (Recommended) - Uses HMAC SHA-256 for cryptographic verification
- Bearer Token - Simpler authentication with
Bearer <token>
format
- Connect and Save - RankPill will generate a unique Secret Key
- Copy Your Secret Key - Store it securely in your application's environment variables (e.g.,
RANKPILL_WEBHOOK_SECRET
) - Send a Test Payload - Click the "Send Test" button to verify your endpoint is working correctly
Once configured, RankPill will send a POST request to your webhook URL every time an article is published, containing all the article data in JSON format.
Testing Your Webhook
After connecting your webhook, you can send a test payload to verify everything is working:
- In the webhook settings, find the Test Your Webhook section
- Click Send Test to send a sample article payload to your endpoint
- Review the response - you'll see the status code and any response from your endpoint
- Check your endpoint logs to verify the test data was received correctly
The test payload includes a test: true
field so you can identify and handle test requests differently in your code if needed.
Authentication
Every webhook request includes authentication headers to verify the request is genuinely from RankPill:
Signature-based Authentication (Default):
X-RankPill-Signature
header with HMAC SHA-256 signatureAuthorization
header withsha256=<signature>
format- Provides cryptographic verification that the request hasn't been tampered with
Bearer Token Authentication (Optional):
Authorization
header withBearer <your-secret-key>
- Simpler to implement, ideal for Zapier and similar tools
Verification Example (Next.js)
// /pages/api/rankpill-webhook.js
import crypto from 'crypto';
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method Not Allowed' });
}
const rawBody = await getRawBody(req);
const secret = process.env.RANKPILL_WEBHOOK_SECRET;
// Verify signature from X-RankPill-Signature header
const signature = req.headers['x-rankpill-signature'];
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).json({ message: 'Invalid signature' });
}
// Process the article data
const article = JSON.parse(rawBody);
console.log('Received article:', article.title);
// Handle test payloads
if (article.test) {
console.log('Test payload received - webhook is working!');
return res.status(200).json({ message: 'Test webhook received successfully' });
}
// Add your custom logic here for real articles
// e.g., save to database, create file, trigger workflow
res.status(200).json({ message: 'Webhook received successfully' });
}
// Helper to get raw body
function getRawBody(req) {
return new Promise((resolve, reject) => {
const chunks = [];
req.on('data', chunk => chunks.push(chunk));
req.on('end', () => resolve(Buffer.concat(chunks)));
req.on('error', reject);
});
}
Bearer Token Example (Simple)
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method Not Allowed' });
}
// Verify bearer token
const authHeader = req.headers['authorization'];
const expectedToken = `Bearer ${process.env.RANKPILL_WEBHOOK_SECRET}`;
if (!authHeader || authHeader !== expectedToken) {
return res.status(401).json({ message: 'Invalid authorization' });
}
const article = req.body;
console.log('Received article:', article.title);
// Handle test payloads
if (article.test) {
console.log('Test payload received - webhook is working!');
return res.status(200).json({ message: 'Test webhook received successfully' });
}
// Add your custom logic here for real articles
res.status(200).json({ message: 'Webhook received successfully' });
}
Payload Structure
The webhook sends article data as JSON:
{
"title": "How to Build Backlinks for Better SEO",
"content_html": "<h1>How to Build Backlinks for Better SEO</h1><p>Building high-quality backlinks...</p>",
"content_markdown": "# How to Build Backlinks for Better SEO\n\nBuilding high-quality backlinks...",
"slug": "how-to-build-backlinks-for-better-seo",
"meta_description": "Learn proven strategies to build high-quality backlinks...",
"status": "published",
"featured_image": "https://images.unsplash.com/photo-1516321318423-f06f85e504b3",
"published_url": "https://yourblog.com/blog/how-to-build-backlinks-for-better-seo",
"scheduled_date": null,
"published_at": "2024-03-15T10:30:00Z",
"is_republish": false,
"test": false
}
Field Descriptions:
title
- Article titlecontent_html
- Full article content in HTML formatcontent_markdown
- Full article content in Markdown formatslug
- URL-friendly slug for the article (remains constant for updates)meta_description
- SEO meta descriptionstatus
- Always "published" when webhook is triggeredfeatured_image
- URL to the featured image (if any)published_url
- URL where the article was published (if available)scheduled_date
- Original scheduled date (if article was scheduled)published_at
- Timestamp when the webhook was sentis_republish
-true
when updating an existing article,false
for new articlestest
-true
for test payloads sent via "Send Test" button,false
or omitted for real articles
Handling Article Updates
After publishing an article, you can click the "Update Article" button to push changes to your webhook endpoint. When this happens, RankPill sends the updated article data with is_republish: true
.
Your webhook endpoint should check this field and handle updates appropriately:
Example: Update vs Insert Logic
export default async function handler(req, res) {
// ... authentication code here ...
const article = req.body;
if (article.test) {
return res.status(200).json({ message: 'Test webhook received' });
}
// Check if this is an update or new article
if (article.is_republish) {
// UPDATE existing article by slug
await db.articles.update(
{ slug: article.slug },
{
title: article.title,
content_html: article.content_html,
content_markdown: article.content_markdown,
meta_description: article.meta_description,
featured_image: article.featured_image,
updated_at: new Date()
}
);
console.log('Updated article:', article.slug);
return res.status(200).json({ message: 'Article updated successfully' });
} else {
// INSERT new article
await db.articles.insert({
slug: article.slug,
title: article.title,
content_html: article.content_html,
content_markdown: article.content_markdown,
meta_description: article.meta_description,
featured_image: article.featured_image,
created_at: new Date()
});
console.log('Created article:', article.slug);
return res.status(200).json({ message: 'Article created successfully' });
}
}
Using Upsert (Recommended):
Many databases support "upsert" operations that automatically insert or update:
// Supabase example
await supabase
.from('articles')
.upsert({
slug: article.slug, // Primary key or unique field
title: article.title,
content_html: article.content_html,
// ... other fields
}, {
onConflict: 'slug' // Match by slug
});
// MongoDB example
await collection.updateOne(
{ slug: article.slug },
{ $set: articleData },
{ upsert: true }
);
// Prisma example
await prisma.article.upsert({
where: { slug: article.slug },
update: articleData,
create: articleData
});
Important Notes:
- The
slug
field remains constant across updates (it's locked after publishing) - Use the
slug
to identify which article to update in your database - The
is_republish
flag helps you track whether this is an update or initial publish - Always handle both cases to avoid duplicate article entries
Troubleshooting
401 Unauthorized - Invalid Signature:
- Ensure you're using the raw request body for signature calculation (not parsed JSON)
- Verify your secret key matches exactly what's shown in RankPill settings
- Check that your HMAC implementation uses SHA-256
- Make sure there are no extra characters or encoding issues with the secret
401 Unauthorized - Missing Authorization Header:
- Your endpoint expects Bearer token format
- Change the Authentication Type to "Bearer Token" in RankPill webhook settings
Webhook Not Triggering:
- Verify your webhook URL is publicly accessible
- Check that your endpoint returns a 200 status code
- Ensure your business has the webhook integration configured and connected
- Try publishing a test article to trigger the webhook
Connection Test Failed:
- Make sure your endpoint accepts POST requests
- Verify the URL is correct and publicly accessible (not localhost)
- Check that your server/function is deployed and running
- Review your endpoint logs for errors
Frequently Asked Questions
Q: Can I use webhooks with Zapier?
A: Yes! Zapier provides webhook URLs that can receive POST requests. Set up a "Webhooks by Zapier" trigger with "Catch Hook", use the provided URL in RankPill, and choose "Bearer Token" authentication for simplest setup.
Q: Do I need to write code to use webhooks?
A: Not necessarily. Tools like Zapier, Make.com, and n8n provide no-code webhook receivers. However, for custom integrations with your own website or CMS, you'll need to write code to handle the webhook.
Q: What happens if my webhook endpoint is down?
A: If your endpoint doesn't respond with a 200 status code, RankPill will show an error. Webhooks are not automatically retried, so you'll need to manually click "Update Article" once your endpoint is fixed to resend the data.
Q: Can I update articles after publishing?
A: Yes! After publishing an article, you can make edits and click the "Update Article" button to push the changes to your webhook endpoint. The webhook will receive the updated data with is_republish: true
so you can update the existing article in your database instead of creating a duplicate.
Q: Can I have multiple webhook URLs?
A: Currently, each RankPill business can have one webhook URL configured. If you need to send data to multiple endpoints, your webhook handler can forward the data to other services.
Q: Is the webhook data secure?
A: Yes. We use HMAC SHA-256 signatures to verify that requests are genuinely from RankPill and haven't been tampered with. Always verify the signature in your endpoint to ensure security.
Q: What's the difference between signature and bearer token authentication?
A: Signature authentication is more secure (cryptographic verification) but requires more code. Bearer token is simpler (just compare the token) and works better with no-code tools like Zapier. Choose based on your technical needs.
Q: Can I test my webhook without publishing an article?
A: Yes! After connecting your webhook, use the "Send Test" button in the RankPill webhook settings to send a test article payload to your endpoint. This helps you verify that your endpoint is configured correctly and can receive article data. The test payload includes a test: true
flag so you can identify test requests in your code.
Q: Does the webhook include images?
A: Yes! The webhook includes the featured_image
URL and any images in the content are already embedded in the content_html
field with their URLs.
For additional support, contact our team through the in-app support chat.