Threads

Threads are a new Discord feature. Threads can be thought of as temporary sub-channels inside an existing channel, to help better organize conversation in a busy channel.

Threads have been designed to be very similar to channel objects, and this topic aggregates all of the information about threads, which should all help to make migrating very straightforward.

Backwards Compatibility

Threads are only available in API v9. Users that do not update to API v9 will not receive most Gateway events for threads, or things that happen in threads (such as Message Create). Users on API v8 will still receive Gateway events for Interactions though.

The list of Gateway events that may be dropped includes, but is not limited to:

  • MESSAGE_CREATE
  • MESSAGE_DELETE
  • MESSAGE_DELETE_BULK
  • MESSAGE_REACTION_ADD
  • MESSAGE_REACTION_REMOVE
  • MESSAGE_REACTION_REMOVE_ALL
  • MESSAGE_REACTION_REMOVE_EMOJI
  • MESSAGE_UPDATE
  • THREAD_CREATE
  • THREAD_UPDATE
  • THREAD_DELETE
  • THREAD_MEMBER_UPDATE
  • THREAD_MEMBERS_UPDATE

Thread Fields

Since threads are a new type of channel, they share and re-purpose a number of the existing fields on a channel object:

  • id, guild_id, type, name, last_message_id, last_pin_timestamp, rate_limit_per_user are being re-used
  • owner_id has been repurposed to store the id of the user that started the thread
  • parent_id has been repurposed to store the id of the GUILD_TEXT or GUILD_NEWS channel the thread was created in

Additionally, there are a few new fields that are only available on threads:

  • member_count stores an approximate member count, but it stops counting at 50
  • message_count and total_message_sent store the number of messages in a thread. The difference is that when a message is deleted, message_count is decremented, but total_message_sent will not be (threads created before July 1, 2022 stop counting both values at 50).
  • thread_metadata contains a few thread specific fields, archived, archive_timestamp, auto_archive_duration, locked. archive_timestamp is changed when creating, archiving, or unarchiving a thread, and when changing the auto_archive_duration field.

Public & Private Threads

Public threads are viewable by everyone who can view the parent channel of the thread. Public threads must be created from an existing message, but can be "orphaned" if that message is deleted. The created thread and the message it was started from will share the same id. The type of thread created matches the type of the parent channel. GUILD_TEXT channels create PUBLIC_THREAD and GUILD_NEWS channels create NEWS_THREAD.

Private threads behave similar to group DMs, but in a guild. Private threads are always created with the PRIVATE_THREAD type and can only be created in GUILD_TEXT channels.

Active & Archived Threads

Every thread can be either active or archived. Changing a thread from archived -> active is referred to as unarchiving the thread. Threads that have locked set to true can only be unarchived by a user with the MANAGE_THREADS permission.

Besides helping to de-clutter the UI for users, archiving exists to limit the working set of threads that need to be kept around. Since the number of archived threads can be quite large, keeping all of them in memory may be quite prohibitive. Therefore guilds are capped at a certain number of active threads, and only active threads can be manipulated. Users cannot edit messages, add reactions, use application commands, or join archived threads. The only operation that should happen within an archived thread is messages being deleted. Sending a message will automatically unarchive the thread, unless the thread has been locked by a moderator.

Because of this constraint, the Gateway protocol is designed to ensure that users are able to have an accurate view of the full set of active threads, but archived threads are not synced up-front via the gateway.

Threads do not count against the max-channels limit in a guild, but there will be a new limit on the maximum number of active threads in a guild.

Threads automatically archive after a period of inactivity (as a guild approaches the max thread limit this timer will automatically lower, but never below the auto_archive_duration). "Activity" is defined as sending a message, unarchiving a thread, or changing the auto-archive time. The auto_archive_duration field previously controlled how long a thread could stay active, but is now repurposed to control how long the thread stays in the channel list. Channels can also set default_auto_archive_duration, which is used by our clients to pre-select a different auto_archive_duration value when a user creates a thread.

Permissions

Threads generally inherit permissions from the parent channel (e.g. if you can add reactions in the parent channel, you can do that in a thread as well).

Three permission bits are specific to threads: CREATE_PUBLIC_THREADS, CREATE_PRIVATE_THREADS, and SEND_MESSAGES_IN_THREADS.

Private threads are similar to group DMs, but in a guild: You must be invited to the thread to be able to view or participate in it, or be a moderator (MANAGE_THREADS permission).

