fix(guild): render activity log entries deterministically (#2766)

This commit is contained in:
Hampus Kraft committed 2026-09-14 17:39:19 +02:00
1 parent ed10f9d323
commit a3d6cf37cb
135 files changed
+75869 -2708

No files matched your search

@@ -317,7 +317,7 @@ On a deployment that configures no voice topology, a missing channel, an invisib
## Modify channel
<RouteHeader method="PATCH" path="/v1/channels/{channel_id}" bot />
<RouteHeader method="PATCH" path="/v1/channels/{channel_id}" bot auditReason />
Modifies a guild channel or a group direct message and returns the updated [channel object](#channel-object). Emits a [Channel Update](/gateway/events/#channel-update) Gateway event.
@@ -455,7 +455,7 @@ A guild channel change emits [Channel Update](/gateway/events/#channel-update) t
Changing `rate_limit_per_user` clears the remaining slowmode delay of every user in the channel, so each user's next message is checked against the new interval. Changing `rtc_region` on a guild voice channel moves its active voice connections to the selected region.
A request that changes at least one field creates a channel update audit log entry with no reason. Supplying `permission_overwrites` also creates one overwrite create, update, or delete audit log entry for each overwrite the array adds, changes, or removes.
A request that changes at least one field creates a channel update audit log entry with the supplied reason. Supplying `permission_overwrites` also creates one overwrite create, update, or delete audit log entry for each overwrite the array adds, changes, or removes.
A group direct message change emits [Channel Update](/gateway/events/#channel-update) to every current recipient. A name or successful icon change creates a system message delivered with [Message Create](/gateway/events/#message-create), and replacing the icon permanently deletes the previous one. Ownership and nickname changes create no system message and no audit entry.
@@ -465,7 +465,7 @@ A group direct message change emits [Channel Update](/gateway/events/#channel-up
## Delete or leave channel
<RouteHeader method="DELETE" path="/v1/channels/{channel_id}" bot />
<RouteHeader method="DELETE" path="/v1/channels/{channel_id}" bot auditReason />
Deletes a guild channel, closes a direct message, or removes the caller from a group direct message. Returns 204 with an empty body. Emits a [Channel Delete](/gateway/events/#channel-delete) Gateway event.
@@ -682,7 +682,7 @@ Removing the final recipient permanently deletes the channel's messages, removes
## Set channel permission overwrite
<RouteHeader method="PUT" path="/v1/channels/{channel_id}/permissions/{overwrite_id}" bot />
<RouteHeader method="PUT" path="/v1/channels/{channel_id}/permissions/{overwrite_id}" bot auditReason />
Creates or replaces one permission overwrite on a guild channel and returns 204 with an empty body. Requires [MANAGE_ROLES](/http-api/permissions/) in that channel. Emits a [Channel Update](/gateway/events/#channel-update) Gateway event.
@@ -737,7 +737,7 @@ A larger decimal string returns 400 `INVALID_FORM_BODY` with the code `INTEGER_O
Fluxer emits [Channel Update](/gateway/events/#channel-update) to every session subscribed to the guild, including when the submitted overwrite exactly matches the stored one. When the channel is a category, every child whose overwrites exactly matched the category's previous values receives a copy of the category's new overwrites and its own Channel Update.
The operation records an overwrite create or update audit entry with no reason. An unchanged overwrite records no audit entry.
The operation records an overwrite create or update audit entry with the supplied reason. An unchanged overwrite records no audit entry.
### Rate limit
@@ -745,7 +745,7 @@ The operation records an overwrite create or update audit entry with no reason.
## Delete channel permission overwrite
<RouteHeader method="DELETE" path="/v1/channels/{channel_id}/permissions/{overwrite_id}" bot />
<RouteHeader method="DELETE" path="/v1/channels/{channel_id}/permissions/{overwrite_id}" bot auditReason />
Deletes one permission overwrite from a guild channel and returns 204 with an empty body. Requires [MANAGE_ROLES](/http-api/permissions/) in that channel. Emits a [Channel Update](/gateway/events/#channel-update) Gateway event.
@@ -774,7 +774,7 @@ The operation is idempotent, does not require `VIEW_CHANNEL`, and never returns
### Side effects
Fluxer emits [Channel Update](/gateway/events/#channel-update) to every session subscribed to the guild, including when the identifier names no existing overwrite. Removing an overwrite that exists also records an overwrite delete audit entry with the previous overwrite state and no reason. An identifier that names no existing overwrite records no audit entry. When the target channel is a category, each child whose overwrites exactly matched the category's previous overwrites receives a copy of the category's new overwrites and its own Channel Update.
Fluxer emits [Channel Update](/gateway/events/#channel-update) to every session subscribed to the guild, including when the identifier names no existing overwrite. Removing an overwrite that exists also records an overwrite delete audit entry with the previous overwrite state and the supplied reason. An identifier that names no existing overwrite records no audit entry. When the target channel is a category, each child whose overwrites exactly matched the category's previous overwrites receives a copy of the category's new overwrites and its own Channel Update.
### Rate limit
@@ -6,7 +6,7 @@ description: The experiment assignments envelope, the revalidation and polling c
import RouteHeader from '@/components/RouteHeader.astro';
An experiment is an instance-wide rollout that an operator configures. For each account, Fluxer works out from that configuration whether the account is in the rollout and which settings the account receives. The single route on this page resolves every experiment the server defines and returns them in one envelope, together with the polling cadence they share. This server defines `voice_noise_suppression`, whose placement protocol [Voice](/voice/) defines, `message_hover_tracking`, which selects one of two client implementations of the message hover state, `message_keyboard_focus`, which selects one of two client implementations of keyboard navigation in the message list, and `blocked_message_groups`, which selects how a client renders a revealed block of blocked messages.
An experiment is an instance-wide rollout that an operator configures. For each account, Fluxer works out from that configuration whether the account is in the rollout and which settings the account receives. The single route on this page resolves every experiment the server defines and returns them in one envelope, together with the polling cadence they share. This server defines `voice_noise_suppression`, whose placement protocol [Voice](/voice/) defines, `message_hover_tracking`, which selects one of two client implementations of the message hover state, `message_keyboard_focus`, which selects one of two client implementations of keyboard navigation in the message list, `blocked_message_groups`, which selects how a client renders a revealed block of blocked messages, and `guild_activity_log_presentation`, which selects one of two client renderings of the community activity log.
Every assignment is advice. A client that ignores one behaves as it does with the rollout off, and no route and no Gateway event reports what a client actually ran.
@@ -36,10 +36,11 @@ One entry per experiment. The envelope reports this object even when it is empty
| message_hover_tracking? | [message hover tracking assignment](#message-hover-tracking-assignment-object) object | The caller's message hover tracking assignment |
| message_keyboard_focus? | [message keyboard focus assignment](#message-keyboard-focus-assignment-object) object | The caller's message keyboard focus assignment |
| blocked_message_groups? | [blocked message groups assignment](#blocked-message-groups-assignment-object) object | The caller's blocked message groups assignment |
| guild_activity_log_presentation? | [guild activity log presentation assignment](#guild-activity-log-presentation-assignment-object) object | The caller's guild activity log presentation assignment |
Ignore unknown experiments and treat a missing experiment as off.
This server version writes `voice_noise_suppression`, `message_hover_tracking`, `message_keyboard_focus`, and `blocked_message_groups` on every response, including while a rollout is disabled. The disabled value is the first [resolution outcome](#resolution-outcomes) below, which reports `enabled` false and the stored `config_version`, so a client that compares `config_version` with the value from its previous response can see that an operator saved the configuration, even while the rollout stays disabled, and needs no second request for it.
This server version writes `voice_noise_suppression`, `message_hover_tracking`, `message_keyboard_focus`, `blocked_message_groups`, and `guild_activity_log_presentation` on every response, including while a rollout is disabled. The disabled value is the first [resolution outcome](#resolution-outcomes) below, which reports `enabled` false and the stored `config_version`, so a client that compares `config_version` with the value from its previous response can see that an operator saved the configuration, even while the rollout stays disabled, and needs no second request for it.
## Noise suppression backends
@@ -186,6 +187,33 @@ A caller is drawn either by the operator's allowlist, which sets `source` to `us
`config_version` reports the stored revision in all outcomes, the rollout being off included. A client branches on `user_targeted` alone.
## Guild activity log presentation assignment object
One resolution of the instance guild activity log presentation rollout against one account. Every field is present whenever the key is written.
The rollout selects how a client renders the community activity log. A drawn client renders each entry as a summary sentence followed by its recorded changes. A client that is not drawn keeps the rendering it ships with. Neither arm changes what the API returns, and no route reports which arm a client ran.
### Structure
| Field | Type | Description |
| --- | --- | --- |
| enabled | boolean | Whether the rollout is running on this instance |
| config_version | integer | The revision of the instance configuration this assignment was resolved from |
| user_targeted | boolean | Whether the caller is inside the rollout |
| source | ?string | Which rule targeted the caller, one of `user_rule` or `canary`, and null where the caller is not targeted |
### Activity log presentation resolution outcomes
The outcomes below set `user_targeted` to false, and they differ in what else they report.
1. The rollout is off. `enabled` is false and `source` is null.
2. The operator has excluded the caller. `enabled` is true and `source` is null.
3. The caller was not drawn. `enabled` is true and `source` is null.
A caller is drawn either by the operator's allowlist, which sets `source` to `user_rule`, or by the sampled share of the account population, which sets `source` to `canary`. The blocklist is read before the allowlist, so an account named in both is not drawn.
`config_version` reports the stored revision in all outcomes, the rollout being off included. A client branches on `user_targeted` alone.
## Get experiment assignments
<RouteHeader method="GET" path="/v1/experiments" bot />
@@ -14,10 +14,19 @@ A route marked Audit reason accepts the `X-Audit-Log-Reason` request header, lis
Fluxer trims the value. A value that is blank before trimming, empty after it, or longer than 512 characters after it counts as no reason at all. None of those values fails the request, so a reason that misses the bound is dropped and the operation still succeeds.
The operation writes the accepted reason onto its entry, returns it as the entry `reason` field, and sends it in the [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) Dispatch.
The channel and message routes that write entries record the header as well: [Modify channel](/http-api/channels/#modify-channel), [Delete or leave channel](/http-api/channels/#delete-or-leave-channel), [Set channel permission overwrite](/http-api/channels/#set-channel-permission-overwrite), [Delete channel permission overwrite](/http-api/channels/#delete-channel-permission-overwrite), [Delete message](/http-api/messages/#delete-message), [Bulk delete messages](/http-api/messages/#bulk-delete-messages), [Pin message](/http-api/messages/#pin-message), and [Unpin message](/http-api/messages/#unpin-message).
Two operations use a reason from the request body when the header supplies none:
- [Create or replace guild ban](/http-api/guild-moderation/#create-or-replace-guild-ban) uses the body `reason`.
- [Modify guild member](/http-api/guild-members/#modify-guild-member) uses the trimmed `timeout_reason` when the body also has `communication_disabled_until`.
Fluxer never percent-decodes a body reason either. The operation writes the accepted reason onto its entry, returns it as the entry `reason` field, and sends it in the [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) Dispatch.
Fluxer resolves the reason again when it reads an entry. A [MEMBER_BAN_ADD](#audit-actions) entry with a blank stored reason returns the ban `reason` from its change list, and a [MEMBER_UPDATE](#audit-actions) entry with a blank stored reason returns the timeout reason stored with it, when it has one. The result is trimmed, and a blank result is omitted.
:::note[Some operations record no reason at all]
Channel modification and deletion, message deletion, bulk message deletion, pinning, unpinning, bot addition, and most [permission overwrite](#permission-overwrite-change-fields) entries never have one.
Bot addition, the role Fluxer creates and assigns to a bot it adds with permissions, a Visionary role grant, a message deletion made by [Delete message attachment](/http-api/messages/#delete-message-attachment), and a consolidated bulk deletion entry never have one.
:::
## Guild audit log response object
@@ -34,11 +43,20 @@ One page of audit history together with the accounts and webhooks it references.
<sup>1</sup> The page ordering is defined by the [query parameters](#query-parameters) of the request that produced it
<sup>2</sup> Deduplicated by user ID, and has no user named only by an option field or by a change value
<sup>2</sup> Deduplicated by user ID, and a referenced value that is not a snowflake names no account
<sup>3</sup> One object for each distinct webhook that a returned webhook action targets and that still exists, so a deleted webhook resolves to no object
`users` holds the actor of every returned entry and, for an action whose `target_id` names an account, that account as well. An account that can no longer be resolved is still returned, as the deleted-account representation with the username `DeletedUser`, the discriminator `0000`, the global name `Deleted User`, a null avatar, a null avatar colour, and `flags` of `0`.
`users` has the actor of every returned entry and each account the entry references:
- `target_id` of every action from [MEMBER_KICK](#audit-actions) through [MEMBER_DISCONNECT](#audit-actions), and of [BOT_ADD](#audit-actions)
- `target_id` of a [channel overwrite action](#audit-actions) whose `options.type` is `1`
- the `owner_id` change `new_value` of [GUILD_UPDATE](#audit-actions)
- the `moderator_id` change `old_value` of [MEMBER_BAN_REMOVE](#audit-actions)
- `options.inviter_id` of [INVITE_DELETE](#audit-actions)
- the `creator_id` change `old_value` of [WEBHOOK_DELETE](#audit-actions), [EMOJI_DELETE](#audit-actions), and [STICKER_DELETE](#audit-actions)
An account that can no longer be resolved is still returned, as the deleted-account representation with the username `DeletedUser`, the discriminator `0000`, the global name `Deleted User`, a null avatar, a null avatar colour, and `flags` of `0`.
## Guild audit log entry object
@@ -60,7 +78,7 @@ One recorded guild change. An entry is immutable once written, except for a run
<sup>2</sup> A decimal snowflake string for every action except [INVITE_CREATE](#audit-actions) and [INVITE_DELETE](#audit-actions), where it is the invite code, and null for [MESSAGE_BULK_DELETE](#audit-actions)
<sup>3</sup> The field is omitted when the operation recorded no reason, and an entry written by consolidation never has one
<sup>3</sup> The field is omitted when no non-blank reason resolves as described under [audit log reason](#audit-log-reason), so an entry written by consolidation never has one
<sup>4</sup> The field is omitted when the action recorded no option key that this registry publishes
@@ -85,7 +103,7 @@ Entries remain available for 45 days. A consolidated entry starts a new 45-day p
## Audit actions
The Value column is the numeric `action_type`, which is also the value the `action_type` query filter accepts. Each description names what `target_id` identifies and which [audit log options](#audit-log-options-object) the action records. The overwrite options are `id`, `type`, and `channel_id`. The invite options are `max_age`, `max_uses`, `temporary`, `channel_id`<sup>7</sup>, and `inviter_id`<sup>7</sup>.
The Value column is the numeric `action_type`, which is also the value the `action_type` query filter accepts. Each description names what `target_id` identifies and which [audit log options](#audit-log-options-object) the action records. The overwrite options are `id`, `type`, `channel_id`, and `role_name`<sup>10</sup>. The invite options are `max_age`, `max_uses`, `temporary`, `channel_id`<sup>7</sup>, and `inviter_id`<sup>7</sup>.
| Value | Name | Description |
| --- | --- | --- |
@@ -98,15 +116,15 @@ The Value column is the numeric `action_type`, which is also the value the `acti
| 15 | CHANNEL_OVERWRITE_DELETE | Channel permission overwrite was deleted. `target_id` is the role or member, and the action records the overwrite options |
| 20 | MEMBER_KICK<sup>2</sup> | Member was removed by a moderator. `target_id` is the user, and the action records no options |
| 21 | MEMBER_PRUNE<sup>3</sup> | Inactive members were pruned. `target_id` is nothing, and the action records no options |
| 22 | MEMBER_BAN_ADD | Guild ban was created or replaced. `target_id` is the user, and the action records `delete_member_days`<sup>4</sup> |
| 22 | MEMBER_BAN_ADD | Guild ban was created or replaced. `target_id` is the user, and the action records `delete_message_seconds`<sup>4</sup> |
| 23 | MEMBER_BAN_REMOVE | Guild ban was removed. `target_id` is the user, and the action records no options |
| 24 | MEMBER_UPDATE<sup>5</sup> | Guild member state was changed. `target_id` is the user, and the action records no options |
| 25 | MEMBER_ROLE_UPDATE<sup>5</sup> | One role was added to or removed from a member. `target_id` is the user, and the action records no options |
| 24 | MEMBER_UPDATE | Guild member state was changed. `target_id` is the user, and the action records no options |
| 25 | MEMBER_ROLE_UPDATE | One role was added to or removed from a member. `target_id` is the user, and the action records `role_name`<sup>5</sup> |
| 26 | MEMBER_MOVE<sup>6</sup> | Member voice session was moved to another channel. `target_id` is the user, and the action records `channel_id` and `count` |
| 27 | MEMBER_DISCONNECT<sup>6</sup> | Member voice session was disconnected. `target_id` is the user, and the action records `channel_id` and `count` |
| 28 | BOT_ADD | Bot was added to the guild. `target_id` is the user, and the action records `temporary` |
| 30 | ROLE_CREATE | Role was created. `target_id` is the role, and the action records no options |
| 31 | ROLE_UPDATE | Role was changed. `target_id` is the role, and the action records no options |
| 31 | ROLE_UPDATE | Role was changed. `target_id` is the role, and the action records `role_name` |
| 32 | ROLE_DELETE | Role was deleted. `target_id` is the role, and the action records no options |
| 40 | INVITE_CREATE | Invite was created. `target_id` is the invite code, and the action records the invite options |
| 41 | INVITE_UPDATE<sup>8</sup> | Invite was changed. `target_id` is the invite code, and the action records no options |
@@ -117,7 +135,7 @@ The Value column is the numeric `action_type`, which is also the value the `acti
| 60 | EMOJI_CREATE | Emoji was created. `target_id` is the emoji, and the action records no options |
| 61 | EMOJI_UPDATE | Emoji was changed. `target_id` is the emoji, and the action records no options |
| 62 | EMOJI_DELETE | Emoji was deleted. `target_id` is the emoji, and the action records no options |
| 72 | MESSAGE_DELETE | Single message was deleted by a moderator. `target_id` is the message, and the action records `channel_id` |
| 72 | MESSAGE_DELETE | Single message was deleted. `target_id` is the message, and the action records `channel_id` |
| 73 | MESSAGE_BULK_DELETE<sup>9</sup> | Several messages were deleted in one operation. `target_id` is nothing, and the action records `channel_id` and `count` |
| 74 | MESSAGE_PIN | Message was pinned. `target_id` is the message, and the action records `channel_id` and `message_id` |
| 75 | MESSAGE_UNPIN | Message was unpinned. `target_id` is the message, and the action records `channel_id` and `message_id` |
@@ -131,11 +149,11 @@ The Value column is the numeric `action_type`, which is also the value the `acti
<sup>3</sup> The value is defined by the wire contract and is accepted by the `action_type` filter, and no current guild operation records it
<sup>4</sup> The option is always recorded. Its value is the legacy `delete_message_days` body field of [Create or replace guild ban](/http-api/guild-moderation/#create-or-replace-guild-ban), which defaults to `0`, so a ban that used `delete_message_seconds` or sent no body at all records `"0"`
<sup>4</sup> The option is recorded when the resolved message deletion window of the ban is positive. An older ban entry can have the deprecated `delete_member_days` option
<sup>5</sup> The entry has no `options`, so a member role change does not name the role it added or removed. The caller reads that from the [guild member change fields](#guild-member-change-fields)
<sup>5</sup> Recorded when the role exists at the write. The role ID is the difference between the `roles` change values in the [guild member change fields](#guild-member-change-fields)
<sup>6</sup> `count` is always `1`. `channel_id` is the destination channel for a move and the vacated channel for a disconnect
<sup>6</sup> `count` is always `1`, and `channel_id` is the destination channel for a move and the vacated channel for a disconnect. A move into the channel the session already occupies records no entry
<sup>7</sup> The option is recorded only when the recorded entity has that value, so an invite with no channel or no inviter records neither, and a webhook with no channel records no option at all
@@ -143,6 +161,12 @@ The Value column is the numeric `action_type`, which is also the value the `acti
<sup>9</sup> A bulk deletion entry is recorded either by a bulk message deletion operation or by the consolidation described under [List guild audit logs](#list-guild-audit-logs)
<sup>10</sup> Recorded for an overwrite whose target is a role other than `@everyone`, when that role exists at the write
:::note[An update that changes nothing records no entry]
GUILD_UPDATE, CHANNEL_UPDATE, CHANNEL_OVERWRITE_UPDATE, MEMBER_UPDATE, MEMBER_ROLE_UPDATE, MEMBER_MOVE, ROLE_UPDATE, WEBHOOK_UPDATE, EMOJI_UPDATE, and STICKER_UPDATE need a change other than `ip`. GUILD_UPDATE also ignores `guild_id`, `member_count`, and the image width and height fields. A reason alone records nothing.
:::
## Audit log options object
Every field is present only for the actions listed against it in the [audit actions](#audit-actions) registry. Only the keys in this registry are published, and the response omits `options` entirely when an entry recorded none of them.
@@ -154,25 +178,30 @@ Every field is present only for the actions listed against it in the [audit acti
| channel_id | string | The decimal ID of the channel the action concerns |
| count<sup>1</sup> | number | The number of entities the action affected |
| delete_member_days<sup>2</sup> | string | The whole days of the banned member's messages the ban deleted |
| delete_message_seconds<sup>3</sup> | number | The seconds of the banned member's messages the ban deleted |
| id | string | The decimal ID of the role or member an overwrite applies to |
| integration_type<sup>3</sup> | number | The type of the integration the entry names |
| integration_type<sup>4</sup> | number | The type of the integration the entry names |
| message_id | string | The decimal ID of the single message the action concerns |
| members_removed<sup>3</sup> | number | The number of memberships the action removed |
| role_name<sup>3</sup> | string | The name of the role the action concerns |
| type<sup>4</sup> | number | The channel type, or the permission overwrite type |
| members_removed<sup>4</sup> | number | The number of memberships the action removed |
| role_name<sup>5</sup> | string | The name of the role the action concerns, as it was when the entry was written |
| type<sup>6</sup> | number | The channel type, or the permission overwrite type |
| inviter_id | string | The decimal ID of the user that created the invite |
| max_age | number | The configured invite lifetime in seconds |
| max_uses | number | The configured maximum use count |
| temporary | boolean | Whether the recorded entity is temporary |
| uses<sup>3</sup> | number | The invite use count recorded with the action |
| uses<sup>4</sup> | number | The invite use count recorded with the action |
<sup>1</sup> The value is the length of the consolidated run for [MESSAGE_BULK_DELETE](#audit-actions) and is always `1` for the voice actions
<sup>2</sup> Read from the legacy `delete_message_days` body field of [Create or replace guild ban](/http-api/guild-moderation/#create-or-replace-guild-ban), so a ban that supplied only `delete_message_seconds` records `"0"`
<sup>2</sup> Deprecated, and present only on a ban entry recorded before `delete_message_seconds`. Its value is the legacy `delete_message_days` body field of [Create or replace guild ban](/http-api/guild-moderation/#create-or-replace-guild-ban), so a ban that sent only `delete_message_seconds` has `"0"`
<sup>3</sup> The field is defined by the wire contract and no current guild operation records it
<sup>3</sup> Present when the resolved deletion window of [Create or replace guild ban](/http-api/guild-moderation/#create-or-replace-guild-ban) is positive, whichever body field set it
<sup>4</sup> The value is a [channel type](/http-api/channels/#channel-types) for the channel actions and a [permission overwrite type](/http-api/channels/#permission-overwrite-types) for the overwrite actions
<sup>4</sup> The field is defined by the wire contract and no current guild operation records it
<sup>5</sup> Recorded by [MEMBER_ROLE_UPDATE](#audit-actions), [ROLE_UPDATE](#audit-actions), and an overwrite action whose target is a role other than `@everyone`
<sup>6</sup> The value is a [channel type](/http-api/channels/#channel-types) for the channel actions and a [permission overwrite type](/http-api/channels/#permission-overwrite-types) for the overwrite actions
## Audit log change object
@@ -236,17 +265,21 @@ Recorded by [GUILD_UPDATE](#audit-actions).
| default_message_notifications | number | The [default message notification level](/http-api/guilds/#default-message-notification-levels) recorded with the change |
| system_channel_id | ?string | The decimal ID of the configured system channel, or null when the guild sets none |
| system_channel_flags | number | The [system channel flags](/http-api/guilds/#system-channel-flags) mask recorded with the change |
| rules_channel_id | ?string | The decimal ID of the configured rules channel, or null when the guild sets none |
| rules_channel_id<sup>3</sup> | ?string | The decimal ID of the configured rules channel, or null when the guild sets none |
| afk_channel_id | ?string | The decimal ID of the configured AFK voice channel, or null when the guild sets none |
| afk_timeout | number | The AFK timeout in seconds |
| disabled_operations | number | The [disabled guild operations](/http-api/guilds/#disabled-guild-operations) mask recorded with the change |
| member_count | number | The member count recorded with the mutation |
| disabled_operations<sup>3</sup> | number | The [disabled guild operations](/http-api/guilds/#disabled-guild-operations) mask recorded with the change |
| member_count<sup>3</sup> | number | The member count recorded with the mutation |
| message_history_cutoff | ?ISO8601 timestamp | The configured message history cutoff, or null when the guild sets none |
<sup>1</sup> The field holds the same value on both sides of every guild action, so it never produces a change object
<sup>2</sup> A change object appears only when a feature was added or removed
<sup>3</sup> No current guild operation records the field
[Modify guild](/http-api/guilds/#modify-guild) records every field in this table except `guild_id`, `owner_id`, `vanity_url_code`, `rules_channel_id`, `disabled_operations`, and `member_count`. [Transfer guild ownership](/http-api/guild-members/#transfer-guild-ownership) records only `owner_id`, and [Modify guild custom invite URL](/http-api/guilds/#modify-guild-custom-invite-url) records only `vanity_url_code`.
#### Channel change fields
Recorded by [CHANNEL_CREATE](#audit-actions), [CHANNEL_UPDATE](#audit-actions), and [CHANNEL_DELETE](#audit-actions).
@@ -257,6 +290,7 @@ Recorded by [CHANNEL_CREATE](#audit-actions), [CHANNEL_UPDATE](#audit-actions),
| type | number | The [channel type](/http-api/channels/#channel-types) recorded with the change |
| name | ?string | The name the channel was given by this change |
| topic | ?string | The topic the channel was given by this change, or null when it has none |
| url<sup>1</sup> | ?string | The destination URL of the channel, or null when it has none |
| parent_id | ?string | The decimal ID of the parent category, or null when the channel sits at the top level |
| position | number | The position of the channel among its siblings |
| nsfw | ?boolean | Whether the channel is marked age restricted, or null when it has no override |
@@ -267,9 +301,13 @@ Recorded by [CHANNEL_CREATE](#audit-actions), [CHANNEL_UPDATE](#audit-actions),
| voice_connection_limit | ?number | The configured per-user voice connection limit |
| bitrate | ?number | The voice bitrate in bits per second |
| rtc_region | ?string | The pinned voice region, or null when the channel is routed automatically |
| permission_overwrite_count<sup>1</sup> | number | The number of permission overwrites the channel has |
| permission_overwrite_count<sup>2</sup> | number | The number of permission overwrites the channel has |
<sup>1</sup> Only the count is recorded. An individual overwrite mutation records its own [permission overwrite](#permission-overwrite-change-fields) entry
<sup>1</sup> Recorded only for a `GUILD_LINK` [channel type](/http-api/channels/#channel-types)
<sup>2</sup> No current channel operation records the field. An individual overwrite mutation records its own [permission overwrite](#permission-overwrite-change-fields) entry
Changing `permission_overwrites` through [Modify channel](/http-api/channels/#modify-channel) records overwrite entries and adds no change to the [CHANNEL_UPDATE](#audit-actions) entry.
#### Permission overwrite change fields
@@ -321,7 +359,9 @@ Recorded by [MEMBER_BAN_ADD](#audit-actions) and [MEMBER_BAN_REMOVE](#audit-acti
| moderator_id | string | The decimal ID of the user that issued the ban |
| banned_at | ISO8601 timestamp | The time the ban was issued |
| expires_at | ?ISO8601 timestamp | The expiry of a temporary ban, or null for a permanent ban |
| reason | ?string | The stored [ban](/http-api/guild-moderation/#guild-ban-object) reason, which is not the [audit log reason](#audit-log-reason) |
| reason<sup>1</sup> | ?string | The stored [ban](/http-api/guild-moderation/#guild-ban-object) reason |
<sup>1</sup> The body `reason` when the request sent one, and the `X-Audit-Log-Reason` value otherwise. The entry `reason` is the header when one was sent, so the two differ when a request sends both with different values
#### Guild role change fields
@@ -343,7 +383,11 @@ Recorded by [ROLE_CREATE](#audit-actions), [ROLE_UPDATE](#audit-actions), and [R
<sup>1</sup> Recorded only by [Modify guild role](/http-api/permissions/#modify-guild-role), and only when the mask changed. The value is an object with `added` and `removed` string arrays of permission names
`permissions_diff` arrives as `new_value` with no `old_value`, and the same change list still has the `permissions` change. The role position and hoist position operations record [ROLE_UPDATE](#audit-actions) without it.
`permissions_diff` arrives as `new_value` with no `old_value`, and the same change list still has the `permissions` change.
[Modify guild role positions](/http-api/permissions/#modify-guild-role-positions) records one [ROLE_UPDATE](#audit-actions) entry for each role that moved, with only the `position` change. Fluxer orders the roles other than `@everyone` by `position` descending, then by ID ascending, before and after the write. The largest set of roles that keep their relative order counts as unmoved, and where several sets qualify, the set whose roles come earliest in the new order is used. A request that only renumbers positions records no entry.
[Modify role hoist positions](/http-api/permissions/#modify-role-hoist-positions) and [Reset role hoist positions](/http-api/permissions/#reset-role-hoist-positions) apply the same rule to the member list order, with only the `hoist_position` change in each entry. That order is the hoisted roles other than `@everyone`, by `hoist_position` descending with `position` used when a role has no hoist position, then by ID ascending. A role that is not hoisted has an entry whenever its `hoist_position` changed.
#### Invite change fields
@@ -451,7 +495,7 @@ The read is ordered from newest to oldest by entry ID. `before` selects entries
Consolidated entries have new IDs but retain the position of the run they replace. Sort the page by ID if a strictly descending array is required.
A page holds fewer entries than `limit` only when the guild has no further matching entries in that direction.
A stored update entry that fails the [no-change rule](#audit-actions) is skipped and does not count toward `limit`. A page holds fewer entries than `limit` only when the guild has no further matching entries in that direction.
### Response
@@ -320,7 +320,7 @@ An emoji that does not belong to the guild in the path returns 404 `UNKNOWN_EMOJ
### Side effects
The operation sets the requested name, emits [Guild Emojis Update](/gateway/events/#guild-emojis-update) with the guild's complete emoji collection, and then writes an [`EMOJI_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry. Both are produced even when the submitted name equals the current name, and the audit entry then records an empty change list.
The operation sets the requested name, emits [Guild Emojis Update](/gateway/events/#guild-emojis-update) with the guild's complete emoji collection, and then writes an [`EMOJI_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry. A submitted name that equals the current name still emits the Dispatch and records no audit entry.
### Rate limit
@@ -121,7 +121,7 @@ The request body shared by [Modify current guild member](#modify-current-guild-m
<sup>6</sup> An ISO 8601 date and time. A blank string is normalised to null
<sup>7</sup> Trimmed, and never published in the audit log response
<sup>7</sup> Trimmed, and used as the [audit log reason](/http-api/guild-audit-logs/#audit-log-reason) of the member update entry when the request sends no `X-Audit-Log-Reason` header
<sup>8</sup> `channel_id` moves or disconnects, and `connection_id` selects one of the member's voice connections
@@ -277,9 +277,9 @@ The body is a [guild member update object](#guild-member-update-object) without
The operation updates the membership, records a [`MEMBER_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry and emits [Guild Member Update](/gateway/events/#guild-member-update).
The target's own sessions always receive the Dispatch. Other guild sessions receive it subject to [event filtering](/gateway/event-filtering/). The audit entry and Dispatch are produced even when the resulting membership is unchanged, and an entry whose change list is empty is published without a `changes` member.
The target's own sessions always receive the Dispatch. Other guild sessions receive it subject to [event filtering](/gateway/event-filtering/). The Dispatch is produced even when the resulting membership is unchanged, and an update that changes nothing records no audit entry.
Supplying `channel_id` moves or disconnects every voice connection the member holds, or only the one named by `connection_id`. It records one [`MEMBER_MOVE`](/http-api/guild-audit-logs/#audit-actions) or [`MEMBER_DISCONNECT`](/http-api/guild-audit-logs/#audit-actions) audit entry for the whole request, however many connections it touched. It emits [Voice State Update](/gateway/events/#voice-state-update) to sessions that can view the affected channel. Supplying `mute` or `deaf` updates the membership, records no separate audit entry, and emits the same voice Dispatch once for each live connection.
Supplying `channel_id` moves or disconnects every voice connection the member holds, or only the one named by `connection_id`. It records one [`MEMBER_MOVE`](/http-api/guild-audit-logs/#audit-actions) or [`MEMBER_DISCONNECT`](/http-api/guild-audit-logs/#audit-actions) audit entry for the whole request, however many connections it touched. A move into the channel the member already occupies records no entry. It emits [Voice State Update](/gateway/events/#voice-state-update) to sessions that can view the affected channel. Supplying `mute` or `deaf` updates the membership, records no separate audit entry, and emits the same voice Dispatch once for each live connection.
Fluxer emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) for every audit entry. A request that fails after uploading a new avatar or banner leaves the previous asset in place.
@@ -449,7 +449,7 @@ The operation requires the [sudo verification fields](/http-api/guilds/#sudo-ver
The operation changes only the guild's `owner_id`. The outgoing owner keeps every role, permission overwrite, and membership they held.
It records a [`GUILD_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry naming the previous and new owner in its change list, emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create), and emits [Guild Update](/gateway/events/#guild-update) to every session that can see the guild. The audit log response has no `options` member for this entry.
It records a [`GUILD_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry naming the previous and new owner in its change list, emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create), and emits [Guild Update](/gateway/events/#guild-update) to every session that can see the guild. A request naming the current owner changes nothing and records no audit entry. The audit log response has no `options` member for this entry.
An account with an enrolled second factor also receives a reissued sudo proof. A bot credential and an account whose only proof was a password receive `X-Fluxer-Sudo-Mode-JWT` only when the request itself has one.
@@ -495,9 +495,9 @@ The guild owner receives 404 `UNKNOWN_ROLE` for a role that does not exist in th
### Side effects
Adding a new role makes a temporary membership permanent. The operation records a [`MEMBER_ROLE_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry and emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) and [Guild Member Update](/gateway/events/#guild-member-update), even if the member already holds the role. An unchanged role set produces an audit entry without `changes`.
Adding a new role makes a temporary membership permanent. The operation records a [`MEMBER_ROLE_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry and emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) and [Guild Member Update](/gateway/events/#guild-member-update). An unchanged role set still emits [Guild Member Update](/gateway/events/#guild-member-update) and records no audit entry.
The audit log response names the role only through the change list.
The audit log response names the role with the [`role_name`](/http-api/guild-audit-logs/#audit-log-options-object) option and through the change list.
### Rate limit
@@ -539,7 +539,7 @@ Removes one role from a member and returns 204 with an empty body. Requires memb
### Side effects
The operation records a [`MEMBER_ROLE_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry and emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) and [Guild Member Update](/gateway/events/#guild-member-update), even if the member did not hold the role. Removing a role does not change whether the membership is temporary.
The operation records a [`MEMBER_ROLE_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry and emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) and [Guild Member Update](/gateway/events/#guild-member-update). A role the member did not hold still emits [Guild Member Update](/gateway/events/#guild-member-update) and records no audit entry. Removing a role does not change whether the membership is temporary.
### Rate limit
@@ -125,9 +125,9 @@ The body is optional, and so is every field in it. Fluxer reads an omitted or wh
<sup>4</sup> Zero and an omitted field both create a permanent ban. Any value outside 60 through 63072000, which is two years, fails with `INVALID_FORMAT`
`delete_message_days` defaults to 0, so a [`MEMBER_BAN_ADD`](/http-api/guild-audit-logs/#audit-actions) entry always records `options.delete_member_days`. That option is `0` for a ban that supplied only `delete_message_seconds`.
A [`MEMBER_BAN_ADD`](/http-api/guild-audit-logs/#audit-actions) entry records `options.delete_message_seconds` when the effective interval is positive, whichever field set it. A ban that deletes nothing records no option, and the deprecated `delete_member_days` option is present only on an entry written before this field.
An omitted or null `reason` falls back to the `X-Audit-Log-Reason` value, while a supplied value that normalises to the empty string is stored as null without falling back. The header remains the audit entry reason in every case.
An omitted or null `reason` falls back to the `X-Audit-Log-Reason` value, while a supplied value that normalises to the empty string is stored as null without falling back. The audit entry reason is the header when the request sends one, and the stored ban reason otherwise.
:::caution[Banning an already banned account succeeds]
The second ban replaces the previous ban's details. Re-issuing can turn a permanent ban into a temporary one or extend a temporary ban. There is no conditional form.
@@ -354,7 +354,7 @@ A non-member who names an existing sticker receives `MISSING_PERMISSIONS`.
### Side effects
The operation writes the supplied metadata, emits [Guild Stickers Update](/gateway/events/#guild-stickers-update) with the guild's complete sticker collection, and then records a [`STICKER_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry. Both are produced even when the submitted values match the stored ones. The audit entry then has no `changes`.
The operation writes the supplied metadata, emits [Guild Stickers Update](/gateway/events/#guild-stickers-update) with the guild's complete sticker collection, and then records a [`STICKER_UPDATE`](/http-api/guild-audit-logs/#audit-actions) audit entry. Submitted values that match the stored ones still emit the Dispatch and record no audit entry.
### Rate limit
@@ -1291,7 +1291,7 @@ The operation deletes the caller's read-through marker, mention count, and pin a
## Delete message
<RouteHeader method="DELETE" path="/v1/channels/{channel_id}/messages/{message_id}" bot />
<RouteHeader method="DELETE" path="/v1/channels/{channel_id}/messages/{message_id}" bot auditReason />
Deletes one message. Returns 204 with an empty body on success. Emits a [Message Delete](/gateway/events/#message-delete) Gateway event.
@@ -1301,7 +1301,6 @@ Deletes one message. Returns 204 with an empty body on success. Emits a [Message
- The author can delete their own message anywhere.
- Deleting another user's guild message requires both [SEND_MESSAGES](/http-api/permissions/) and [MANAGE_MESSAGES](/http-api/permissions/), which is an [elevated permission](/http-api/permissions/#elevated-permissions). No caller can delete another user's private channel message.
- A caller who holds `MANAGE_MESSAGES` with no enrolled authenticator receives 400 [`TWO_FACTOR_REQUIRED`](/http-api/errors/) in a guild whose [MFA level](/http-api/guilds/#mfa-levels) is elevated, unless they own the guild.
- The operation reads no audit reason header.
### Path parameters
@@ -1330,7 +1329,7 @@ The operation permanently deletes the message, its reactions, and its attachment
It emits [Message Delete](/gateway/events/#message-delete) to every session that can see the channel. When the message was pinned, it also removes the channel pin entry and emits [Channel Pins Update](/gateway/events/#channel-pins-update) with the channel's unchanged last-pin timestamp.
Every deletion in a guild channel writes one `MESSAGE_DELETE` guild audit log entry with no reason, including deletion of the caller's own message.
Every deletion in a guild channel writes one `MESSAGE_DELETE` guild audit log entry with the supplied reason, including deletion of the caller's own message.
### Rate limit
@@ -1378,7 +1377,7 @@ The operation permanently deletes the stored attachment object, purges it from m
## Bulk delete messages
<RouteHeader method="POST" path="/v1/channels/{channel_id}/messages/bulk-delete" bot mfa />
<RouteHeader method="POST" path="/v1/channels/{channel_id}/messages/bulk-delete" bot auditReason mfa />
Deletes from 1 through 100 messages in one guild channel. Returns 204 with an empty body on success. Emits one [Message Delete Bulk](/gateway/events/#message-delete-bulk) Gateway event.
@@ -1388,7 +1387,6 @@ Deletes from 1 through 100 messages in one guild channel. Returns 204 with an em
- The caller requires [MANAGE_MESSAGES](/http-api/permissions/), which is an [elevated permission](/http-api/permissions/#elevated-permissions).
- A caller who holds `MANAGE_MESSAGES` with no enrolled authenticator receives 400 [`TWO_FACTOR_REQUIRED`](/http-api/errors/) in a guild whose [MFA level](/http-api/guilds/#mfa-levels) is elevated, unless they own the guild.
- The operation applies no age boundary, so a message of any age can be selected.
- The operation reads no audit reason header.
### Path parameters
@@ -1428,7 +1426,7 @@ The operation permanently removes every selected message, attachment, and reacti
When at least one ID matched, it emits one [Message Delete Bulk](/gateway/events/#message-delete-bulk) with every requested ID to sessions that can see the channel, including an ID that matched nothing. It emits no [Channel Pins Update](/gateway/events/#channel-pins-update) and no individual [Message Delete](/gateway/events/#message-delete).
It writes one `MESSAGE_BULK_DELETE` guild audit log entry with the matched count and no reason.
It writes one `MESSAGE_BULK_DELETE` guild audit log entry with the matched count and the supplied reason.
### Rate limit
@@ -1593,7 +1591,7 @@ Acknowledging pins is a separate operation, [Acknowledge pins](#acknowledge-pins
## Pin message
<RouteHeader method="PUT" path="/v1/channels/{channel_id}/pins/{message_id}" bot />
<RouteHeader method="PUT" path="/v1/channels/{channel_id}/pins/{message_id}" bot auditReason />
Pins a `DEFAULT` or `REPLY` message. Returns 204 with an empty body on success. Emits [Message Update](/gateway/events/#message-update), [Channel Pins Update](/gateway/events/#channel-pins-update), and [Message Create](/gateway/events/#message-create) Gateway events.
@@ -1630,7 +1628,7 @@ Pinning a message that is not already pinned sets its pin timestamp, advances th
It emits [Message Update](/gateway/events/#message-update) for the target, [Channel Pins Update](/gateway/events/#channel-pins-update) for the channel, and [Message Create](/gateway/events/#message-create) for the system message, each to every session that can see the channel.
A guild pin also writes one `MESSAGE_PIN` guild audit log entry with no reason. Pinning an already pinned message changes nothing and emits no Dispatch.
A guild pin also writes one `MESSAGE_PIN` guild audit log entry with the supplied reason. Pinning an already pinned message changes nothing and emits no Dispatch.
### Rate limit
@@ -1638,7 +1636,7 @@ A guild pin also writes one `MESSAGE_PIN` guild audit log entry with no reason.
## Unpin message
<RouteHeader method="DELETE" path="/v1/channels/{channel_id}/pins/{message_id}" bot />
<RouteHeader method="DELETE" path="/v1/channels/{channel_id}/pins/{message_id}" bot auditReason />
Unpins a message. Returns 204 with an empty body on success. Emits [Message Update](/gateway/events/#message-update) and [Channel Pins Update](/gateway/events/#channel-pins-update) Gateway events.
@@ -1668,7 +1666,7 @@ Unpins a message. Returns 204 with an empty body on success. Emits [Message Upda
### Side effects
Unpinning a pinned message clears its pin timestamp and emits [Message Update](/gateway/events/#message-update) and [Channel Pins Update](/gateway/events/#channel-pins-update) to every session that can see the channel. A guild unpin also writes one `MESSAGE_UNPIN` guild audit log entry with no reason.
Unpinning a pinned message clears its pin timestamp and emits [Message Update](/gateway/events/#message-update) and [Channel Pins Update](/gateway/events/#channel-pins-update) to every session that can see the channel. A guild unpin also writes one `MESSAGE_UNPIN` guild audit log entry with the supplied reason.
It creates no system message. The `CHANNEL_PINNED_MESSAGE` system message the original pin created stays in the channel, and the channel's last-pin timestamp is unchanged.
@@ -376,7 +376,7 @@ Every field is optional, and an omitted field leaves the stored value unchanged.
### Side effects
The operation creates a [ROLE_UPDATE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry with the supplied reason and emits [Guild Role Update](/gateway/events/#guild-role-update) to every session that can see the guild. A permission change adds a [permissions_diff](/http-api/guild-audit-logs/#guild-role-change-fields) change entry recording the exact added and removed permission names. A request whose values all match the current role still emits the Dispatch and still records the entry, with no change rows in it.
The operation creates a [ROLE_UPDATE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry with the supplied reason and emits [Guild Role Update](/gateway/events/#guild-role-update) to every session that can see the guild. A permission change adds a [permissions_diff](/http-api/guild-audit-logs/#guild-role-change-fields) change entry recording the exact added and removed permission names. A request whose values all match the current role still emits the Dispatch and records no entry.
### Rate limit
@@ -423,7 +423,7 @@ The top-level body is an array of [role position objects](#role-position-object)
### Side effects
The operation creates one [ROLE_UPDATE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry for each role whose position changed. It emits one [Guild Role Update Bulk](/gateway/events/#guild-role-update-bulk) with those roles to every session that can see the guild. A request that produces no positional change creates no audit entry and emits no Dispatch. Dense renumbering can change the numeric `position` of an unnamed role without changing its rank, and such a role is included in the Dispatch.
The operation creates one [ROLE_UPDATE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry for each role that moved, under the [ordering rule](/http-api/guild-audit-logs/#guild-role-change-fields) the audit log defines. It emits one [Guild Role Update Bulk](/gateway/events/#guild-role-update-bulk) with every role whose stored position changed to every session that can see the guild. A request that produces no positional change creates no audit entry and emits no Dispatch. Dense renumbering can change the numeric `position` of an unnamed role without changing its rank, and such a role is included in the Dispatch and records no audit entry.
### Rate limit
@@ -464,7 +464,7 @@ The top-level body is an array of [role hoist position objects](#role-hoist-posi
### Side effects
The operation creates one [ROLE_UPDATE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry for each named role whose hoist position changed. It emits one [Guild Role Update Bulk](/gateway/events/#guild-role-update-bulk) with the changed roles to every session that can see the guild. Setting the current positions again changes nothing, creates no audit entry, and emits no Dispatch.
The operation creates one [ROLE_UPDATE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry for each named role that moved in the member list order, under the [ordering rule](/http-api/guild-audit-logs/#guild-role-change-fields) the audit log defines, and for each changed role that is not hoisted. It emits one [Guild Role Update Bulk](/gateway/events/#guild-role-update-bulk) with every role whose hoist position changed to every session that can see the guild. Setting the current positions again changes nothing, creates no audit entry, and emits no Dispatch.
### Rate limit
@@ -502,7 +502,7 @@ The request has no body.
### Side effects
The operation creates one [ROLE_UPDATE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry for each role whose hoist position is cleared. It emits one [Guild Role Update Bulk](/gateway/events/#guild-role-update-bulk) with those roles to every session that can see the guild. When no role held a hoist position, no audit entry is created and no Dispatch is emitted.
The operation creates one [ROLE_UPDATE](/http-api/guild-audit-logs/#audit-actions) guild audit log entry for each cleared role that moved in the member list order, under the [ordering rule](/http-api/guild-audit-logs/#guild-role-change-fields) the audit log defines, and for each cleared role that is not hoisted. It emits one [Guild Role Update Bulk](/gateway/events/#guild-role-update-bulk) with every cleared role to every session that can see the guild. When no role held a hoist position, no audit entry is created and no Dispatch is emitted.
### Rate limit
@@ -852,7 +852,7 @@ A deployment that configures no Visionary guild or no Visionary role returns 400
A new membership sends [Guild Create](/gateway/events/#guild-create) with complete guild state to the joining account and [Guild Member Add](/gateway/events/#guild-member-add) to the guild. It also creates a join system message and sends [Message Create](/gateway/events/#message-create) unless the guild has no system channel or suppresses join notifications.
A new or existing member then receives the Visionary role, which records a `MEMBER_ROLE_UPDATE` guild audit log entry attributed to the system user and emits [Guild Member Update](/gateway/events/#guild-member-update). An account already holding the role causes no role change. Billing state is unchanged.
A new or existing member then receives the Visionary role, which records a `MEMBER_ROLE_UPDATE` guild audit log entry attributed to the system user and emits [Guild Member Update](/gateway/events/#guild-member-update). An account already holding the role causes no role change and records no audit entry. Billing state is unchanged.
### Rate limit