Skip to main content
The welcome system sends a message to a configured channel every time a new member joins your server. You can use static template variables, rotate through multiple message variants, or enable the dynamic mode that adapts the greeting to the time of day, current server activity, and member count milestones.

Configuration

All welcome settings live under the welcome key in config.json.
config.json
{
  "welcome": {
    "enabled": true,
    "channelId": "1438631182379253814",
    "message": "Welcome to Volvox, {user}! 🌱 You're member #{memberCount}!\n\nCheck out <#1446317676988465242> to see what we're working on.",
    "dynamic": {
      "enabled": true,
      "timezone": "America/New_York",
      "activityWindowMinutes": 45,
      "milestoneInterval": 25,
      "highlightChannels": [
        "1438631182379253814",
        "1444154471704957069",
        "1446317676988465242"
      ],
      "excludeChannels": []
    },
    "rulesChannel": null,
    "verifiedRole": null,
    "introChannel": null,
    "roleMenu": {
      "enabled": false,
      "options": []
    },
    "dmSequence": {
      "enabled": false,
      "steps": []
    }
  }
}

Template variables

Every message template supports the following placeholders. The bot replaces them at send time.
VariableAliasResolves to
{user}Discord mention (<@id>)
{username}Plain username, no mention
{server}{guild}Server name
{memberCount}{count}Current member count
Example template
Welcome to {server}, {user}! You're member #{memberCount}.

Multiple message variants

Instead of a single message, you can supply a variants array. The bot picks one at random for each join event.
config.json
{
  "welcome": {
    "enabled": true,
    "channelId": "1438631182379253814",
    "variants": [
      "Hey {user}, welcome to {server}! You're #{memberCount}.",
      "Welcome aboard, {user}! {server} just got better.",
      "{user} just joined — say hi! Member #{memberCount}."
    ]
  }
}
If both variants and message are set, variants takes priority. The message field is used as a fallback when variants is empty or not set.

Per-channel welcome messages

You can send different messages to multiple channels on the same join event using the channels array. Each entry targets a specific channel and supports its own message or variants.
config.json
{
  "welcome": {
    "enabled": true,
    "channelId": "1438631182379253814",
    "message": "Welcome, {user}!",
    "channels": [
      {
        "channelId": "1444154471704957069",
        "message": "New member alert — say hi to {user}!"
      },
      {
        "channelId": "1446317676988465242",
        "variants": [
          "{user} just landed. Show them around!",
          "Fresh face spotted: {user}"
        ]
      }
    ]
  }
}
The primary channelId always fires. Extra entries in channels with a different channelId fire as additional messages.

Dynamic mode

When dynamic.enabled is true, the bot replaces your static template with a context-aware message built from:
  • Time of day — morning, afternoon, evening, or night greeting (based on timezone)
  • Activity snapshot — counts messages in the activityWindowMinutes rolling window (default: 45 minutes) to pick an activity level: quiet, light, steady, busy, or hype
  • Active channels — the top channels by recent message count are mentioned in the greeting
  • Milestone detection — a special line appears when the member count hits a notable number

Activity window

The bot records every non-bot message from guild text channels into an in-memory activity map. When a new member joins, it counts messages from the past activityWindowMinutes minutes to build the activity snapshot. Channels listed in excludeChannels are not counted toward the activity window.

Milestone detection

Two rules trigger a milestone line:
  1. The member count matches one of the notable milestones: 10, 25, 50, 100, 250, 500, 1000
  2. The member count is divisible by milestoneInterval (default: 25)
When triggered, the message includes:
🎉 Perfect timing - you're our #100 member milestone!
Set milestoneInterval to 0 to disable the divisibility check and only use the notable milestones list.

Highlight channels

The highlightChannels array lists channel IDs that should be suggested in the greeting CTA. These are merged with the top active channels from the activity snapshot (active channels take priority, highlights fill the rest).
config.json
{
  "welcome": {
    "dynamic": {
      "highlightChannels": [
        "1438631182379253814",
        "1444154471704957069",
        "1446317676988465242"
      ]
    }
  }
}
Up to three channel IDs are mentioned in the message. Channels that no longer exist in the guild are silently skipped.
Dynamic mode ignores your message and variants fields. To keep a static fallback, set dynamic.enabled to false.

Onboarding panels

The welcome system supports a full onboarding flow triggered by the /welcome setup command. It posts interactive panels to configured channels.

Rules agreement panel

Set rulesChannel to a channel ID and verifiedRole to a role ID. When you run /welcome setup, the bot posts a message with an Accept Rules button. Members who click it receive the verified role. After accepting, if introChannel is set, the bot sends a prompt in that channel:
👋 Welcome <@member>! Drop a quick intro so we can meet you.
config.json
{
  "welcome": {
    "rulesChannel": "111222333444555666",
    "verifiedRole": "999888777666555444",
    "introChannel": "111222333444555667"
  }
}

DM sequence

Enable dmSequence to send a series of DM messages to the user immediately after they accept the rules. Each steps entry is sent as a separate DM in order.
config.json
{
  "welcome": {
    "dmSequence": {
      "enabled": true,
      "steps": [
        "Welcome! Here's a quick tour of the server.",
        "Check out #announcements for the latest news.",
        "Feel free to ask questions any time."
      ]
    }
  }
}
If the member has DMs disabled, delivery of the DM sequence stops at the first failed step. This is not fatal — the rules acceptance and role assignment still complete successfully.

Role menu on join

Enable roleMenu to post a dropdown in the welcome channel that lets members self-assign roles. Options support a label, a role ID, and an optional description.
config.json
{
  "welcome": {
    "roleMenu": {
      "enabled": true,
      "options": [
        { "label": "Frontend", "roleId": "111111111111111111", "description": "I build UIs" },
        { "label": "Backend", "roleId": "222222222222222222", "description": "I build APIs" },
        { "label": "DevOps", "roleId": "333333333333333333" }
      ]
    }
  }
}
Up to 25 options are supported. Options with an empty or missing roleId are filtered out automatically.
The bot’s role must be positioned above any role it assigns. If the verified role or any role menu option is higher in the hierarchy than the bot’s role, the assignment will fail.

Returning members

If a member rejoins (Discord sets the DidRejoin flag), the bot ignores your configured template and sends a fixed message instead:
Welcome back, <@member>! Glad to see you again. Jump back in whenever you are ready.

Commands

Posts the rules agreement panel and role menu to the configured channels. Requires moderator or administrator permission.
  • If rulesChannel is set, posts the Accept Rules button panel there.
  • If roleMenu.enabled is true and welcome.channelId is set, posts the role dropdown in the welcome channel.
The command reports which panels were posted and warns you about any missing configuration.

Build docs developers (and LLMs) love