Gateway capabilities — opt-in protocol behaviour sent in the IDENTIFY payload.
Capabilities let a bot enable a gateway behaviour before Discord makes it mandatory, so the change can be observed and handled ahead of its deadline.
Configuration
config :eda, capabilities: [:channel_obfuscation]
config :eda, capabilities: :channel_obfuscation # single capability
config :eda, capabilities: 32768 # raw bitfieldUnset (the default) sends no capabilities field at all, so gateway behaviour
is unchanged.
Channel obfuscation
:channel_obfuscation (1 <<< 15) opts into Discord's redaction of channels the
bot cannot see. Once enabled — and for every bot from 2026-11-16, whether opted
in or not — such channels are still dispatched over the gateway but caviarded:
namebecomes"___hidden___",- sensitive fields are nulled or reduced,
permission_overwritesholds a single overwrite denyingVIEW_CHANNELto the guild's@everyonerole,- the channel's
flagscarryCHANNEL_OBFUSCATED(1 <<< 17).
Interaction payloads are built through a separate path and are never obfuscated.
REST is not obfuscated
Discord's changelog states GET /guilds/{guild.id}/channels omits inaccessible
channels. That is not the observed behaviour. Probed against a real guild on
2026-09-19 with this capability enabled: all 44 obfuscated channels were returned
by REST, with their real name, flags: 0 and their genuine permission
overwrites — only the gateway payloads were redacted. Treat the two sources as
disagreeing, and do not rely on either to hide a channel.
The same opt-in is available as the "Private Channel Obfuscation" toggle in the Developer Portal; this option is the programmatic equivalent.
Cache and permissions
Obfuscated channels are cached like any other, so EDA.Cache.channels_for_guild/1
returns "___hidden___" entries — reject them with EDA.Channel.obfuscated?/1 before
showing a channel list. EDA.Permission.in_channel/3 already refuses to compute from
the synthetic overwrite and returns {:error, :channel_obfuscated}.
Summary
Functions
Returns all capability names EDA knows about.
Checks whether a capability is enabled in a bitfield.
Converts capabilities to a bitfield integer.
Functions
@spec all_capabilities() :: [atom()]
Returns all capability names EDA knows about.
@spec has_capability?(non_neg_integer(), atom()) :: boolean()
Checks whether a capability is enabled in a bitfield.
Examples
iex> EDA.Gateway.Capabilities.has_capability?(32768, :channel_obfuscation)
true
iex> EDA.Gateway.Capabilities.has_capability?(0, :channel_obfuscation)
false
@spec to_bitfield(non_neg_integer() | atom() | [atom()]) :: non_neg_integer()
Converts capabilities to a bitfield integer.
Accepts a list of atoms, a single atom, or a raw integer. A raw integer passes through unchanged so that capabilities Discord adds later can be sent without a library update.
Examples
iex> EDA.Gateway.Capabilities.to_bitfield([])
0
iex> EDA.Gateway.Capabilities.to_bitfield([:channel_obfuscation])
32768
iex> EDA.Gateway.Capabilities.to_bitfield(:channel_obfuscation)
32768
iex> EDA.Gateway.Capabilities.to_bitfield(41)
41