From ghost
Configure and handle Ghost webhooks for all 31 events covering posts, pages, members, and tags. Includes local dev setup with Stripe webhook forwarding.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ghost:ghost-webhooksThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Ghost webhooks send a POST request to a target URL when content or member events occur. There are 31 events across four categories. Webhooks can be created via the Ghost Admin UI or the Admin API.
Ghost webhooks send a POST request to a target URL when content or member events occur. There are 31 events across four categories. Webhooks can be created via the Ghost Admin UI or the Admin API.
Prerequisite: For API-managed webhooks, authentication configured — see ghost-admin-api-auth.
Announce at start: "I'm using the ghost-webhooks skill."
Settings → Advanced → Integrations → Add custom integration → Add webhook.
curl -X POST \
-H "Authorization: Ghost $TOKEN" \
-H "Accept-Version: v5.0" \
-H "Content-Type: application/json" \
-d '{
"webhooks": [{
"event": "post.published",
"target_url": "https://yourserver.com/hooks/ghost",
"name": "Notify on publish",
"secret": "your_webhook_secret"
}]
}' \
"$GHOST_ADMIN_URL/ghost/api/admin/webhooks/"
curl -X PUT \
-H "Authorization: Ghost $TOKEN" \
-H "Accept-Version: v5.0" \
-H "Content-Type: application/json" \
-d '{"webhooks": [{"target_url": "https://newurl.com/hooks/ghost"}]}' \
"$GHOST_ADMIN_URL/ghost/api/admin/webhooks/{WEBHOOK_ID}/"
curl -X DELETE \
-H "Authorization: Ghost $TOKEN" \
-H "Accept-Version: v5.0" \
"$GHOST_ADMIN_URL/ghost/api/admin/webhooks/{WEBHOOK_ID}/"
| Event | Triggers on |
|---|---|
site.changed | Any content or settings change |
| Event | Triggers on |
|---|---|
post.added | New post created (any status) |
post.edited | Post content or settings changed |
post.deleted | Post deleted |
post.published | Post status changed to published |
post.published.edited | Published post content edited |
post.unpublished | Published post reverted to draft |
post.scheduled | Post scheduled for future publish |
post.unscheduled | Scheduled post moved back to draft |
post.scheduled.cancelled | Scheduled post deleted before publishing |
post.tag.attached | Tag added to a post |
post.tag.detached | Tag removed from a post |
| Event |
|---|
page.added |
page.edited |
page.deleted |
page.published |
page.unpublished |
page.tag.attached |
page.tag.detached |
| Event |
|---|
tag.added |
tag.edited |
tag.deleted |
| Event |
|---|
member.added |
member.edited |
member.deleted |
Ghost sends a POST with Content-Type: application/json. The body structure is:
{
"post": {
"current": {
"id": "abc123",
"title": "My Post",
"status": "published",
"published_at": "2024-08-01T10:00:00.000Z",
"url": "https://example.com/my-post/",
"authors": [...],
"tags": [...]
},
"previous": {
"status": "draft"
}
}
}
The top-level key matches the resource type (post, page, tag, member). current is the current state; previous contains only changed fields.
If you set a secret when creating the webhook, Ghost signs each payload with HMAC-SHA256.
Signature is in the X-Ghost-Signature header:
X-Ghost-Signature: sha256=abc123..., t=1722513600
Node.js verification:
import crypto from 'crypto';
function verifyGhostWebhook(secret, rawBody, signatureHeader) {
const [sigPart, tPart] = signatureHeader.split(', ');
const receivedSig = sigPart.replace('sha256=', '');
const timestamp = tPart.replace('t=', '');
const expectedSig = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(receivedSig),
Buffer.from(expectedSig)
);
}
Always verify before processing. Return 200 regardless (prevents retry loops).
2xx response within a reasonable timeoutFor local webhook testing, expose your local server:
# Install ngrok
npm install -g ngrok
# Expose local port 3000
ngrok http 3000
Use the generated https://xxxxx.ngrok.io URL as your webhook target. Update it in Ghost Admin each session.
Ghost uses Stripe webhooks internally for subscription events. For local dev:
# Install Stripe CLI
brew install stripe/stripe-cli/stripe
# Login
stripe login
# Forward Stripe webhooks to your local Ghost instance
stripe listen --forward-to http://localhost:2368/members/webhooks/stripe/
// Express.js handler
app.post('/hooks/ghost', express.json(), async (req, res) => {
res.sendStatus(200); // Acknowledge immediately
if (req.body?.post?.current?.status === 'published') {
await triggerNetlifyBuild(); // or Vercel, Cloudflare Pages, etc.
}
});
app.post('/hooks/ghost/member-added', express.json(), async (req, res) => {
res.sendStatus(200);
const member = req.body?.member?.current;
if (member) {
await syncToCRM({ email: member.email, name: member.name });
}
});
app.post('/hooks/ghost/published', express.json(), async (req, res) => {
res.sendStatus(200);
const post = req.body?.post?.current;
if (post?.status === 'published') {
await fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: `New post: ${post.title} — ${post.url}` }),
});
}
});
secret set on webhook for signature verification200 immediately (before async processing)X-Ghost-Signature verified before processing payload/members/webhooks/stripe/npx claudepluginhub thinkmorestupidless/claude-marketplace --plugin ghostGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.