Payload structure
All webhook payloads follow a consistent structure with two main sections:
{
"data": {
// Content-specific data about the changed item
},
"webhook": {
"event": "event.type",
"target": "https://your-webhook-endpoint.com"
}
}
The webhook object is included in all webhook payloads and contains:
| Field | Type | Description |
|---|
event | string | The event type that triggered this webhook (e.g., page.published) |
target | string (URI) | The webhook endpoint URL that received this notification |
Page webhook payloads
Data fields
| Field | Type | Description | Required |
|---|
id | string | Page slug | Yes |
buttercms_id | integer | Page numeric ID | Yes |
page_type | string | Page type key, or * for single pages | Yes |
editor | string | Full name of the editor who made the change | Yes |
name | string | Human-readable page name | Yes |
timestamp | string | Timestamp when webhook was generated | Yes |
locale | string/null | Locale code for localized content, null for default locale | No |
status | string | Current page status: draft, published, scheduled, or deleted | Yes |
updated | string (datetime) | Last updated timestamp in ISO 8601 format | Yes |
published | string/null (datetime) | Published timestamp, null if not published | No |
scheduled | string/null (datetime) | Scheduled publication timestamp, null if not scheduled | No |
Example: Page published
{
"data": {
"id": "product-launch",
"buttercms_id": 5678,
"page_type": "landing",
"editor": "Marketing Team",
"name": "Product Launch Landing Page",
"timestamp": "2024-01-15T14:30:00",
"locale": null,
"status": "published",
"updated": "2024-01-15T14:30:00.123456Z",
"published": "2024-01-15T14:30:00.123456Z",
"scheduled": null
},
"webhook": {
"event": "page.published",
"target": "https://yourapp.com/webhooks/buttercms"
}
}
Example: Page deleted
{
"data": {
"id": "old-promotion",
"buttercms_id": 3456,
"page_type": "promo",
"editor": "Content Manager",
"name": "Old Promotion Page",
"timestamp": "2024-01-15T20:15:00",
"locale": null,
"status": "deleted",
"updated": "2024-01-15T20:15:00.111222Z",
"published": "2024-01-10T10:00:00.000000Z",
"scheduled": null
},
"webhook": {
"event": "page.delete",
"target": "https://yourapp.com/webhooks/buttercms"
}
}
Blog Post webhook payloads
Data fields
| Field | Type | Description | Required |
|---|
id | string | Blog post slug | Yes |
_id | integer | Blog post numeric ID | Yes |
Example: Blog Post published
{
"data": {
"id": "announcing-new-features",
"_id": 5678
},
"webhook": {
"event": "post.published",
"target": "https://yourapp.com/webhooks/buttercms"
}
}
Blog post webhook payloads are intentionally lightweight, containing only the post slug and ID. To get the full post content, use the slug to fetch the complete post data from the ButterCMS API.
Fetching full post data
// After receiving the webhook
const { data, webhook } = webhookPayload;
if (webhook.event === 'post.published') {
// Fetch the full post using the slug
const butter = require('buttercms')('your-api-token');
const response = await butter.post.retrieve(data.id);
const fullPost = response.data.data;
// Now you have access to all post fields:
// fullPost.title, fullPost.body, fullPost.author, etc.
}
Collection item webhook payloads
Data fields
| Field | Type | Description | Required |
|---|
id | string | Collection key identifier | Yes |
itemid | string | API URI for retrieving the specific collection item | Yes |
label | string | Display label for the collection item | Yes |
editor | string | Full name of the editor who made the change | Yes |
timestamp | string | Timestamp when webhook was generated | Yes |
locale | string/null | Locale code for localized content, null for default locale | No |
status | string | Current status: draft, published, or deleted | Yes |
Example: collection item published
{
"data": {
"id": "products",
"itemid": "/v2/content/?keys=products[_id=5678]",
"label": "Wireless Headphones Pro",
"editor": "Product Manager",
"timestamp": "2024-01-15T13:45:00",
"locale": null,
"status": "published"
},
"webhook": {
"event": "collectionitem.published",
"target": "https://yourapp.com/webhooks/buttercms"
}
}
Fetching full collection item data
The itemid field contains the API path to fetch the specific item:
// After receiving the webhook
const { data } = webhookPayload;
// The itemid contains the API query path
// e.g., "/v2/content/?keys=products[_id=5678]"
// Fetch the full item using the ButterCMS client
const butter = require('buttercms')('your-api-token');
// Extract the collection key and item ID
const collectionKey = data.id; // "products"
const itemFilter = `[_id=${extractId(data.itemid)}]`;
const response = await butter.content.retrieve([collectionKey], {
[collectionKey]: itemFilter
});
Localized content payloads
For organizations with localization enabled, the locale field indicates which locale triggered the event:
Default locale
When content in the default locale is changed, locale is null:
{
"data": {
"id": "homepage",
"locale": null,
...
}
}
Specific locale
When content in a specific locale is changed, locale contains the locale code:
{
"data": {
"id": "homepage",
"locale": "es",
...
}
}
Handling localized webhooks
app.post('/webhooks/buttercms', (req, res) => {
const { data, webhook } = req.body;
// Check if this is for a specific locale
if (data.locale) {
// Handle locale-specific cache invalidation
invalidateCache(data.id, data.locale);
} else {
// Handle default locale
invalidateCache(data.id, 'default');
}
res.status(200).json({ received: true });
});
Parsing webhook payloads
TypeScript interface
Define types for webhook payloads to ensure type safety:
interface WebhookMeta {
event: string;
target: string;
}
interface PageWebhookData {
id: string;
buttercms_id: number;
page_type: string;
editor: string;
name: string;
timestamp: string;
locale: string | null;
status: 'draft' | 'published' | 'scheduled' | 'deleted';
updated: string;
published: string | null;
scheduled: string | null;
}
interface BlogPostWebhookData {
id: string;
_id: number;
}
interface CollectionItemWebhookData {
id: string;
itemid: string;
label: string;
editor: string;
timestamp: string;
locale: string | null;
status: 'draft' | 'published' | 'deleted';
}
interface WebhookPayload<T> {
data: T;
webhook: WebhookMeta;
}
Event type detection
function handleWebhook(payload) {
const { event } = payload.webhook;
// Parse event type
const [contentType, action] = event.split('.');
switch (contentType) {
case 'page':
handlePageEvent(action, payload.data);
break;
case 'post':
handlePostEvent(action, payload.data);
break;
case 'collectionitem':
handleCollectionEvent(action, payload.data);
break;
case 'media':
handleMediaEvent(action, payload.data);
break;
}
}