ghost-webhooks
Ghost Webhooks & Integrations
Overview
Ghost webhooks enable event-driven integrations by sending HTTP POST requests to configured URLs when specific events occur. Combined with the Content and Admin API SDKs, webhooks form the foundation for custom integrations, static site generation triggers, notification systems, and content automation pipelines.
When to Use
- Setting up webhooks to react to Ghost events (publishing, member signup, etc.)
- Building custom integrations with Ghost
- Triggering static site rebuilds on content changes
- Sending notifications (Slack, Discord, email) on Ghost events
- Automating content workflows (cross-posting, syndication)
- Building headless CMS frontends with Ghost as the backend
- Migrating content to or from Ghost
Webhook Events
Ghost supports 31 webhook events across 5 categories:
Site Events
| Event | Trigger |
|---|---|
site.changed |
Any content or settings change |
Post Events (10)
| Event | Trigger |
|---|---|
post.added |
Post created |
post.deleted |
Post deleted |
post.edited |
Post edited |
post.published |
Post published |
post.published.edited |
Published post edited |
post.unpublished |
Post unpublished (reverted to draft) |
post.scheduled |
Post scheduled for future publication |
post.unscheduled |
Scheduled post unscheduled |
post.rescheduled |
Scheduled post rescheduled |
Page Events (10)
| Event | Trigger |
|---|---|
page.added |
Page created |
page.deleted |
Page deleted |
page.edited |
Page edited |
page.published |
Page published |
page.published.edited |
Published page edited |
page.unpublished |
Page unpublished |
page.scheduled |
Page scheduled |
page.unscheduled |
Page unscheduled |
page.rescheduled |
Page rescheduled |
Tag Events (7)
| Event | Trigger |
|---|---|
tag.added |
Tag created |
tag.edited |
Tag edited |
tag.deleted |
Tag deleted |
post.tag.attached |
Tag added to a post |
post.tag.detached |
Tag removed from a post |
page.tag.attached |
Tag added to a page |
page.tag.detached |
Tag removed from a page |
Member Events (3)
| Event | Trigger |
|---|---|
member.added |
New member registered |
member.edited |
Member profile updated |
member.deleted |
Member deleted |
Creating Webhooks
Via Ghost Admin UI
Ghost Admin > Settings > Integrations > Custom Integration > Add Webhook
Select the event and provide the target URL.
Via Admin API
import GhostAdminAPI from '@tryghost/admin-api';
const api = new GhostAdminAPI({
url: 'https://my-ghost-site.com',
key: '{id}:{secret}',
version: 'v5.0'
});
// Create a webhook
const webhook = await api.webhooks.add({
event: 'post.published',
target_url: 'https://my-app.com/webhooks/ghost',
name: 'Rebuild site on publish',
integration_id: 'your-integration-id'
});
// Update a webhook
await api.webhooks.edit({
id: webhook.id,
target_url: 'https://new-url.com/webhook'
});
// Delete a webhook
await api.webhooks.delete({id: webhook.id});
Via cURL
# Create webhook
curl -X POST "https://site.com/ghost/api/admin/webhooks/" \
-H "Authorization: Ghost {jwt-token}" \
-H "Content-Type: application/json" \
-d '{
"webhooks": [{
"event": "post.published",
"target_url": "https://example.com/hook",
"name": "My Webhook"
}]
}'
# Delete webhook
curl -X DELETE "https://site.com/ghost/api/admin/webhooks/{id}/" \
-H "Authorization: Ghost {jwt-token}"
Important: There is no Browse endpoint for webhooks — they cannot be listed via the API.
Webhook Payload
Webhooks send an HTTP POST with a JSON payload containing the relevant resource data:
{
"post": {
"current": {
"id": "...",
"title": "My Post",
"slug": "my-post",
"status": "published",
"published_at": "2024-01-15T12:00:00.000Z",
"...": "full post object"
},
"previous": {
"title": "Old Title",
"updated_at": "2024-01-14T10:00:00.000Z"
}
}
}
currentcontains the full current state of the resourcepreviouscontains only the fields that changed (for edit events)- A 2xx response is considered successful delivery
- Response body contents are discarded by Ghost
Important caveats:
- Ghost does not formally document exact payload schemas per event — test empirically or inspect Ghost source
- No documented retry logic — no retry count, backoff strategy, or timeout is specified
- The
secretfield exists on webhook creation but the signature verification mechanism is undocumented — no HMAC algorithm, header name, or verification procedure is publicly specified - There is no Browse endpoint — webhooks cannot be listed via the API
Webhook Receiver Example
// Express.js webhook receiver
import express from 'express';
const app = express();
app.use(express.json());
app.post('/webhooks/ghost', (req, res) => {
const { post, page, tag, member } = req.body;
if (post) {
console.log(`Post event: ${post.current.title}`);
// Trigger rebuild, send notification, etc.
}
if (member) {
console.log(`Member event: ${member.current.email}`);
// Sync to CRM, send welcome email, etc.
}
res.status(200).send('OK');
});
app.listen(3000);
JavaScript SDKs
Content API SDK (@tryghost/content-api)
Read-only access to published content:
npm install @tryghost/content-api
import GhostContentAPI from '@tryghost/content-api';
const api = new GhostContentAPI({
url: 'https://demo.ghost.io',
key: 'content-api-key',
version: 'v5.0'
});
// Browse posts
const posts = await api.posts.browse({
limit: 10,
include: 'tags,authors',
filter: 'featured:true'
});
// Read single post
const post = await api.posts.read({slug: 'welcome'});
// All resources: posts, pages, authors, tags, tiers, settings
Admin API SDK (@tryghost/admin-api)
Full read-write access (server-side only):
npm install @tryghost/admin-api
import GhostAdminAPI from '@tryghost/admin-api';
const api = new GhostAdminAPI({
url: 'https://my-ghost-site.com',
key: '{id}:{secret}',
version: 'v5.0'
});
// CRUD operations
const post = await api.posts.add({title: 'New Post'});
await api.posts.edit({id: post.id, title: 'Updated', updated_at: post.updated_at});
await api.posts.delete({id: post.id});
// Image upload
const image = await api.images.upload({file: '/path/to/image.jpg'});
Helper Libraries
npm install @tryghost/helpers # Formatting utilities
npm install @tryghost/string # Slug generation
Common Integration Patterns
Static Site Rebuild
Trigger a Netlify/Vercel rebuild when content changes:
// Webhook receiver
app.post('/webhooks/ghost', async (req, res) => {
// Trigger Vercel deploy hook
await fetch('https://api.vercel.com/v1/integrations/deploy/prj_xxx/hook_xxx', {
method: 'POST'
});
res.status(200).send('OK');
});
Or use site.changed webhook directly pointed at a Vercel/Netlify deploy hook URL.
Slack Notification on Publish
app.post('/webhooks/ghost-slack', async (req, res) => {
const post = req.body.post?.current;
if (!post) return res.status(200).send('OK');
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
text: `New post published: *${post.title}*\n${post.url}`
})
});
res.status(200).send('OK');
});
Member Sync to CRM
app.post('/webhooks/ghost-crm', async (req, res) => {
const member = req.body.member?.current;
if (!member) return res.status(200).send('OK');
// Sync to Mailchimp, HubSpot, etc.
await syncToCRM({
email: member.email,
name: member.name,
status: member.status,
labels: member.labels
});
res.status(200).send('OK');
});
Headless CMS Setups
Ghost works as a headless CMS with popular frameworks:
Next.js
// lib/ghost.js
import GhostContentAPI from '@tryghost/content-api';
export const ghost = new GhostContentAPI({
url: process.env.GHOST_URL,
key: process.env.GHOST_CONTENT_API_KEY,
version: 'v5.0'
});
// app/page.tsx (Next.js App Router)
export default async function Home() {
const posts = await ghost.posts.browse({
limit: 10,
include: 'tags,authors'
});
return posts.map(post => <PostCard key={post.id} post={post} />);
}
Gatsby
// gatsby-config.js
module.exports = {
plugins: [{
resolve: 'gatsby-source-ghost',
options: {
apiUrl: 'https://my-ghost-site.com',
contentApiKey: 'content-api-key'
}
}]
};
Eleventy (11ty)
// _data/posts.js
const GhostContentAPI = require('@tryghost/content-api');
const api = new GhostContentAPI({
url: 'https://my-ghost-site.com',
key: 'content-api-key',
version: 'v5.0'
});
module.exports = async function() {
return await api.posts.browse({limit: 'all', include: 'tags,authors'});
};
Other Supported Frameworks
Ghost has documented integrations with: Nuxt, Gridsome, VuePress, Hexo, and Hugo.
Migration
Ghost provides migration tools for importing content from other platforms:
Supported Source Platforms
BeeHiiv, Buttondown, Ghost-to-Ghost, Gumroad, Jekyll, Kit (ConvertKit), Mailchimp, Medium, Memberful, Newspack, Patreon, Squarespace, Substack, WordPress
Developer Migration
For custom migrations, use the Admin API to import content:
const api = new GhostAdminAPI({
url: 'https://new-ghost-site.com',
key: '{id}:{secret}',
version: 'v5.0'
});
// Import posts from external source
for (const item of externalPosts) {
await api.posts.add({
title: item.title,
html: item.content,
status: 'published',
published_at: item.date,
tags: item.tags.map(t => ({name: t})),
feature_image: item.image
}, {source: 'html'});
}
// Import members
for (const subscriber of externalSubscribers) {
await api.members.add({
email: subscriber.email,
name: subscriber.name,
labels: [{name: 'imported'}]
});
}
Ghost Custom Integrations
Create integrations in Ghost Admin > Settings > Integrations:
Each integration provides:
- Content API Key — For read-only access
- Admin API Key — For read-write access (id:secret format)
- Webhook Configuration — Event subscriptions
Integrations have fixed, limited permissions. For full API access, use Staff Access Tokens (from user profile) or User Authentication (session-based).
API Versioning
- Ghost maintains backward compatibility within major versions
- Use
Accept-Version: v5.0header to target minimum version - Breaking changes only occur in major version bumps (v5 → v6)
- The
?limit=allparameter was removed in Ghost 6.0 (max 100 per page)
More from bowtiedswan/ghost-cms-skills
ghost-admin-api
Create, update, and manage Ghost CMS content via the Admin API. This skill should be used when creating or editing posts/pages, managing members, newsletters, tiers, offers, tags, users, uploading images/themes, or configuring webhooks. Covers JWT authentication from Admin API keys, the JavaScript SDK (@tryghost/admin-api), Lexical content format, and all CRUD endpoints.
2ghost-cli
Ghost CLI tool for installing, configuring, and managing Ghost CMS instances. This skill should be used when installing Ghost, configuring a production server, managing Ghost processes (start/stop/restart), updating Ghost, setting up SSL/NGINX, backing up sites, or troubleshooting Ghost installations.
2