Set Up Graph Webhooks
Why use webhooks
Section titled “Why use webhooks”Graph indexes content and makes it queryable, but your frontend may need to react to content changes proactively. Webhooks send HTTP notifications to your systems when content is created, updated, or deleted in Graph. This enables automatic static site rebuilds, CDN cache purges, and downstream system updates without polling.
Step 1: Set up a webhook endpoint
Section titled “Step 1: Set up a webhook endpoint”Create an HTTPS endpoint that accepts POST requests. The endpoint must respond with a 2xx status code within 10 seconds to confirm receipt.
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.GRAPH_WEBHOOK_SECRET;
app.post('/webhooks/graph', (req, res) => {
// Verify the webhook signature
const signature = req.headers['x-graph-signature'];
const payload = JSON.stringify(req.body);
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expected) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { type, data } = req.body;
console.log(`Received: ${type} for ${data.contentType}`);
// Handle the event
switch (type) {
case 'content/updated':
case 'content/created':
triggerRebuild(data);
break;
case 'content/deleted':
purgeCache(data);
break;
}
res.status(200).json({ received: true });
});
app.listen(3001); Step 2: Register the webhook
Section titled “Step 2: Register the webhook”Register your endpoint with Graph through the admin API. Specify which content events should trigger notifications.
curl -X POST \
https://cg.optimizely.com/api/content/v3/webhooks \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic {base64(AppKey:Secret)}' \
-d '{
"url": "https://your-app.com/webhooks/graph",
"events": [
"content/created",
"content/updated",
"content/deleted"
],
"secret": "your-webhook-secret",
"enabled": true
}' Event types
Section titled “Event types”| Event | Trigger | Use case |
|---|---|---|
content/created | New content published for the first time | Add to search index, send notifications |
content/updated | Existing content republished | Rebuild affected pages, purge caches |
content/deleted | Content unpublished or removed | Remove from search, purge caches |
sync/completed | Full sync operation finished | Trigger complete site rebuild |
Step 3: Filter by content type
Section titled “Step 3: Filter by content type”For large sites, you may not want every content change to trigger a webhook. Filter notifications to specific content types.
curl -X POST \
https://cg.optimizely.com/api/content/v3/webhooks \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic {base64(AppKey:Secret)}' \
-d '{
"url": "https://your-app.com/webhooks/graph",
"events": ["content/updated", "content/created"],
"filters": {
"contentTypes": ["ArticlePage", "ProductPage", "LandingPage"]
},
"secret": "your-webhook-secret",
"enabled": true
}' Step 4: Handle common webhook scenarios
Section titled “Step 4: Handle common webhook scenarios”Trigger a static site rebuild
Section titled “Trigger a static site rebuild”// pages/api/revalidate.js
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).end();
}
const { data } = req.body;
try {
// Revalidate the specific page
if (data.url) {
await res.revalidate(data.url);
}
// Revalidate listing pages that may include this content
await res.revalidate('/articles');
await res.revalidate('/');
return res.json({ revalidated: true });
} catch (err) {
return res.status(500).json({ error: 'Revalidation failed' });
}
} Purge CDN cache
Section titled “Purge CDN cache”async function purgeCache(data) {
const urlsToPurge = [
data.url,
'/articles',
'/sitemap.xml',
].filter(Boolean);
await fetch('https://api.cdn-provider.com/purge', {
method: 'POST',
headers: {
'Authorization': `Bearer ${CDN_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ urls: urlsToPurge }),
});
console.log(`Purged cache for: ${urlsToPurge.join(', ')}`);
} Step 5: Monitor and manage webhooks
Section titled “Step 5: Monitor and manage webhooks”List, update, and remove webhook registrations through the admin API.
# List all registered webhooks
curl -s \
https://cg.optimizely.com/api/content/v3/webhooks \
-H 'Authorization: Basic {base64(AppKey:Secret)}'
# Disable a webhook temporarily
curl -X PATCH \
https://cg.optimizely.com/api/content/v3/webhooks/{webhook-id} \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic {base64(AppKey:Secret)}' \
-d '{"enabled": false}'
# Delete a webhook
curl -X DELETE \
https://cg.optimizely.com/api/content/v3/webhooks/{webhook-id} \
-H 'Authorization: Basic {base64(AppKey:Secret)}' Troubleshooting
Section titled “Troubleshooting”| Issue | Cause | Resolution |
|---|---|---|
| No webhooks received | Endpoint not reachable | Verify the URL is publicly accessible over HTTPS |
| 401 responses logged | Signature mismatch | Verify the webhook secret matches between Graph config and your endpoint |
| Duplicate notifications | Retry after timeout | Ensure your endpoint responds within 10 seconds; implement idempotent handling |
| Missed events | Webhook disabled after failures | Re-enable the webhook; fix the endpoint issue that caused consecutive failures |