Discord <> Ghost role sync

Hey Ghost community,

I activated the Members feature on my blog a while back, and the question of syncing membership tiers with Discord roles came up pretty quickly. The NoCode route (Make, Zapier) works, but the pairing logic isn’t exactly straightforward to set up.

So I built ghost-discord-worker — an open-source Cloudflare Worker that bridges Ghost and Discord automatically.

The idea is simple:

  1. Configure Ghost webhooks to notify the Worker on member events
  2. The Worker maps Ghost emails to Discord user IDs using a Cloudflare KV store
  3. Members link their accounts once with /link on Discord — after that, everything is automatic

Supported events: member added (free or paid), upgraded, downgraded, deleted. Runs entirely on Cloudflare’s free tier.

The project is on GitHub: GitHub - ltoinel/ghost-discord-worker: A simple worker to update Discord role based on Ghost Subscription

It’s a first version and I’d genuinely love feedback from anyone who gives it a try — whether it works for your setup, what’s missing, or what could be done better. All contributions welcome!

My Ghost post on the project :

Regards,

5 Likes

Thanks for sharing this!

1 Like

Does this support multiple paid tiers? Rather than just free & premium?

Only Paid and free, but it could be adapted here line 84 : ghost-discord-worker/src/commands.ts at main · ltoinel/ghost-discord-worker · GitHub

const errors: string[] = [];
const err1 = await addRole(env, userId, env.DISCORD_ROLE_MEMBER);
if (err1) errors.push(err1);
if (isPaid(ghostResult.member.status)) {
const err2 = await addRole(env, userId, env.DISCORD_ROLE_PREMIUM);
if (err2) errors.push(err2);
}

I really like how smooth this flow is. But I think I see one problem, which is that a discord user can effectively steal another user’s premium access, if they can guess that user’s email address and that user has not already claimed their premium plan’s discord access.

I think a safer flow here might be:

  • On the Ghost side on your “how to get discord access” directions page, client-side js posts the user’s entitlements jwt (from /members/api/entitlements) to the CF worker.

  • The worker returns a code to the browser and stores it alongside the user’s Ghost info in the kv.

  • The browser displays "here’s your code, head to discord and type ‘/link [code]’ to get access

  • The discord bot validates that [code] is a valid code in the kv and then does the account linkage.

You have completly right Cathy ! Working on a V2

1 Like

My new implementation regarding your comment Cathy.
You can test the feature here: Serveur Discord Geeek.org