See Agility CMS in action. Watch a product demo
Publishing to Social Channels from Agility CMS
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.
The one trigger everything depends on
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:
- Webhooks are fire-and-forget. Agility sends the event once and does not retry. If your endpoint is down or slow when content publishes, that notification is gone. Whatever catches the event needs its own queue and retry, which is a strong reason to prefer a tool that provides one.
- The payload is a pointer, not the full article. The publish event tells you what changed (
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.
Path 1 — No-code automation (recommended starting point)
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 covers the fire-and-forget gap.
Well-documented platforms that work cleanly with an Agility webhook:
- Zapier — largest connector library; "Catch Hook" trigger.
- Make — visual scenario builder, generous free tier; "Custom webhook" module.
- n8n — open-source and self-hostable; "Webhook" node.
- Power Automate — best fit if you're in the Microsoft ecosystem; "When an HTTP request is received" trigger.
The setup is the same shape on all four:
- In the automation tool, create a flow that starts from an incoming webhook and copy the URL it generates.
- In Agility, go to Settings → Webhooks → New, paste that URL, and subscribe to Content Publish Events. Use the Send Test Payload button to confirm it arrives.
- Add a filter step so you only act on the content you want, for example only items where
stateisPublishedandreferenceNameis your blog container. - Add a format step that maps your content fields to each channel's requirements (text, link, image, character limits).
- Add a post action for each destination.
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.
Path 2 — A social management platform API (Buffer)
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.
Path 3 — A custom endpoint with the Content Management API
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:
- Acknowledge fast. Verify the request, and return
200immediately, then do the real work asynchronously (a queue, a background job). Because the webhook does not retry, a slow handler risks dropping the event. - Fetch the full item. Use the Content Management API (or Content Fetch API) to pull the fields your post needs from the
contentIDin the payload. - Format per channel and dispatch. Build each payload and call your destination. Pointing this at Buffer's API (Path 2) is the most reliable target, since it absorbs OAuth refresh, channel quirks, and scheduling for you.
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.
// (Webhooks are fire-and-forget — never block this response on the post.)
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 validating the security key you set when creating the webhook (sent as a header), so only Agility can trigger it.
Path 4 — A custom Agility App
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.
The Channels selector pattern
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.
What to watch out for
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 |
|---|---|---|
| No webhook retries | A missed event means a missed post | Use a tool/queue with its own retry (Paths 1, 2) |
| 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) |
Choosing an approach
| 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.