Custom Webhook Integration
Connect BlogSEO to any platform or custom application using webhooks. Receive article data via HTTP POST requests whenever articles are published.
Custom webhooks allow you to integrate BlogSEO with any system that can receive HTTP requests, including custom CMS platforms, static site generators, headless CMS systems, or your own backend services.
Setting Up Webhook Integration
Navigate to Settings → Integrations in your BlogSEO dashboard, click Custom Webhook, then click Create Webhook. You'll need to configure:
1. Webhook URL
Enter the endpoint URL where BlogSEO will send article data. This must be a publicly accessible HTTPS URL that accepts POST requests.
https://your-app.com/api/webhooks/blogseo
2. Content Format
Choose how you want to receive article content:
- Markdown: Raw markdown content with formatting (recommended for further processing)
- HTML: Pre-converted HTML ready for display
3. Authentication Method
Select how BlogSEO should authenticate requests to your endpoint:
Generated Shared Secret (Recommended)
BlogSEO generates a secure 64-character hex string that is sent with every request in the X-Webhook-Secret header. You'll need to store this secret and verify it on your server.
Custom Header
Use your own authentication header. Specify a custom header name and value (e.g., Authorization: Bearer your-api-key).
Webhook Payload Structure
When an article is published, BlogSEO sends a POST request with the following JSON payload:
{
"article": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"slug": "your-article-title-as-url-slug",
"title": "Your Article Title",
"content": "# Article content in markdown or HTML...",
"format": "markdown",
"published_at": "2024-01-15T10:30:00.000Z",
"main_image_url": "https://storage.blogseo.io/images/article-image.webp"
},
"main_image": {
"url": "https://storage.blogseo.io/images/article-image.webp",
"alt": "Your Article Title"
},
"website": {
"id": "fedcba98-7654-3210-fedc-ba9876543210",
"baseUrl": "https://your-website.com"
},
"timestamp": "2024-01-15T10:30:00.000Z"
}
Payload Fields
| Field | Type | Description |
|---|---|---|
article.id | string (UUID) | Unique identifier for the article |
article.slug | string | URL-friendly slug generated from the title |
article.title | string | The article headline |
article.content | string | Article content in markdown or HTML format |
article.format | string | Either "markdown" or "html" |
article.published_at | string | ISO 8601 timestamp of publication |
article.main_image_url | string | URL of the featured image |
main_image.url | string | Same as article.main_image_url |
main_image.alt | string | Alt text for the image (same as title) |
website.id | string (UUID) | Unique identifier for the website |
website.baseUrl | string | Base URL of your website |
timestamp | string | ISO 8601 timestamp of when the webhook was sent |
Slug
The slug field is an SEO-optimized, URL-friendly string automatically generated from the article title. Use it as the pathname for the page displaying the article content (e.g., /blog/your-article-slug). If you're storing articles in a database, consider indexing this field for better query performance.
Verifying Webhook Requests
Always verify that incoming webhook requests are authentic by checking the shared secret or custom header.
Verifying a BlogSEO webhook request
const signingSecret = request.headers['x-webhook-secret'];
if (signingSecret === process.env.BLOGSEO_WEBHOOK_SECRET) {
// Process request
} else {
throw new Error('Invalid signature');
}
Do not expose your webhook signing secret to the public. In particular, make sure your signing secret is not included in your git history and do not hardcode it in your application code. Use environment variables to store your signing secret securely.
Handling the Webhook Payload
After verifying the request, parse the JSON payload and process the article data.
Processing a BlogSEO webhook payload
// app/api/webhooks/blogseo/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const signingSecret = request.headers.get('x-webhook-secret');
if (signingSecret !== process.env.BLOGSEO_WEBHOOK_SECRET) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const { article, website, timestamp } = await request.json();
console.log(`Received article: ${article.title}`);
console.log(`Slug: ${article.slug}`);
console.log(`Format: ${article.format}`);
console.log(`Image: ${article.main_image_url}`);
// Save to your database, trigger builds, etc.
// await saveArticle(article);
return NextResponse.json({ success: true });
}
Testing Your Webhook
Use the Test button in the webhook configuration dialog to send a test payload to your endpoint. This allows you to verify:
- Your endpoint is accessible
- Authentication is working correctly
- Your code correctly parses the payload
The test payload uses randomly generated UUIDs and sample content, allowing you to validate your integration without publishing a real article.
Response Requirements
Your webhook endpoint should:
- Return a
2xxstatus code (200-299) to indicate success - Respond within 30 seconds
- Return any status code outside
2xxto indicate failure
If your endpoint returns an error or times out, BlogSEO will retry the request automatically.
Use Cases
Custom webhooks are ideal for:
- Static Site Generators: Trigger rebuilds when new content is published (Hugo, Jekyll, Gatsby, Next.js)
- Headless CMS: Push content to Strapi, Sanity, or other headless platforms
- Custom Applications: Integrate with your own backend services or databases
- Notification Systems: Send alerts to Slack, Discord, or email when articles are published
- Content Syndication: Automatically distribute content to multiple platforms
Rendering Articles in React
If you're building a React application and receiving content in markdown format, we recommend using the react-markdown package to render your articles.
Installation
npm install react-markdown
Basic Usage
Create a reusable article component that renders markdown content:
import ReactMarkdown from 'react-markdown';
interface ArticleProps {
title: string;
content: string;
mainImageUrl?: string;
mainImageAlt?: string;
}
export function Article({ title, content, mainImageUrl, mainImageAlt }: ArticleProps) {
return (
<article>
<h1>{title}</h1>
{mainImageUrl && (
<img src={mainImageUrl} alt={mainImageAlt || title} />
)}
<div className="markdown-content">
<ReactMarkdown>{content}</ReactMarkdown>
</div>
</article>
);
}
You'll need to add CSS styles for the .markdown-content class to properly style headings, paragraphs, lists, code blocks, tables, and other markdown elements.
Troubleshooting
Webhook Not Receiving Requests
- Verify your endpoint URL is correct and publicly accessible
- Check that your server accepts POST requests at the specified path
- Ensure your SSL certificate is valid (HTTPS is required)
Authentication Failures
- Confirm you're checking the correct header (
X-Webhook-Secretfor generated secrets) - Verify the secret in your environment matches the one shown in BlogSEO
- Check for trailing whitespace in your environment variables
Timeout Errors
- Ensure your endpoint responds within 30 seconds
- Process heavy operations asynchronously after sending the response
- Consider using a queue system for time-consuming tasks
Payload Parsing Errors
- Verify your endpoint expects
application/jsoncontent type - Check that you're parsing the request body as JSON
- Validate all expected fields exist before accessing them
Tables Not Rendering Correctly
If you're using the HTML format and tables appear unstyled, you'll need to add CSS styles for table elements. Here's example CSS you can use:
.article-content table {
width: 100%;
border-collapse: collapse;
border: 1px solid #d1d5db;
margin-bottom: 1rem;
}
.article-content th {
background-color: #f9fafb;
border: 1px solid #d1d5db;
padding: 0.5rem 1rem;
text-align: left;
font-weight: 600;
}
.article-content td {
border: 1px solid #d1d5db;
padding: 0.5rem 1rem;
}
You may also want to style other HTML elements like headings, lists, blockquotes, and code blocks for consistent rendering.
Security Best Practices
- Always verify the webhook signature before processing
- Use HTTPS for your webhook endpoint
- Store secrets in environment variables, never in code
- Log webhook requests for debugging and auditing
- Validate and sanitize all incoming data before use
Frequently Asked Questions
Q: Can I have multiple webhook endpoints? A: Currently, each BlogSEO website can have one webhook endpoint. Contact support if you need to send to multiple destinations.
Q: What happens if my endpoint is down? A: BlogSEO automatically retries failed requests. If all attempts fail, the article status will be set to "preview" and you can manually retry from your dashboard.
Q: Can I change the webhook URL after creation? A: Yes, you can update your webhook configuration at any time in the Integrations settings.
Q: Can I receive webhooks for article updates, not just new publications? A: Currently, webhooks are sent when articles are published. Republishing an existing article will trigger a new webhook with the updated content.
For additional support, contact our team through the in-app support chat.