Kick, ban, and timeout commands

Give your bot /kick, /ban, and /timeout — with permission gates, role-hierarchy checks, and reasons that show up in the audit log — in one discord.js file.

Skip the setup This guide has a one-click starter template that installs everything below onto your server.

This recipe adds the three moderation commands almost every server wants — /kick, /ban, and /timeout — to a discord.js bot. Each one takes a member and an optional reason, refuses to act on someone the bot can't touch, and writes the reason into Discord's audit log.

At a glance
You need a working bot from Host a discord.js bot
Plan Free or premium
Time About twenty minutes

New to bots? Build the discord.js starter first — this recipe assumes you already have a server running the Node.js application with your token in .env, and it reuses that guide's Client + Events structure.

The code

This is the whole index.js. It registers three commands and answers them in one InteractionCreate handler.

require('dotenv').config();
const {
  Client, Events, GatewayIntentBits,
  SlashCommandBuilder, PermissionFlagsBits,
} = require('discord.js');

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

const commands = [
  new SlashCommandBuilder()
    .setName('kick')
    .setDescription('Kick a member')
    .addUserOption(o => o.setName('member').setDescription('Who to kick').setRequired(true))
    .addStringOption(o => o.setName('reason').setDescription('Why').setMaxLength(400))
    .setDefaultMemberPermissions(PermissionFlagsBits.KickMembers),
  new SlashCommandBuilder()
    .setName('ban')
    .setDescription('Ban a member')
    .addUserOption(o => o.setName('member').setDescription('Who to ban').setRequired(true))
    .addStringOption(o => o.setName('reason').setDescription('Why').setMaxLength(400))
    .setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
  new SlashCommandBuilder()
    .setName('timeout')
    .setDescription('Time a member out')
    .addUserOption(o => o.setName('member').setDescription('Who to time out').setRequired(true))
    .addIntegerOption(o => o.setName('minutes').setDescription('How long, in minutes').setRequired(true).setMinValue(1).setMaxValue(40320))
    .addStringOption(o => o.setName('reason').setDescription('Why').setMaxLength(400))
    .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
];

client.once(Events.ClientReady, async (c) => {
  await c.application.commands.set(commands);
  console.log(`Listening as ${c.user.tag}`);
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;

  const target = interaction.options.getMember('member');
  const reason = interaction.options.getString('reason') ?? 'No reason given';

  if (!target) {
    return interaction.reply({ content: 'That user is not in this server.', ephemeral: true });
  }

  if (interaction.commandName === 'kick') {
    if (!target.kickable) {
      return interaction.reply({ content: "I can't kick that member — my role is too low or they're above me.", ephemeral: true });
    }
    await target.kick(reason);
    await interaction.reply(`👢 Kicked **${target.user.tag}** — ${reason}`);
  }

  if (interaction.commandName === 'ban') {
    if (!target.bannable) {
      return interaction.reply({ content: "I can't ban that member — my role is too low or they're above me.", ephemeral: true });
    }
    await target.ban({ reason, deleteMessageSeconds: 60 * 60 * 24 });
    await interaction.reply(`🔨 Banned **${target.user.tag}** — ${reason}`);
  }

  if (interaction.commandName === 'timeout') {
    if (!target.moderatable) {
      return interaction.reply({ content: "I can't time out that member — my role is too low or they're above me.", ephemeral: true });
    }
    const minutes = interaction.options.getInteger('minutes');
    await target.timeout(minutes * 60 * 1000, reason);
    await interaction.reply(`⏳ Timed out **${target.user.tag}** for ${minutes} min — ${reason}`);
  }
});

client.login(process.env.DISCORD_TOKEN);

Paste it into index.js, save, and restart. After the usual first-time delay, /kick, /ban, and /timeout appear.

The parts that matter

Only a few pieces are new compared with the /ping bot:

  • .setDefaultMemberPermissions(PermissionFlagsBits.KickMembers) hides the command from anyone who lacks that permission. This is Discord's own gate — a member without Kick Members never even sees /kick in the menu, so you don't have to check permissions in code.
  • interaction.options.getMember('member') returns the guild member the moderator picked (not just a User), which is what you need to kick or ban. Discord sends this member inside the interaction, so it works without the Server Members intent. It's null if the person already left — hence the early "not in this server" reply.
  • kickable / bannable / moderatable are the safety checks. Each is true only when the bot can act on that member — the bot's highest role is above theirs and the bot has the permission. Checking these first turns a red error in your console into a clean "I can't touch that member" reply.
  • target.timeout(ms, reason) takes milliseconds, which is why the code multiplies minutes by 60 * 1000. The setMaxValue(40320) caps it at Discord's real limit: 28 days.
  • deleteMessageSeconds on the ban wipes the last 24 hours of the banned user's messages. Set it to 0 to keep their history.
  • The reason passed to kick/ban/timeout is what shows up in Server Settings → Audit Log — worth filling in so future-you knows why.

Permissions and intents

This bot needs no privileged intentsGatewayIntentBits.Guilds is enough, and nothing extra to toggle in the Developer Portal. What it does need is permission inside the server, plus the right role position.

Command Bot permission it needs
/kick Kick Members
/ban Ban Members
/timeout Moderate Members

⚠️ Heads up: Discord's role hierarchy is absolute. Your bot can only kick, ban, or time out members whose highest role sits below the bot's highest role — no permission overrides this. Drag the bot's role up in Server Settings → Roles so it's above the members it should manage. That's exactly what the kickable/bannable/moderatable checks are protecting you from.

Grant the permissions when you invite the bot (tick them in the OAuth2 URL generator) or by giving the bot's role those permissions later.

Making it yours

  • Log actions to a channel. Instead of only replying, build an embed and send it to a mod-log channel so there's a record.
  • Add /unban. Bans are the one action that outlives the member leaving — interaction.guild.bans.remove(userId, reason) reverses one. Use a String option for the user ID (an ex-member won't show in a user picker).
  • Require a reason. Add .setRequired(true) to the reason option if you want to force moderators to explain themselves.
  • Stop it clashing with your other commands. application.commands.set() replaces your whole command list, so if you already have commands, add these to the same array rather than calling set twice.

Troubleshooting

  • "Missing Permissions" in the console — the bot's role doesn't have Kick/Ban/Moderate Members, or it isn't high enough in the role list. Fix both in Server Settings → Roles.
  • The command replies "I can't touch that member" — that's the hierarchy check doing its job: the target's role is at or above the bot's. Move the bot's role higher.
  • /timeout errors on a big number — timeouts max out at 28 days; the setMaxValue(40320) keeps the input in range, but check you're passing minutes, not seconds.
  • Commands don't appear — first-time global registration lags a few minutes. Still missing? Re-invite with the applications.commands scope. See Bot appears offline.

Next steps

Was this guide helpful?