Finally, threads are treated slightly differently from channels in the Gateway protocol. Clients will not be informed of a thread through the Gateway if they do not have permission to view that thread.

Gateway Events

  • Guild Create contains a new field, threads, which is an array of channel objects. For bots, this represents all active threads in the guild that the current user is able to view. For user accounts, only joined threads are sent.
  • When a thread is created, updated, or deleted, a Thread Create, Thread Update, or Thread Delete event is sent. Like their channel counterparts, these just contain a thread.
  • Since the Gateway only syncs active threads that the user can see, if a user gains access to a channel, then the Gateway may need to sync the active threads in that channel to the user. It will send a Thread List Sync event for this.

Thread Membership

Each thread tracks explicit membership. There are two primary use cases for this data:

  • Clients use their own thread member to calculate read states and notification settings.
  • Knowing everyone that is in a thread.

Membership is tracked in an array of thread member objects. These have four fields, id (the thread id), user_id, join_timestamp, and flags. Currently the only flags are for notification settings, but others may be added in future updates.

Syncing for the current user

  • A Thread Members Update Gateway Event is always sent when the current user is added to or removed from a thread.
  • A Thread Member Update Gateway Event is sent whenever the current user's thread member object is updated.
  • Certain API calls, such as listing archived threads and search will return an array of thread member objects for any returned threads the current user is a member of. Other API calls, such as getting a channel will return the thread member object for the current user as a property on the channel, if the current user is a member of the thread.
  • The Guild Create Gateway Event will contain a thread member object as a property on any returned threads the current is a member of.
  • The Thread Create Gateway Event will contain a thread member object as a property of the thread if the current user is a member of, and the user has recently gained access to view the parent channel.
  • The Thread List Sync Gateway Event will contain an array of thread member objects for any returned threads the current user is a member of.

Syncing for other users

Editing & Deleting Threads

Threads can be edited and deleted with the existing PATCH and DELETE endpoints to edit a channel.

  • Deleting a thread requires the MANAGE_THREADS permission.
  • Editing a thread to set archived to false only requires the current user has already been added to the thread. If locked is true, then the user must have MANAGE_THREADS.
  • Editing a thread to change the name, archived, auto_archive_duration fields requires MANAGE_THREADS or that the current user is the thread creator.
  • Editing a thread to change rate_limit_per_user or locked requires MANAGE_THREADS.

NSFW Threads

Threads do not explicitly set the nsfw field. All threads in a channel marked as nsfw inherit that setting though.

New Message Types

Threads introduce a few new message types, and re-purpose some others:

  • RECIPIENT_ADD and RECIPIENT_REMOVE have been repurposed to also send when a user is added to or removed from a thread by someone else.
  • CHANNEL_NAME_CHANGE has been repurposed and is sent when the thread's name is changed.
  • THREAD_CREATED is a new message sent to the parent GUILD_TEXT channel, used to inform users that a thread has been created. It is currently only sent in one case: when a PUBLIC_THREAD is created from an older message (older is still TBD, but is currently set to a very small value). The message contains a message reference with the guild_id and channel_id of the thread. The content of the message is the name of the thread.
  • THREAD_STARTER_MESSAGE is a new message sent as the first message in threads that are started from an existing message in the parent channel. It only contains a message reference field that points to the message from which the thread was started.

Enumerating Threads

There are four GET routes for enumerating threads in a specific channel:

Webhooks

Webhooks can send messages to threads by using the thread_id query parameter. See the Execute Webhook docs for more details.

While threads are mostly similar to channels in terms of structure and how they are synced, there are two important product requirements that lead to differences in how threads and channels are synced. This section helps explain the behavior behind the Thread List Sync and Thread Create dispatches by going over those problems and how they are solved.

The two product requirements are: The Gateway will only sync threads to a client that the client has permission to view, and it will only sync those threads once the client has "subscribed" to the guild. For context, in Discord's official clients, a subscription happens when the user visits a channel in the guild.

As mentioned, these lead to a couple of edge cases that are worth going into:

Details About Thread Access and Syncing

While the syncing of threads is similar to channels, there are two important differences that are relevant for Thread List Sync and Thread Create events:

  1. The Gateway will only sync threads that the user has permission to view.
  2. The Gateway will only sync threads once the user has "subscribed" to the guild. For context, in Discord's official clients, a subscription happens when the user visits a channel in the guild.

These differences mean there is some unique behavior that is worth going into.

Thread Access

