Webhook guide — setup & use
This short guide explains how to connect your website to Smooth Marketing using webhooks. It’s written for non-technical users but includes the important bits developers will need.
What is a webhook?
A webhook is a simple way for our service to send your website updates automatically. When an article is published, we POST the article data to a URL you provide. Your site receives the data and publishes the article automatically.
Quick checklist (what you need)
- A secure URL on your website that accepts POST requests (HTTPS)
- A secret token shared between your site and us (we use it to sign requests)
- Someone who can paste the URL and token into the integration setup (you or a developer)
Step-by-step setup
- Create a webhook endpoint on your site that accepts JSON POSTs at an HTTPS URL. Example path:https://your-site.org/api/webhooks/articles
- Choose a secret token (a long random string). Save it somewhere safe. You will enter this token into our dashboard when creating the webhook integration.
- In the Smooth Marketing dashboard, add a new integration of type Webhook. Paste your URL and the secret token into the form and save.
- Use the dashboard "Test webhook" button. If the test succeeds you will see a 200 response. This confirms the URL and secret are working for the small test payload.
- When an article is published, we will POST a payload to your URL. Make sure your endpoint verifies the signature (details below) and then processes the article data.
Payload example (what you will receive)
{
"event_type": "publish_articles",
"timestamp": "2025-11-06T12:34:56Z",
"data": {
"articles": [
{
"id": "bc12...",
"title": "How to Grow Your Bakery",
"slug": "grow-your-bakery",
"content_markdown": "# Hello",
"content_html": "<h1>Hello</h1>",
"meta_description": "Short summary",
"image_url": null,
"tags": [],
"created_at": "2025-11-06T12:00:00Z"
}
]
}
}How to show the received article on your website
Below are simple, copy-paste examples that show the typical end-to-end flow: 1) receive the webhook, 2) store or parse the payload, and 3) render the article on your site. Pick the example that matches your setup.
Next.js (API route + page)
This example shows a minimal API route that verifies a signature and saves the article JSON to a local file (for demo). In production you would save to your DB or CMS.
// pages/api/webhooks/articles.ts (Next.js)
import { NextApiRequest, NextApiResponse } from 'next'
import { createHmac } from 'crypto'
import fs from 'fs'
const SECRET = process.env.ARTICLES_WEBHOOK_SECRET || 'change-me'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const raw = await new Promise<Buffer>((resolve, reject) => {
const chunks: Buffer[] = []
req.on('data', (c) => chunks.push(Buffer.from(c)))
req.on('end', () => resolve(Buffer.concat(chunks)))
req.on('error', reject)
})
const sig = req.headers['x-smooth-signature'] as string | undefined
const expected = createHmac('sha256', SECRET).update(raw).digest('hex')
if (!sig || sig !== expected) return res.status(401).end('Unauthorized')
const payload = JSON.parse(raw.toString('utf8'))
// For demo: write the first article to a local file (danger: use DB in prod)
const article = payload.data.articles?.[0]
if (article) fs.writeFileSync('./public/_latest_article.json', JSON.stringify(article, null, 2))
return res.status(200).json({ ok: true })
}Render the saved file on a Next.js page:
// app/article/page.tsx (Next.js App router)
import fs from 'fs'
export default function ArticlePage() {
let article = { title: '', content_html: '' }
try { article = JSON.parse(fs.readFileSync('./public/_latest_article.json', 'utf8')) } catch (e) {}
return (
<article className="container mx-auto prose">
<h1>{article.title}</h1>
<div dangerouslySetInnerHTML={{ __html: article.content_html }} />
</article>
)
}
Vanilla JavaScript (example render on any site)
If you receive the webhook and expose the article JSON at /_latest_article.json, this simple script will render it inside a page element.
<div id="article-root"></div>
<script>
fetch('/_latest_article.json').then(r => r.json()).then(article => {
const root = document.getElementById('article-root')
root.innerHTML = '<h1>' + article.title + '</h1>' + article.content_html
})
</script>React (client-side render)
import React, {useEffect, useState} from 'react'
export default function ArticleWidget(){
const [article, setArticle] = useState(null)
useEffect(()=>{ fetch('/_latest_article.json').then(r=>r.json()).then(setArticle) },[])
if(!article) return <div>Loading…</div>
return (<article className="prose"><h1>{article.title}</h1><div dangerouslySetInnerHTML={{__html: article.content_html}}/></article>)
}
Keep in mind:
- For production, store articles in a database (e.g., Supabase) instead of local files.
- Always verify the HMAC signature using the shared secret to ensure requests are genuine.
- Sanitize HTML or use a trusted markdown-to-HTML renderer on the server before rendering to users.
Signature & security (simple)
Each webhook request is signed so you can confirm it really came from Smooth Marketing. We create a signature using the secret token you provided and a standard method called HMAC-SHA256. The signature is sent in the X-Smooth-Signature header.
Your endpoint should:
- Read the raw request body (bytes) and compute HMAC-SHA256 using your secret.
- Compare the result with the X-Smooth-Signature header.
- If they match, accept the request (return 200) and process the article(s). If not, return 401.
Troubleshooting
- 401 Unauthorized: Usually means the signature did not match. Check the secret token you entered in the dashboard and ensure your site uses the same token to verify signatures.
- Empty test vs. real payload: The dashboard test sends a very small payload. Real article payloads are larger — make sure your endpoint verifies the signature against the raw bytes it receives (not a re- serialized JSON object), and that the signing and sending code use the exact same byte sequence.
- 500 Server Error: Check your site logs for parsing errors. Ensure your endpoint accepts JSON and handles missing fields gracefully.
Best practices
- Use HTTPS for your webhook URL. Never accept webhooks over plain HTTP.
- Keep your secret token private — rotate it if you think it was exposed.
- Return a 200 response quickly. If processing takes time, enqueue the article and return 200 immediately.
- Log webhook deliveries and failures so you can diagnose problems quickly.