---
title: "Temporary voice channels on Discord: the join to create system | StreetHosting"
description: "Build temporary voice channels with discord.js v14: someone joins the trigger, gets their own room and the bot deletes it when empty and cleans orphans."
url: "https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot"
type: "page"
language: "en-US"
---

Discord Bots · 8 min · Intermediate

Published on Nov 13, 2026 · Updated on Nov 13, 2026

# Temporary voice channels on Discord: how to build join to create

Instead of keeping dozens of empty voice rooms, leave a single trigger channel. When someone joins, the bot creates a room named after the person, moves them in and deletes the channel when everyone leaves.

By [Equipe StreetHosting](https://streethosting.com.br/en/autores#equipe-streethosting) · StreetHosting infrastructure and support team

[Getting started](https://streethosting.com.br/en/guides/topics/getting-started)

Summarize with:

[](https://chat.openai.com/?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "ChatGPT") [](https://claude.ai/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Claude") [](https://www.google.com/search?udm=50&aep=11&q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Google AI Mode") [](https://x.com/i/grok?text=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Grok") [](https://www.perplexity.ai/search/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Perplexity")

Share:

[](https://x.com/intent/tweet?text=Temporary%20voice%20channels%20on%20Discord%3A%20the%20join%20to%20create%20system&url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot "Share on X") [](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot "Share on Facebook") [](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot "Share on LinkedIn") [](https://wa.me/?text=Temporary%20voice%20channels%20on%20Discord%3A%20the%20join%20to%20create%20system%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-temporary-voice-channels-bot "Share on WhatsApp")

For agents: Copy as Markdown [.md](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot.md)

In this guide 7 sections

* [How join to create works](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#como-funciona)
* [Prepare intents, permissions and the trigger channel](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#preparar)
* [The complete code](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#codigo)
* [Give the owner control of the room](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#dono-da-sala)
* [Clean up orphan channels on restart](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#canais-orfaos)
* [Care points and API limits](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#cuidados)
* [Where to host this bot](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#onde-rodar)

Quick answer

Listen to the `voiceStateUpdate` event with the `Guilds` and `GuildVoiceStates` intents. When someone joins the trigger channel, create a voice channel with the person's name, give them permission to manage that room, move them with `member.voice.setChannel` and delete the channel when the last occupant leaves. Save the room IDs in a file or database to clean up orphans on restart.

## How join to create works[](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#como-funciona)

Servers with many people in calls tend to have a huge list of empty voice channels: Room 1, Room 2, Room 3, and so on. Nobody knows which one is free, nobody can lock a room for a group of friends and the sidebar becomes a mess. The temporary rooms system solves this with a single voice channel, called the trigger, usually named something like "Join to create".

1. The person joins the trigger channel.
2. The bot receives the voice state change event and creates a new channel in the same category.
3. The bot gives the owner permission to manage that channel and moves them inside.
4. When everyone leaves, the bot deletes the channel.

Discord has no native feature for this. Everything depends on a bot reacting to the `voiceStateUpdate` event, which fires every time someone joins, leaves, switches channels, mutes or turns on a camera. That is why the code has to compare the old state with the new one to know what actually changed.

## Prepare intents, permissions and the trigger channel[](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#preparar)

Before writing code, three preparations. First, create a category just for the temporary rooms, for example "Private rooms", and inside it the trigger voice channel. Second, copy the trigger's ID (with developer mode on in Discord's settings, right click the channel and choose copy ID) and store it in the `.env` file, next to the token, as `TRIGGER_CHANNEL_ID`.

Third, the permissions part. The bot needs these in the category, and it is simpler to grant them to its role right there:

| Permission               | What it does here                                                                                                  |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| Manage Channels          | Create the temporary channel, adjust its permissions and delete it when it empties                                 |
| Move Members             | Take the person from the trigger channel to the newly created room                                                 |
| Manage Permissions       | Create the permission rule in the room that gives the owner control. On the server role it is called Manage Roles  |
| View Channel and Connect | See the trigger channel and the rooms. Moving someone requires the bot to be able to connect to the target channel |

For intents, use `GatewayIntentBits.Guilds` and `GatewayIntentBits.GuildVoiceStates`. Neither is privileged, so declaring them in the code is enough. Without the second one the bot never receives the event, which is why the most common error in this tutorial is "the code is right and nothing happens".

## The complete code[](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#codigo)

Below is the core of the system, in a single file. The list of temporary channels lives in a `Map` (channel ID to owner ID) and is saved to disk on every change, which will be useful for the orphan cleanup further on.

`const fs = require('node:fs'); const { Client, GatewayIntentBits, ChannelType, Events } = require('discord.js'); const TRIGGER_ID = process.env.TRIGGER_CHANNEL_ID; const FILE = './temp-channels.json'; const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates], }); // temporary channel ID -> owner ID const tempChannels = new Map(); // Who has a room being created right now (avoids creating two) const creating = new Set(); function load() { try { const data = JSON.parse(fs.readFileSync(FILE, 'utf8')); for (const [channelId, ownerId] of Object.entries(data)) { tempChannels.set(channelId, ownerId); } } catch { // file does not exist yet, start empty } } function save() { fs.writeFileSync(FILE, JSON.stringify(Object.fromEntries(tempChannels))); } // Counts who is actually in the channel, using the server's voice states function occupants(channel) { return channel.guild.voiceStates.cache.filter((v) => v.channelId === channel.id).size; } async function createRoom(member, trigger) { if (creating.has(member.id)) return; creating.add(member.id); let room; try { room = await member.guild.channels.create({ name: (member.displayName + "'s room").slice(0, 100), type: ChannelType.GuildVoice, parent: trigger.parentId, }); await room.permissionOverwrites.edit(member, { ManageChannels: true, MoveMembers: true, }); tempChannels.set(room.id, member.id); save(); await member.voice.setChannel(room); } catch (error) { console.error('Failed to create the temporary room:', error); if (room) { tempChannels.delete(room.id); save(); await room.delete().catch(() => {}); } } finally { creating.delete(member.id); } } client.on(Events.VoiceStateUpdate, async (oldState, newState) => { // 1. Joined the trigger channel if (newState.channelId === TRIGGER_ID && oldState.channelId !== TRIGGER_ID) { await createRoom(newState.member, newState.channel); } // 2. Left a temporary room const leftId = oldState.channelId; if (leftId && leftId !== newState.channelId && tempChannels.has(leftId)) { const room = oldState.channel; if (room && occupants(room) === 0) { tempChannels.delete(leftId); save(); await room.delete().catch(() => {}); } } }); // If someone deletes the room by hand, forget the record client.on(Events.ChannelDelete, (channel) => { if (tempChannels.delete(channel.id)) save(); }); client.login(process.env.DISCORD_TOKEN);`

A few points in this code deserve attention. The snippet `oldState.channelId !== TRIGGER_ID` makes sure muting the microphone inside the trigger doesn't create repeated rooms. The `creating` Set covers the case of someone joining and leaving the trigger several times in a row, before the first creation finishes. And if the person has already disconnected when the bot tries to move them, the `setChannel` call fails and the error block deletes the room that was just born, instead of leaving it empty forever.

A channel name takes at most 100 characters, which is why `slice(0, 100)` is there. If you want another pattern, swap it for something like "Call with " plus the name, or use a counter. What Discord doesn't accept is an empty name.

## Give the owner control of the room[](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#dono-da-sala)

The line with `permissionOverwrites.edit` is what turns any room into the owner's room. It creates a permission exception just for that member and just in that channel:

* **Manage Channels:** the owner can rename the room and change the user limit and the bitrate through Discord itself, in the channel menu.
* **Move Members:** the owner can pull members into the room or kick them out without having that permission on the whole server.

Since the channel is born inside the trigger's category with no permissions of its own, it inherits the category rules, which keeps everything consistent with the rest of the server. If you want the room to be locked by default, also add a rule denying `Connect` to the `@everyone` role (that role's ID is the same as the server's ID) and allow `Connect`for the owner and for the bot itself, which otherwise can't move anyone into the room. Then the owner pulls in whoever they want.

The bot can only grant a permission that it has in that channel. If error 50013 (Missing Permissions) shows up when editing the permissions, review the bot's role in the category: it needs Manage Channels, Move Members and Manage Permissions there.

## Clean up orphan channels on restart[](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#canais-orfaos)

This is where most tutorials stop. The bot keeps the list of temporary channels in memory, and memory disappears when the process restarts, whether from a deploy, a crash or an update. The rooms created before stay on the server forever, empty, and the bot doesn't even know they exist. That is why the code above saves the IDs in `temp-channels.json`. All that is left is to use that file when the bot starts:

`client.once(Events.ClientReady, async () => { load(); for (const channelId of [...tempChannels.keys()]) { const channel = await client.channels.fetch(channelId).catch(() => null); if (!channel) { // already deleted, just forget it tempChannels.delete(channelId); continue; } if (occupants(channel) === 0) { await channel.delete().catch(() => {}); tempChannels.delete(channelId); } // still has people, keep it: the leave event deletes it later } save(); console.log('Cleanup done, ' + tempChannels.size + ' room(s) in use.'); });`

That way the bot deletes what was left empty during the downtime and keeps tracking the rooms that still have people, without dropping anyone's call. We use `Events.ClientReady` instead of the event's plain text because its name changed in recent discord.js versions, and the constant follows the installed version.

The JSON file is enough for a single server. If the bot serves several servers or you already use a database, store the same information in a table, as shown in the guide on [connecting the bot to a MongoDB database](https://streethosting.com.br/en/guides/discord-bots/connect-discord-bot-to-database). An alternative that needs no record at all is to treat the whole category as a temporary area: on startup, delete every empty voice channel in it that isn't the trigger.

## Care points and API limits[](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#cuidados)

* **Channel limit:** a category takes up to 50 channels and the server up to 500. If creation fails because of that, handle the error and tell the person instead of leaving the bot silent.
* **Rate limits:** creating and deleting channels has its own rate limit, and discord.js queues the calls by itself. On very busy servers, a minimum interval per user prevents abuse from people who join and leave the trigger nonstop. If 429 errors show up, see [how to fix the Discord rate limit](https://streethosting.com.br/en/guides/discord-bots/fix-discord-429-rate-limit).
* **Owner leaves first:** the code keeps the room while anyone is inside, even if the owner left. If you prefer to pass ownership on, store the new owner in the Map when the old one leaves.
* **Action log:** to know who created what, send one line to a log channel for each creation. The guide on [server logs and auditing](https://streethosting.com.br/en/guides/discord-bots/discord-logging-bot) shows how to set that up.

## Where to host this bot[](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#onde-rodar)

This bot has to be on all the time: if it goes down, nobody creates a new room and the empty ones pile up. On [StreetHosting bot hosting](https://streethosting.com.br/en/bots) the Node.js project runs in a container with a panel, and the Discord Essential plan, at R$ 9.99 per month, has 1 GB of RAM, 0.5 vCPU, 2 GB of NVMe and up to 2 bots, more than enough for a temporary rooms system. For the step by step on keeping the process alive, see [Discord bot hosting 24/7](https://streethosting.com.br/en/guides/discord-bots/discord-bot-hosting-24-7).

In this guide

* [How join to create works](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#como-funciona)
* [Prepare intents, permissions and the trigger channel](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#preparar)
* [The complete code](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#codigo)
* [Give the owner control of the room](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#dono-da-sala)
* [Clean up orphan channels on restart](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#canais-orfaos)
* [Care points and API limits](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#cuidados)
* [Where to host this bot](https://streethosting.com.br/en/guides/discord-bots/discord-temporary-voice-channels-bot#onde-rodar)

## Frequently asked questions

Which intents does the bot need for temporary voice channels?

Guilds and GuildVoiceStates. The second one delivers the voiceStateUpdate event, which tells you when someone joins, leaves or switches voice channels. Neither is privileged, so you don't need to turn anything on in the developer portal besides declaring the intent in the code.

Which permissions does the bot need?

Manage Channels to create and delete the rooms, Move Members to take the person from the trigger channel to the new room and Manage Permissions (shown as Manage Roles on the server role) to create the rule that gives the owner control of the room. All of them need to apply in the category where the rooms live, because the bot can only grant the owner a permission it has there itself.

What happens to the rooms if the bot restarts?

Without care, the rooms created before the crash stay forever, because the list of temporary channels was only in memory. The fix is to save the IDs in a file or database and, on startup, delete the empty ones and resume tracking the ones that still have people.

How many temporary rooms can I have at the same time?

A category holds up to 50 channels and the whole server up to 500. If the category fills up, creation fails. It is worth handling the error and telling the person, or spreading the rooms across more than one category.

Can the owner rename the room and limit slots?

Yes, if they get the Manage Channels permission on that room only. That lets them rename it and change the user limit, without any power over the rest of the server. Editing the room's permissions is a different permission (Manage Permissions), which this tutorial does not give the owner.

Next step

See bot plans

24/7 Discord bots with fast activation and support in Portuguese.

[See bot plans](https://streethosting.com.br/en/bots)

## Related guides

[Discord bots Intermediate Member count voice channel on Discord: how to build it with a bot A stats channel shows the member total in the name of a voice channel nobody can join. The trick is Discord's rename limit, which only allows 2 changes per 10 minutes per channel. 8 min Read guide](https://streethosting.com.br/en/guides/discord-bots/discord-member-count-voice-channel) [Discord bots Beginner How to create a Discord bot with slash commands Slash commands improve the user experience and make your bot easier to use. Here is a clear path to get started. 2 min Read guide](https://streethosting.com.br/en/guides/discord-bots/create-discord-bot-slash-commands) [Discord bots Intermediate How to log and audit a Discord server with a bot Without logs, you never know who deleted what. An audit bot records deleted messages, joins, leaves and moderation actions in a channel, giving your server a memory. 3 min Read guide](https://streethosting.com.br/en/guides/discord-bots/discord-logging-bot)

[← Back to the Guide Center](https://streethosting.com.br/en/guides)