Gaining Access to Private Threads

When a user is added to a private thread, it likely doesn't have that thread in memory yet since it doesn't have permission to view it.

Private threads are only synced to you if you are a member or a moderator. Whenever a user is added to a private thread, the Gateway also sends a Thread Create event. This ensures the client always has a non-null value for that thread.

Gaining Access to Public Threads

When a client is added to a public thread, but has not yet subscribed to threads, they might not have that public thread in memory yet. This is actually only a problem for user accounts, and not for bots. The Gateway will auto-subscribe bots to all thread dispatches and active threads on connect. But user accounts only receive threads that are active and they have also joined on connect in order to reduce the amount of data needed on initial connect. But this means when a user account is added to a thread, that thread now becomes an "active-joined" thread and needs to be synced to the client. To solve this, whenever a user is added to any thread, the Gateway also sends a Thread Create dispatch.

Channel Access

Gaining Access to Channels

When a user gains access to a channel (for example, they're given the moderator role), they likely won't have the threads in memory for that channel since the Gateway only syncs threads that the client has permission to view. To account for this, a Thread List Sync event is sent.

Losing Access to Channels

When a user loses access to a channel, the Gateway does not send it Thread Delete event (or any equivalent thread-specific event). Instead, the user will receive the event that caused its permissions on the channel to change.

If a user wanted to track when it lost access to any thread, it's possible but difficult as it would need to handle all cases correctly. Usually, events that cause permission changes are a Guild Role Update, Guild Member Update or Channel Update event.

Additionally, when a user loses access to a channel, they are not removed from the thread and will continue to be reported as a member of that thread. However, they will not receive any new Gateway events unless they are removed from the thread, in which case they will receive a Thread Members Update event.

Unarchiving a Thread

When a thread is unarchived, as user accounts only load active threads into memory on start, there is no guarantee that a user has the thread or its member status in memory. To account for this, the Gateway will send two events (in the listed order):

  1. A Thread Update event, which contains the full channel object.
  2. A Thread Member Update event, which is sent to all members of the unarchived thread, so users know they are a member and what their notification setting is.

Forums

A GUILD_FORUM channel is similar to a GUILD_TEXT channel, except only threads can be created in them. Unless otherwise noted, threads in forum channels behave in the same way as in text channels—meaning they use the same endpoints and receive the same Gateway events.

Media Channels

A GUILD_MEDIA channel is similar to a GUILD_FORUM channel. Similar to forum channel, only threads can be created in them. Unless otherwise noted, threads in media channels behave in the same way as in forum channel—meaning they use the same endpoints and receive the same Gateway events.

Creating Threads in Thread-Only Channels

Within a thread-only channel, threads appear as posts. They can be created using the Create Thread endpoint as threads in text channels, but with slightly different parameters. For example, when creating threads in a threads-only channel, a message is created that has the same ID as the thread. This requires you to pass parameters for both a thread and a message.

Threads in a thread-only channel have the same permissions behavior as threads in a text channel, inheriting all permissions from the parent channel, with one exception: creating a thread in a thread-only channel only requires the SEND_MESSAGES permission.

Thread-Only Channel Fields

It's worth calling out a few details about fields specific to thread-only channels that may be important to keep in mind:

  • The last_message_id field is the ID of the most recently created thread in that channel. As with messages, you will not receive a Channel Update event when the field is changed. Instead, clients should update the value when receiving Thread Create events.
  • The topic field is what is shown in the "Guidelines" section within clients.
  • The rate_limit_per_user field limits how frequently threads can be created. There is a new default_thread_rate_limit_per_user field on thread-only channels as well, which limits how often messages can be sent in a thread. This field is copied into rate_limit_per_user on the thread at creation time.
  • The available_tags field can be set when creating or updating a channel, which determines which tags can be set on individual threads within the thread's applied_tags field.

All fields for channels, including thread-only channels, can be found in the Channel Object.

Thread-Only Channel Thread Fields

A thread can be pinned within a thread-only channel, which is represented by the PINNED flag. A thread that is pinned will have the flag set, and archiving that thread will unset the flag. A pinned thread will not auto-archive.

The message_count and total_message_sent fields on threads in thread-only channels will increment on Message Create events, and decrement on Message Delete and Message Delete Bulk events. There will be no specific Channel Update event that notifies you of changes to those fields—instead, you should update those values when receiving corresponding events.

All fields for threads in thread-only channels can be found in the channel resource documentation.