RankPill Logo

Integrations

Webhooks

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

  1. Enter Your Webhook URL - Paste the public URL of your endpoint that will receive the article data
  2. Choose Authentication Type:
    • Signature-based (Recommended) - Uses HMAC SHA-256 for cryptographic verification
    • Bearer Token - Simpler authentication with Bearer <token> format
  3. Connect and Save - RankPill will generate a unique Secret Key
  4. Copy Your Secret Key - Store it securely in your application's environment variables (e.g., RANKPILL_WEBHOOK_SECRET)
  5. 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:

  1. In the webhook settings, find the Test Your Webhook section
  2. Click Send Test to send a sample article payload to your endpoint
  3. Review the response - you'll see the status code and any response from your endpoint
  4. 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 signature
  • Authorization header with sha256=<signature> format
  • Provides cryptographic verification that the request hasn't been tampered with

Bearer Token Authentication (Optional):

  • Authorization header with Bearer <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 title
  • content_html - Full article content in HTML format
  • content_markdown - Full article content in Markdown format
  • slug - URL-friendly slug for the article (remains constant for updates)
  • meta_description - SEO meta description
  • status - Always "published" when webhook is triggered
  • featured_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 sent
  • is_republish - true when updating an existing article, false for new articles
  • test - 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.