What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
Extensibility
How to publish content from Agility to social channels. Four paths built on the publish webhook: no-code automation, the Buffer API, a custom endpoint, and a custom Agility App.
Agility doesn't have a built-in "post to social" button, and that's by design. As a headless CMS, Agility owns your content and the publish event; the social networks own posting, authentication, and formatting. Connecting the two is an integration pattern, not a single setting. The good news: every approach hangs off one thing you already have, the publish webhook, so once you understand the trigger you can pick whichever path fits your team.
This guide covers four ways to do it, from a no-code recipe you can wire up in an afternoon to a fully custom endpoint, plus the caveats that trip people up.
When an editor publishes a content item, Agility fires a Content Publish Event webhook to any endpoint you've registered under Settings → Webhooks. That single HTTP POST is the starting gun for social posting. Whatever you build downstream, this is the event that kicks it off.
Before you build, two things are worth knowing about the webhook itself, because they shape every approach below:
2xx response counts as a failure, so an endpoint that is down or slow can still miss an event if retries are off or run out, and a retry can deliver the same event twice. Design whatever catches the event to acknowledge quickly and to be idempotent (the webhook-id header makes a good key). See the Webhook Events and Payload Reference.contentID, referenceName, state), not the complete content. If your social post needs the title, body excerpt, hero image, or canonical URL, you fetch those separately via the Content Delivery or Content Management API.See the Webhooks reference for the full payload shape and setup steps.
For most teams this is the right answer. Point the webhook at an automation platform and let it handle the catch, the branching, the formatting, and the call to each social network. No servers to run, and the platform's built-in queue adds its own buffering on top of Agility's retries.
Well-documented platforms that work cleanly with an Agility webhook:
The setup is the same shape on all four:
state is Published and referenceName is your blog container.A practical refinement: rather than acting on every publish, add a Channels field to your content model (see The Channels selector pattern below) so editors decide per item where it should go, and your filter reads that field.
If your team already schedules social posts through a management platform, you can post into that instead of to each network directly. The platform then owns the OAuth tokens, per-channel formatting, scheduling, and retries, and your marketing team keeps one familiar calendar UI.
Buffer is the one we recommend building against here, because it has a public, documented GraphQL API with OAuth2 that covers X, LinkedIn, Facebook, Instagram, Threads, TikTok, Pinterest, Mastodon, Bluesky, and more. It supports exact-time scheduling via dueAt, threads, and first-comments, all things you'd otherwise hand-roll against each network.
The flow is the same as Path 1 or Path 3, except the final step calls Buffer's createPost rather than each social network. You can drive it from a no-code tool or from your own endpoint.
Trade-off worth naming. A management platform is a second subscription and a second integration to maintain, and you inherit its rate limits and uptime. It shines when a team already lives in that tool. If you just want "publish in Agility, post goes out," a direct automation recipe (Path 1) is lighter.
When you need full control over routing, formatting, scheduling logic, or token handling, point the webhook at your own serverless function. This is more work, but nothing is hidden from you.
The pattern that holds up in production:
200 immediately, then do the real work asynchronously (a queue, a background job). A slow response counts as a failed delivery: with retries off the event is lost, and with retries on it arrives again, so keep the handler fast and idempotent.contentID in the payload.A minimal Next.js route handler for the receiving end:
// app/api/social/route.js
export async function POST(req) {
const event = await req.json();
// 1. Only act on publishes; acknowledge everything else immediately.
if (event.state !== "Published") {
return new Response("ignored", { status: 200 });
}
// 2. Hand off to a queue/background job so we can return 200 right away.
// (Never block this response on the post: a slow reply counts as a failed delivery.)
await enqueueSocialPost({
contentID: event.contentID,
referenceName: event.referenceName,
});
return new Response("queued", { status: 200 });
}
Your background worker then fetches the item and posts it:
async function processSocialPost({ contentID }) {
// Fetch the full content item (title, body, hero, canonical URL, channels).
const item = await getContentItem(contentID);
// Respect the editor's per-item channel choices.
const channels = item.fields.channels?.split(",") ?? [];
// Send to Buffer (handles OAuth, formatting, scheduling, retries).
await fetch("https://graph.buffer.com/", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.BUFFER_TOKEN}`,
},
body: JSON.stringify({
query: CREATE_POST_MUTATION,
variables: {
channels,
text: buildPostText(item), // trims to each channel's limit
media: item.fields.heroImage?.url,
},
}),
});
}
Secure the endpoint by turning on Enable secure delivery for the webhook and verifying the signature on every request, so only Agility can trigger it. Signed deliveries carry the Standard Webhooks headers webhook-id, webhook-timestamp and webhook-signature. Verify them against the webhook's own whsec_ signing secret with the standardwebhooks npm package, using the raw request body. The instance Security Key is for preview, not webhooks, and Agility does not send it with a delivery. See Verifying Signed Webhooks for full examples.
If you want the posting experience to live inside the CMS, where editors write the social copy, pick channels, or schedule from a panel next to the content, build a Custom App. This is the heaviest lift and only worth it when the in-CMS editor experience is the point. Most teams get what they need from Paths 1 to 3.
Across every path, the cleanest way to control where content goes is a field on the content item itself. Add a multi-select or checkbox Channels field to your content model (for example: Website, LinkedIn, X, Newsletter). Editors choose per item, and your downstream automation or code reads that field to decide what to post and where. It keeps routing decisions with the people creating the content, and out of brittle hard-coded rules.
Agility emits a clean event, but the hard parts live on the social side and are worth setting expectations around:
| Concern | Why it matters | Where it's handled |
|---|---|---|
| Missed or repeated deliveries | Retries are off by default, and a retry can send the same event twice | Turn on webhook retries, check delivery history, and make the handler idempotent (key on webhook-id) |
| OAuth token expiry | Tokens for each network expire and must refresh | A platform like Buffer manages this; rolling your own means handling refresh |
| Per-channel limits | Character counts, image specs, and media rules differ | Format step (every path) |
| Rate limits & approvals | Networks throttle and some require app review | Plan for backoff; budget time for platform review |
| Publish timing | The webhook fires on Agility publish, which may not be your ideal post time | Schedule downstream (e.g. Buffer's dueAt) |
| If you want… | Use |
|---|---|
| The fastest path, no servers | Path 1 — no-code automation |
| One scheduling UI your team already uses | Path 2 — Buffer API |
| Full control over routing and formatting | Path 3 — custom endpoint + Management API |
| Editors to post from inside the CMS | Path 4 — custom Agility App |
Most teams start with Path 1 and a Channels field, then graduate to Path 2 or 3 as their needs grow. Whatever you choose, it all begins with that one publish webhook.