npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lizardglobal/payload-discord-sync

v0.1.7

Published

Payload CMS plugin to sync Discord guilds, channels, members, and roles via discord.js

Downloads

726

Readme

@lizardglobal/payload-discord-sync

Payload CMS plugin that syncs Discord guilds, channels, members, and roles into Payload collections using discord.js.

npm

Release

[!WARNING] This plugin is still experimental. APIs, collection schemas, and behavior may change without a stable compatibility guarantee. Use in production with caution and pin versions deliberately.

Features

  • Syncs Discord guilds, channels, members, and roles into Payload collections
  • Triggers: admin UI, programmatic API, HTTP endpoint, or scheduled Payload jobs
  • Scope syncs to all guilds the bot is in, or a subset of guild IDs
  • Optional user linking (automatic and/or programmatic) to your auth/users collection
  • Member email field (from OAuth — Discord bots cannot read emails from guild member lists)
  • Typed config with collection slug/field overrides

Sync is on-demand or scheduled. The plugin does not listen to live Discord gateway events (guildUpdate, channelUpdate, etc.).

Requirements

  • Payload 3.x
  • Node.js ^18.20.2 || >=20.9.0
  • A Discord bot with the Server Members Intent enabled (for member sync)

Install

pnpm add @lizardglobal/payload-discord-sync discord.js
# or
npm install @lizardglobal/payload-discord-sync discord.js

Discord bot setup

  1. Create an application in the Discord Developer Portal
  2. Create a bot and copy the bot token
  3. Under Bot → Privileged Gateway Intents, enable Server Members Intent
  4. Invite the bot to your guild(s) with permission to view channels and members

Quick start

import { buildConfig } from 'payload'
import { discordSyncPlugin } from '@lizardglobal/payload-discord-sync'

export default buildConfig({
  // ...
  plugins: [
    discordSyncPlugin({
      botToken: process.env.DISCORD_BOT_TOKEN!,
      // Optional: omit to sync every guild the bot is in
      guildIds: ['123456789012345678'],
      linkUsers: {
        collection: 'users',
      },
      schedule: {
        enabled: true,
        cron: '0 */6 * * *',
        seedOnInit: true, // avoid in busy serverless environments
      },
    }),
  ],
})

Configuration

| Option | Type | Description | | --- | --- | --- | | botToken | string | Discord bot token (required unless disabled) | | disabled | boolean | Keep collections in the schema but skip sync, endpoints, and jobs | | guildIds | string[] | Limit sync to these guilds; empty/omitted = all bot guilds | | resources | object | Enable/disable or customize guilds, channels, members, roles | | linkUsers | object | Link Discord members to your users collection | | schedule | boolean \| object | Recurring sync via Payload jobs (cron, queue, seedOnInit, …) | | admin.group | string | Admin sidebar group (default Discord) | | admin.syncAction | boolean | Show Sync Discord on the guilds list (default true) | | intents | number[] | Override discord.js gateway intents | | onSyncComplete | fn | Callback after a successful sync | | onSyncError | fn | Callback when a sync fails |

Resources

discordSyncPlugin({
  botToken: process.env.DISCORD_BOT_TOKEN!,
  resources: {
    members: false, // skip this collection
    guilds: {
      slug: 'servers',
      fields: ({ defaultFields }) => [
        ...defaultFields,
        { name: 'notes', type: 'textarea' },
      ],
    },
  },
})

Linking users

discordSyncPlugin({
  botToken: process.env.DISCORD_BOT_TOKEN!,
  linkUsers: {
    collection: 'users', // your users / auth collection slug
    discordIdField: 'discordId', // injected on users (default)
    userField: 'user', // relationship on discord-members (default)
    autoLink: true, // link on sync when discordId matches
    matchByEmail: false, // also match member.email → users.email
    injectUserFields: true,
  },
})

Automatic: on member sync, if a user already has discordId (or email when matchByEmail is on), the member’s user relationship is set.

Programmatic (typical after Discord OAuth with identify + email):

import { linkDiscordUser, unlinkDiscordUser } from '@lizardglobal/payload-discord-sync'

await linkDiscordUser({
  payload,
  options: {
    botToken: process.env.DISCORD_BOT_TOKEN!,
    linkUsers: { collection: 'users' },
  },
  userId: req.user.id,
  discordUserId: discordProfile.id,
  email: discordProfile.email, // stored on matching guild members
})

await unlinkDiscordUser({
  payload,
  options: {
    botToken: process.env.DISCORD_BOT_TOKEN!,
    linkUsers: { collection: 'users' },
  },
  userId: req.user.id,
})

Triggering syncs

Admin panel

Open Discord Guilds and click Sync Discord (queues a job on the discord-sync queue).

Programmatic

import { syncDiscord, queueDiscordSync } from '@lizardglobal/payload-discord-sync'

// Inline
await syncDiscord({
  payload,
  options: { botToken: process.env.DISCORD_BOT_TOKEN! },
  input: {
    guildIds: ['123'], // optional
    resources: ['guilds', 'channels'], // optional
  },
})

// Via jobs queue
await queueDiscordSync({
  payload,
  options: { botToken: process.env.DISCORD_BOT_TOKEN! },
  input: { guildIds: ['123'] },
  reschedule: true,
})

HTTP

POST /api/discord-sync (authenticated admin/user session)

{
  "guildIds": ["123"],
  "resources": ["guilds", "channels", "members", "roles"],
  "async": true
}

Omit async or set false to run inline and return the result in the response.

Scheduled jobs

With schedule.enabled, the plugin registers a discordSync task and an autoRun entry on the discord-sync queue. Your app must process Payload jobs (via jobs.autoRun or a worker).

Seed the first job with schedule.seedOnInit: true, the admin button, or queueDiscordSync({ reschedule: true }).

Collections

| Resource | Default slug | Key fields | | --- | --- | --- | | Guilds | discord-guilds | discordId, name, icon, ownerId, memberCount | | Channels | discord-channels | discordId, name, type, guild | | Members | discord-members | discordId (guildId:userId), userDiscordId, username, email, user (if linkUsers), guild | | Roles | discord-roles | discordId, name, permissions, guild |

Every document also has lastSyncedAt and a raw JSON snapshot from Discord.

Contributing

See CONTRIBUTING.md for local setup, PR guidelines, and how releases work.

License

MIT