Files
fluxer/fluxer_docs/src/content/docs/admin-api/guilds.mdx
T

908 lines
45 KiB
Plaintext

---
# SPDX-License-Identifier: AGPL-3.0-or-later
title: Admin guilds
description: Guild search, detail, mutation, deletion, membership, expressions, and Gateway lifecycle.
---
import RouteHeader from '@/components/RouteHeader.astro';
Admin guild operations read and change the guilds the public [Guilds](/http-api/guilds/) resource exposes. Most of them need no membership in the guild and no guild permission, and [Remove guild member](#remove-guild-member) and [Ban guild member](#ban-guild-member) are the two exceptions.
[Archives](/admin-api/archives/) owns the archive lifecycle and the downloads.
Reads use the `admin:lookup` bucket, which permits 200 requests per minute for each authenticated user. Writes use the `admin:guild:modify` bucket, which permits 100 requests per minute for each authenticated user. [Create guild archive](#create-guild-archive) is the one write served from the read bucket.
:::danger[Guild deletion is immediate]
[Delete guild](#delete-guild) destroys the guild at once. There is no scheduled deletion, no recovery window, and no restore operation.
:::
## Admin guild object
The compact guild representation returned by [List guilds](#list-guilds) and by [List user guilds](/admin-api/users/#list-user-guilds). It has no channel or role state.
### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The ID of the guild |
| name | string | The name of the guild |
| features | array[string] | The complete [guild feature](/http-api/guilds/#guild-features) set (at most 100) |
| owner_id | snowflake | The ID of the owner |
| owner_username<sup>1</sup> | ?string | The username of the owner |
| owner_global_name<sup>1</sup> | ?string | The display name of the owner |
| owner_discriminator<sup>1</sup> | ?string | The owner discriminator, zero-padded to four digits |
| icon | ?string | The icon hash, or null when unset |
| banner | ?string | The banner hash, or null when unset |
| member_count<sup>2</sup> | integer | The recorded member count |
| nsfw_level? | integer | The [NSFW level](/http-api/guilds/#nsfw-levels) |
| nsfw?<sup>3</sup> | boolean | Whether the guild is flagged as adult content |
| content_warning_level?<sup>3</sup> | integer | The [guild content warning level](/http-api/guilds/#guild-content-warning-levels) |
| content_warning_text?<sup>3</sup> | ?string | The custom content warning text |
| approximate_member_count?<sup>4</sup> | integer | The member count the main Gateway reports |
| approximate_presence_count?<sup>4</sup> | integer | The connected member count the main Gateway reports |
<sup>1</sup> All three are null whenever the owner account is not resolved. [List guilds](#list-guilds) never resolves it, so all three are always null there
<sup>2</sup> A member connecting or disconnecting does not change it
<sup>3</sup> No operation populates these three, so they are absent from every current response
<sup>4</sup> Present only on [List user guilds](/admin-api/users/#list-user-guilds) when that operation is asked for counts
### Example
```json
{
"id": "1471002884199612416",
"name": "Tidepool",
"features": ["DISCOVERABLE", "VANITY_URL"],
"owner_id": "1483920011884392448",
"owner_username": null,
"owner_global_name": null,
"owner_discriminator": null,
"icon": "b7f1c2d3e4a5968778695a4b3c2d1e0f",
"banner": null,
"member_count": 4182,
"nsfw_level": 0
}
```
## Admin guild detail object
The full guild representation returned by [Get guild](#get-guild). It embeds every channel and every role of the guild.
### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The ID of the guild |
| owner_id | snowflake | The ID of the owner |
| owner_username<sup>1</sup> | ?string | The username of the owner |
| owner_global_name<sup>1</sup> | ?string | The display name of the owner |
| owner_discriminator<sup>1</sup> | ?string | The owner discriminator, zero-padded to four digits |
| name | string | The name of the guild (1-100 characters) |
| vanity_url_code | ?string | The custom invite code, or null when the guild owns none |
| icon | ?string | The icon hash, or null when unset |
| banner | ?string | The banner hash, or null when unset |
| splash | ?string | The invite splash hash, or null when unset |
| embed_splash | ?string | The embedded invite splash hash, or null when unset |
| features | array[string] | The complete [guild feature](/http-api/guilds/#guild-features) set (at most 100) |
| verification_level | integer | The [verification level](/http-api/guilds/#verification-levels) |
| mfa_level | integer | The [MFA level](/http-api/guilds/#mfa-levels) required of moderators |
| nsfw_level | integer | The [NSFW level](/http-api/guilds/#nsfw-levels) |
| nsfw?<sup>2</sup> | boolean | Whether the guild is flagged as adult content |
| content_warning_level?<sup>2</sup> | integer | The [guild content warning level](/http-api/guilds/#guild-content-warning-levels) |
| content_warning_text?<sup>2</sup> | ?string | The custom content warning text |
| explicit_content_filter | integer | The [explicit content filter level](/http-api/guilds/#guild-explicit-content-filter-levels) |
| default_message_notifications | integer | The [default message notification level](/http-api/guilds/#default-message-notification-levels) |
| afk_channel_id | ?snowflake | The voice channel idle members are moved to, or null when unset |
| afk_timeout | integer | The idle time before a member is moved, in seconds |
| system_channel_id | ?snowflake | The channel system messages are sent to, or null when they are disabled |
| system_channel_flags | integer | The [system channel flags](/http-api/guilds/#system-channel-flags) |
| rules_channel_id | ?snowflake | The channel holding the guild rules, or null when unset |
| disabled_operations | integer | The [disabled guild operations](/http-api/guilds/#disabled-guild-operations) bitfield |
| member_count | integer | The recorded member count |
| channels | array[[Admin guild channel](#admin-guild-channel-object) object] | Every channel in the guild |
| roles | array[[Admin guild role](#admin-guild-role-object) object] | Every role in the guild |
<sup>1</sup> All three are null when the owner account cannot be resolved
<sup>2</sup> The operation does not populate these three, so they are absent from every current response
### Admin guild channel object
#### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The ID of the channel |
| name | ?string | The channel name (1-100 characters) |
| type | integer | The [channel type](/http-api/channels/#channel-types) |
| position | integer | The sort position within the guild |
| parent_id | ?snowflake | The parent category ID, or null when the channel is top-level |
| nsfw | ?boolean | Whether the channel is marked age restricted |
| nsfw_override?<sup>3</sup> | ?boolean | The age restriction set on the channel itself |
| content_warning_level?<sup>3</sup> | integer | The [content warning level](/http-api/guilds/#guild-content-warning-levels) set on the channel |
| content_warning_text?<sup>3</sup> | ?string | The content warning text set on the channel |
| url<sup>4</sup> | ?string | The external channel URL (1-2048 characters) |
<sup>3</sup> The operation does not populate these three, so they are absent from every current response
<sup>4</sup> Null for every channel type that is not an external link channel
### Admin guild role object
#### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The ID of the role |
| name | string | The role name (1-100 characters) |
| color | integer | The packed RGB colour |
| position | integer | The sort position within the guild |
| permissions | string | The [permission](/http-api/permissions/) bitfield as an unsigned 64-bit decimal string |
| hoist | boolean | Whether the role is displayed separately in the member list |
| mentionable | boolean | Whether the role can be mentioned by anyone |
## Admin guild update object
The guild state [Update guild](#update-guild) reads back after applying the request. It has no channel, role, or owner identity state.
### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The ID of the guild |
| name | string | The name of the guild (1-100 characters) |
| features | array[string] | The complete [guild feature](/http-api/guilds/#guild-features) set after the update (at most 100) |
| owner_id | snowflake | The ID of the owner after the update |
| icon | ?string | The icon hash, or null when unset or cleared |
| banner | ?string | The banner hash, or null when unset or cleared |
| member_count | integer | The recorded member count |
| nsfw_level | integer | The [NSFW level](/http-api/guilds/#nsfw-levels) |
## Admin guild expression object
One custom emoji or one sticker of a guild, together with a resolvable media URL. The two listings return the same shape.
### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The ID of the emoji or sticker |
| name | string | The expression name (1-100 characters) |
| animated | boolean | Whether the stored image is animated |
| creator_id | snowflake | The account that uploaded the expression |
| media_url<sup>1</sup> | string | The [Media Proxy](/media-proxy/routes/) URL the expression is served from (1-2048 characters) |
<sup>1</sup> Always the WebP representation, at size 160 for an emoji and size 320 for a sticker, and it has the `animated=true` selector only when `animated` is true
### Example
```json
{
"id": "1496613881730531328",
"name": "party_parrot",
"animated": true,
"creator_id": "1483920011884392448",
"media_url": "https://media.example.com/emojis/1496613881730531328.webp?size=160&animated=true"
}
```
## Admin guild asset purge result object
One entry of the `processed` array returned by [Purge guild assets](#purge-guild-assets).
### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The asset that was purged |
| asset_type | string | The [asset type](#asset-types) the ID resolved to |
| found_in_db<sup>1</sup> | boolean | Whether the ID matched an emoji or sticker record |
| guild_id<sup>2</sup> | ?snowflake | The guild the purged record belonged to |
| guild_nsfw_level<sup>2</sup> | ?integer | The [NSFW level](/http-api/guilds/#nsfw-levels) of that guild |
<sup>1</sup> False exactly when `asset_type` is `unknown`, in which case only the stored media was queued for removal
<sup>2</sup> Both null when `asset_type` is `unknown`, and `guild_nsfw_level` is also null when the owning guild can no longer be resolved
## Admin guild asset purge error object
One entry of the `errors` array returned by [Purge guild assets](#purge-guild-assets).
### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | string | The asset that could not be purged, as supplied |
| error<sup>1</sup> | string | The reason the asset could not be purged (1-4000 characters) |
<sup>1</sup> The operation produces `Invalid numeric ID`, `Asset belongs to another guild`, and `Failed to purge asset`. Any other value is the message of the underlying failure
## Asset types
| Value | Description |
| --- | --- |
| emoji | The ID resolved to a custom emoji owned by the requested guild |
| sticker | The ID resolved to a sticker owned by the requested guild |
| unknown | The ID matched no emoji and no sticker record, and only associated media was queued for removal |
An ID owned by a different guild appears in `errors` with no asset type.
## List guilds
<RouteHeader method="GET" path="/v1/admin/guilds" />
Searches guilds through the guild search index and returns [Admin guild](#admin-guild-object) objects. Requires `guild:lookup`.
The index matches `q` against the guild name, the discovery tags, the custom invite code, and the discovery description, in that order of weight. Hits are ordered by guild creation time ascending, so the oldest matching guild is first. An empty or omitted `q` matches every indexed guild.
### Query parameters
| Field | Type | Description |
| --- | --- | --- |
| q?<sup>1</sup> | string | Free-text query (1-1024 characters) |
| limit? | integer | Maximum guilds to return (1-200, default 50) |
| offset? | integer | Guilds to skip before returning results (0-10000, default 0) |
<sup>1</sup> A `q` of all decimal digits also resolves that value as an exact guild ID, but only while `offset` is 0. A guild the index did not return is prepended to `guilds` and adds one to `total`
### Response body
| Field | Type | Description |
| --- | --- | --- |
| guilds | array[[Admin guild](#admin-guild-object) object] | Guilds in this page |
| total<sup>2</sup> | integer | The number of guilds the index reported for the query |
<sup>2</sup> The value can exceed the number of entries in `guilds`. Pagination advances with `offset`, and the operation returns no cursor
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Guild page was returned |
| 403 | [error response](/admin-api/#error-response) | `FEATURE_TEMPORARILY_DISABLED`, because the instance has no search backend configured |
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get guild
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}" />
Returns one guild with its channels and roles. Requires `guild:lookup`.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### Response body
| Field | Type | Description |
| --- | --- | --- |
| guild<sup>1</sup> | ?[Admin guild detail](#admin-guild-detail-object) object | The guild, or null when the ID resolves to nothing |
<sup>1</sup> A guild that does not exist answers 200 with a null guild
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | A lookup was performed, whether or not it resolved |
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Update guild
<RouteHeader method="PATCH" path="/v1/admin/guilds/{guild_id}" auditReason />
Applies one or more field groups to a guild and returns the [Admin guild update](#admin-guild-update-object) object read back afterwards. Omitted fields are left unchanged.
This is the only way to change the owner of a guild without acting as its current owner. The public [Transfer guild ownership](/http-api/guild-members/#transfer-guild-ownership) operation requires the caller to be the owner.
Fluxer evaluates authorisation in two stages and reads the body between them. The account first needs at least one of `guild:update:name`, `guild:update:settings`, `guild:update:features`, `guild:update:vanity`, and `guild:transfer_ownership`. The validated body then selects a set of ACLs and every one of them is required, so a body with `name` and `nsfw` needs both `guild:update:name` and `guild:update:settings`. The wildcard satisfies both stages.
- `guild:update:name` is selected by `name`.
- `guild:update:settings` is selected by `fields`, `verification_level`, `mfa_level`, `nsfw_level`, `nsfw`, `content_warning_level`, `content_warning_text`, `explicit_content_filter`, `default_message_notifications`, and `disabled_operations`.
- `guild:update:features` is selected by `add_features` and `remove_features`.
- `guild:update:vanity` is selected by `vanity_url_code`.
- `guild:transfer_ownership` is selected by `new_owner_id`.
A body with no field at all selects nothing, so an empty patch applies no change.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### JSON body
| Field | Type | Description |
| --- | --- | --- |
| name? | string | The replacement name (1-100 characters) |
| vanity_url_code?<sup>1</sup> | ?string | The replacement custom invite code, or null to release the current one |
| new_owner_id?<sup>2</sup> | snowflake | The replacement owner |
| add_features?<sup>3</sup> | array[string] | [Guild features](/http-api/guilds/#guild-features) to add (at most 100) |
| remove_features?<sup>3</sup> | array[string] | [Guild features](/http-api/guilds/#guild-features) to remove (at most 100) |
| fields?<sup>4</sup> | array[string] | Image fields to clear, each `icon`, `banner`, `splash`, or `embed_splash` (at most 10) |
| verification_level? | integer | The replacement [verification level](/http-api/guilds/#verification-levels) |
| mfa_level? | integer | The replacement [MFA level](/http-api/guilds/#mfa-levels) |
| nsfw_level? | integer | The replacement [NSFW level](/http-api/guilds/#nsfw-levels) |
| nsfw?<sup>5</sup> | boolean | Accepted and ignored |
| content_warning_level?<sup>5</sup> | integer | Accepted and ignored |
| content_warning_text?<sup>5</sup> | ?string | Accepted and ignored (at most 200 characters) |
| explicit_content_filter? | integer | The replacement [explicit content filter level](/http-api/guilds/#guild-explicit-content-filter-levels) |
| default_message_notifications? | integer | The replacement [default message notification level](/http-api/guilds/#default-message-notification-levels) |
| disabled_operations?<sup>6</sup> | integer | The replacement [disabled guild operations](/http-api/guilds/#disabled-guild-operations) bitfield |
<sup>1</sup> Normalised to lowercase with whitespace folded to hyphens and consecutive hyphens collapsed. The normalised result is 2 to 32 characters, and a shorter or longer one is rejected with 400
<sup>2</sup> The operation does not confirm that the replacement account exists, and an unresolvable owner makes every `owner_` field null on later reads
<sup>3</sup> Additions are applied before removals, so a feature named in both is removed. Any string is accepted
<sup>4</sup> The array only clears an image, and no Admin operation uploads one
<sup>5</sup> Validated, selects `guild:update:settings`, and records an `update_settings` audit entry. The guild record is left unchanged
<sup>6</sup> Any integer from 0 to 2147483647, stored as supplied, including bits outside the documented registry
A code already claimed by any invite is rejected with 400 `INVALID_FORM_BODY` and the validation code `THIS_VANITY_URL_IS_ALREADY_TAKEN` against `vanity_url_code`.
The channel references, idle timeout, and message history cutoff of a guild are not Admin-writable. They change through the public [Modify guild](/http-api/guilds/#modify-guild) operation.
### Response body
| Field | Type | Description |
| --- | --- | --- |
| guild | [Admin guild update](#admin-guild-update-object) object | The guild state read back after the request was applied |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Every selected field group was applied |
| 400 | [error response](/admin-api/#error-response) | The custom invite code is malformed or already claimed |
| 403 | [error response](/admin-api/#error-response) | `MISSING_ACL`, because the account holds none of the five update ACLs, or lacks an ACL a supplied field selects |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_GUILD`, because the guild does not exist |
:::caution[Field groups apply one at a time]
The groups run in a fixed order: image clears, settings, features, name, custom invite code, ownership. Each is a separate write with its own [Guild Update](/gateway/events/#guild-update) Dispatch and its own Admin audit entry. A failure part way through leaves the earlier groups applied.
:::
### Side effects
Clearing an image field queues the previous stored object for deletion. Replacing the custom invite code deletes the invite record holding the previous code and creates one for the new code, while sending null deletes the previous record without creating another.
Supplying `add_features` or `remove_features` reconciles an existing discovery application. The application is approved when [DISCOVERABLE](/http-api/guilds/#guild-features) becomes present and it is not already approved, and it is marked removed when `DISCOVERABLE` becomes absent and it was approved. A guild that has never applied for [discovery](/admin-api/discovery/) gains no application.
Every applied group refreshes the guild's entry in the guild search index and fires one [Guild Update](/gateway/events/#guild-update) Dispatch to every session that can see the guild, including when the write changes no value.
Each applied group records one Admin audit entry with the target type `guild` and the guild ID as the target. The actions are `clear_fields` with the cleared field names, `update_settings` with each applied setting, and `update_features` with the added, removed, and resulting feature sets. The remaining actions are `update_name` with the old and new names, `update_vanity` with the old and new codes, and `transfer_ownership` with the old and new owner IDs.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:guild:modify` bucket.
## Delete guild
<RouteHeader method="DELETE" path="/v1/admin/guilds/{guild_id}" auditReason />
Permanently deletes a guild and every record it owns. Requires `guild:delete`.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### Response body
| Field | Type | Description |
| --- | --- | --- |
| success | boolean | Always true |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Guild was deleted |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_GUILD`, because the guild does not exist |
:::danger[A guild archive is the only way back]
The guild, its channels, roles, memberships, messages, and attachments are destroyed as soon as the request is accepted. Take a [guild archive](#create-guild-archive) first when the content has to be retained.
:::
### Side effects
Fluxer emits one [Guild Delete](/gateway/events/#guild-delete) Dispatch first, then detaches every member from the guild on the main Gateway. It deletes each member's [guild settings](/http-api/users/settings/) entry for the guild, and drops each human member's [guild folder](/http-api/users/settings/) references to the guild.
Every invite, every webhook, every message of every channel, and every channel attachment are deleted. Any discovery application is deleted. The guild record is then deleted, the guild is stopped on the main Gateway, and it is removed from the guild search index.
One Admin audit entry is recorded with the action `delete_guild`, the target type `guild`, and the guild ID in both the target and the metadata.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:guild:modify` bucket.
## List guild members
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/members" />
Returns one page of [guild member](/http-api/guild-members/#guild-member-object) objects read from the main Gateway, without requiring membership. Requires `guild:list:members`.
:::note[Members come from the main Gateway]
A guild the main Gateway cannot load answers 404 `UNKNOWN_GUILD`.
:::
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### Query parameters
| Field | Type | Description |
| --- | --- | --- |
| limit? | integer | Maximum members to return (1-200, default 50) |
| offset? | integer | Members to skip before returning results (0-2147483647, default 0) |
### Response body
| Field | Type | Description |
| --- | --- | --- |
| members | array[[guild member](/http-api/guild-members/#guild-member-object) object] | Members in this page |
| total | integer | The total member count the main Gateway reported |
| limit<sup>1</sup> | integer | The limit the operation applied |
| offset<sup>1</sup> | integer | The offset the operation applied |
<sup>1</sup> Both are echoed from the resolved query, so a request that omitted them reads back the defaults. A client advances the page by adding `limit` to `offset`
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Member page was returned |
| 500 | [error response](/admin-api/#error-response) | The API loses its connection to the main Gateway during the call |
A member query the main Gateway reports as failed returns 502 `BAD_GATEWAY`, an unanswered call returns 504 `GATEWAY_TIMEOUT`, and an overloaded cluster returns 503 `SERVICE_UNAVAILABLE`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Add guild member
<RouteHeader method="PUT" path="/v1/admin/guilds/{guild_id}/members/{user_id}" auditReason />
Adds a user to a guild without an invite. Requires `guild:force_add_member`. The operation takes no request body.
Only the Admin ACL is evaluated.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
| user_id | snowflake | The user to add |
### Response body
| Field | Type | Description |
| --- | --- | --- |
| success | boolean | Always true |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Member was added, or the user was already a member |
| 400 | [error response](/admin-api/#error-response) | `MAX_GUILDS` because the user is at their guild limit, or `MAX_GUILD_MEMBERS` because the guild is at its member limit |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_USER` because the user does not exist, or `UNKNOWN_GUILD` because the guild does not exist |
### Side effects
Fluxer creates the membership with the Admin force-add join source and restores a communication timeout still in force from a previous membership. The guild ban list is not consulted, so a banned user can be admitted. The suspicious activity phone gate does not run. The per-user guild limit and the guild member limit are still enforced.
[Guild Member Add](/gateway/events/#guild-member-add) fires to the guild, the user's sessions are joined to the guild on the main Gateway, and the member enters guild member search when the guild has an indexed member set. The ordinary join system message is created, and with it a [Message Create](/gateway/events/#message-create) Dispatch, unless the guild sets `SUPPRESS_JOIN_NOTIFICATIONS` or has no usable system channel. A bot target additionally records a `BOT_ADD` entry in the guild's own [audit log](/http-api/guild-audit-logs/#audit-actions).
A user who is already a member keeps their existing membership. No membership is created, no counter moves, and no Dispatch is emitted. The Admin audit entry is still written.
One Admin audit entry is recorded with the action `force_add_to_guild`, the target type `user`, the added user as the target, and the guild ID in the metadata.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:guild:modify` bucket.
## Remove guild member
<RouteHeader method="DELETE" path="/v1/admin/guilds/{guild_id}/members/{user_id}" auditReason />
Removes a member from a guild without banning them and returns 204 with an empty body. Requires `guild:kick_member`.
:::caution[Removal also needs KICK_MEMBERS in the guild]
The acting account must see the guild, hold [KICK_MEMBERS](/http-api/permissions/) in it, and rank above the target. A guild at [MFA level](/http-api/guilds/#mfa-levels) 1 also requires an enrolled authenticator.
:::
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
| user_id | snowflake | The member to remove |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 204 | empty | Member was removed |
| 400 | [error response](/admin-api/#error-response) | `TWO_FACTOR_REQUIRED` in an MFA level 1 guild |
| 403<sup>1</sup> | [error response](/admin-api/#error-response) | `MISSING_ACCESS` when the acting account cannot see the guild, or `MISSING_PERMISSIONS` when `KICK_MEMBERS` is absent |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_MEMBER`, because the target is not a member, is the guild owner, or is the acting account |
<sup>1</sup> `MISSING_PERMISSIONS` also covers a target who outranks the acting account
### Side effects
Fluxer snapshots the membership metadata, including any communication timeout, so that a later rejoin restores it. It then deletes the membership, decreases the recorded member count by one, and detaches the user from the guild on the main Gateway. The member is removed from guild member search when the guild has an indexed member set.
[Guild Member Remove](/gateway/events/#guild-member-remove) fires to the guild. A `MEMBER_KICK` entry is written to the guild's own [audit log](/http-api/guild-audit-logs/#audit-actions). The entry names the acting Admin account and has the audit reason, and the write fires [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create) to sessions holding [VIEW_AUDIT_LOG](/http-api/permissions/).
The removal creates no ban record, so the user can rejoin.
One Admin audit entry is recorded with the action `kick_member`, the target type `guild_member`, the removed user as the target, and the guild and user IDs in the metadata.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:guild:modify` bucket.
## Ban guild member
<RouteHeader method="PUT" path="/v1/admin/guilds/{guild_id}/bans/{user_id}" auditReason />
Bans a user from a guild, optionally removes their recent messages, and returns 204 with an empty body. The target need not be a member. Requires `guild:ban_member`.
:::caution[A ban also needs BAN_MEMBERS in the guild]
The acting account must hold [BAN_MEMBERS](/http-api/permissions/) in the guild, and must rank above a target who is a member. A guild at [MFA level](/http-api/guilds/#mfa-levels) 1 also requires an enrolled authenticator.
:::
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
| user_id | snowflake | The user to ban |
### JSON body
| Field | Type | Description |
| --- | --- | --- |
| delete_message_seconds?<sup>1</sup> | integer | The window of recent messages to delete, in seconds (0-604800) |
| delete_message_days?<sup>1</sup> | integer | The window of recent messages to delete, in days (0-7, default 0) |
| reason? | ?string | The guild ban reason stored on the ban record (at most 512 characters) |
| ban_duration_seconds?<sup>2</sup> | integer | The ban duration in seconds, either exactly 0 or between 60 and 63072000 |
<sup>1</sup> `delete_message_seconds` wins when both are supplied, and `delete_message_days` is otherwise multiplied by 86400. A resolved window of 0 deletes no message
<sup>2</sup> Omitting the field and sending 0 both produce a permanent ban. There is no field that sets an absolute expiry timestamp
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 204 | empty | User was banned |
| 400 | [error response](/admin-api/#error-response) | Duration, reason, or message window validation fails, or `TWO_FACTOR_REQUIRED` in an MFA level 1 guild |
| 403 | [error response](/admin-api/#error-response) | `MISSING_PERMISSIONS` because `BAN_MEMBERS` is absent in the guild or the target member outranks the acting account |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_MEMBER` because the target is the acting account, or `UNKNOWN_USER` because the user does not exist |
:::caution[The ban can also delete messages]
The ban removes current membership and blocks later joins until its expiry or explicit removal. Messages inside the resolved deletion window are permanently deleted and are not restored when the ban ends.
:::
### Side effects
The ban record names the acting Admin account as moderator and has the expiry, the reason, the target's last known IP address unless that address is on the ban exemption list, and the target's lowercased email address. While the ban exists the guild also blocks that address and that email, as described by [Guild moderation](/http-api/guild-moderation/#guild-ban-object), and removing the ban releases both.
A positive deletion window queues a background job that deletes the target's matching messages after the response, which fires [Message Delete Bulk](/gateway/events/#message-delete-bulk) as deletion progresses.
[Guild Ban Add](/gateway/events/#guild-ban-add) fires to the guild. A target who was a member is then removed, which decreases the recorded member count, detaches the user from the guild on the main Gateway, removes the member from guild member search, and fires [Guild Member Remove](/gateway/events/#guild-member-remove). The ban path snapshots no membership metadata, so a communication timeout in force at the moment of the ban is not restored on a later rejoin. This operation writes no entry to the guild's own audit log, and it emits no [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create).
One Admin audit entry is recorded with the action `ban_member`, the target type `guild_member`, the banned user as the target, and the guild ID, user ID, `delete_message_days` value, and any supplied reason and duration in the metadata.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:guild:modify` bucket.
## List guild emojis
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/emojis" />
Returns every custom emoji of a guild as [Admin guild expression](#admin-guild-expression-object) objects. Requires `asset:purge`.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### Response body
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The guild the listing covers, echoed from the path |
| emojis<sup>1</sup> | array[[Admin guild expression](#admin-guild-expression-object) object] | Every custom emoji of the guild |
<sup>1</sup> The listing is not paginated and takes no query parameters
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Emojis were returned, or the guild owns none |
The operation does not verify that the guild exists, so an unknown guild ID returns an empty listing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## List guild stickers
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/stickers" />
Returns every sticker of a guild as [Admin guild expression](#admin-guild-expression-object) objects. Requires `asset:purge`.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### Response body
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The guild the listing covers, echoed from the path |
| stickers<sup>1</sup> | array[[Admin guild expression](#admin-guild-expression-object) object] | Every sticker of the guild |
<sup>1</sup> The listing is not paginated and takes no query parameters
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Stickers were returned, or the guild owns none |
The operation does not verify that the guild exists, so an unknown guild ID returns an empty listing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Purge guild assets
<RouteHeader method="DELETE" path="/v1/admin/guilds/{guild_id}/assets" auditReason />
Deletes emoji and sticker records owned by a guild and queues their stored media for removal, reporting the outcome of every ID separately. Requires `asset:purge`.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### JSON body
| Field | Type | Description |
| --- | --- | --- |
| ids<sup>1</sup> | array[string] | Emoji and sticker IDs to delete with their media, each 1 to 64 characters (at most 100) |
<sup>1</sup> Each entry is trimmed, and Fluxer skips one that is then empty and any repeat of an entry already seen, so neither appears in `processed` or `errors`. An empty array is accepted and purges nothing
### Response body
| Field | Type | Description |
| --- | --- | --- |
| processed | array[[Admin guild asset purge result](#admin-guild-asset-purge-result-object) object] | Assets that were purged |
| errors | array[[Admin guild asset purge error](#admin-guild-asset-purge-error-object) object] | Assets that could not be purged |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | The purge attempt completed, whether or not every ID succeeded |
The operation does not verify that the guild exists. Under an unknown guild ID, an ID that matches another guild's record lands in `errors` and every other ID is reported as `unknown`.
:::danger[Successful asset purges cannot be undone]
Every ID in `processed` loses its record and its stored media permanently. The operation still returns 200 when other IDs fail.
:::
### Side effects
A record owned by the guild in the path is deleted, and its stored media is queued for removal. The guild then receives one [Guild Emojis Update](/gateway/events/#guild-emojis-update) or [Guild Stickers Update](/gateway/events/#guild-stickers-update) Dispatch with its complete remaining expression set. A request that purges several records emits one such Dispatch per record.
A record owned by a different guild is left untouched and reported in `errors`. An ID with no record queues emoji and sticker media removal for that ID and is reported with the `unknown` asset type, so the operation also clears orphaned media.
Every entry in `processed` records its own Admin audit entry, with the numeric ID as the target. A purged emoji records `purge_guild_emoji_asset` with the target type `guild_emoji`, a purged sticker records `purge_guild_sticker_asset` with `guild_sticker`, and an `unknown` ID records `purge_asset` with `asset`. Entries in `errors` record nothing.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:guild:modify` bucket.
## List guild audit logs
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/audit-logs" />
Returns one page of the guild's own in-app audit log, without requiring guild membership or [VIEW_AUDIT_LOG](/http-api/permissions/). Requires `guild:audit_log:view`.
The page has the same shape and the same semantics as the public [List guild audit logs](/http-api/guild-audit-logs/#list-guild-audit-logs) operation, including the message deletion consolidation that operation performs.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### Query parameters
| Field | Type | Description |
| --- | --- | --- |
| limit? | integer | Maximum entries to return (1-100, default 50) |
| before?<sup>1</sup> | snowflake | Return entries older than this entry ID |
| after?<sup>1</sup> | snowflake | Return entries newer than this entry ID |
| user_id?<sup>2</sup> | snowflake | Return only entries recorded for this actor |
| action_type?<sup>2</sup> | integer | Return only entries with this [audit action](/http-api/guild-audit-logs/#audit-actions) value |
<sup>1</sup> The two cursors are mutually exclusive. Supplying both fails with 400 `INVALID_FORM_BODY` and the validation code `CANNOT_SPECIFY_BOTH_BEFORE_AND_AFTER` against `before`
<sup>2</sup> Supplying either filter disables the consolidation of consecutive message deletion entries
### Response body
| Field | Type | Description |
| --- | --- | --- |
| audit_log_entries | array[[guild audit log entry](/http-api/guild-audit-logs/#guild-audit-log-entry-object) object] | The returned page of audit entries |
| users | array[[partial user](/http-api/users/#partial-user-object) object] | Users referenced by the returned entries |
| webhooks | array[[audit log webhook](/http-api/guild-audit-logs/#audit-log-webhook-object) object] | Webhooks referenced by the returned entries |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Audit page was returned, or the guild has no matching entry |
| 400 | [error response](/admin-api/#error-response) | `before` and `after` were supplied together |
The operation does not verify that the guild exists, so an unknown guild ID returns an empty page.
### Side effects
The read records no Admin audit entry and appends no ordinary entry to the guild's own audit log. It still changes that log. Runs of consecutive message deletion entries are replaced with one bulk entry, and that replacement emits [Guild Audit Log Entry Create](/gateway/events/#guild-audit-log-entry-create), exactly as the public operation does.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Reload guild
<RouteHeader method="POST" path="/v1/admin/guilds/{guild_id}/reloads" auditReason />
Reloads a guild's state on the main Gateway. Requires `guild:reload`. The operation takes no request body.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### Response body
| Field | Type | Description |
| --- | --- | --- |
| success | boolean | Always true |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Reload command completed |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_GUILD`, because the guild does not exist |
| 500 | [error response](/admin-api/#error-response) | The API loses its connection to the main Gateway during the call |
A reload the main Gateway reports as failed returns 502 `BAD_GATEWAY`, an unanswered call returns 504 `GATEWAY_TIMEOUT`, and an overloaded cluster returns 503 `SERVICE_UNAVAILABLE`.
### Side effects
The main Gateway reloads the guild's current state from storage and fires one [Guild Update](/gateway/events/#guild-update) Dispatch to every session subscribed to the guild. The operation changes no guild data.
One Admin audit entry is recorded with the action `reload_guild`, the target type `guild`, and the guild ID in both the target and the metadata.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:guild:modify` bucket.
## Shut down guild
<RouteHeader method="POST" path="/v1/admin/guilds/{guild_id}/shutdowns" auditReason />
Unloads a guild from the main Gateway without deleting stored data. Requires `guild:shutdown`. The operation takes no request body.
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### Response body
| Field | Type | Description |
| --- | --- | --- |
| success | boolean | Always true |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | response body | Shutdown command completed |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_GUILD`, because the guild does not exist |
| 500 | [error response](/admin-api/#error-response) | The API loses its connection to the main Gateway during the call |
A shutdown the main Gateway reports as failed returns 502 `BAD_GATEWAY`, an unanswered call returns 504 `GATEWAY_TIMEOUT`, and an overloaded cluster returns 503 `SERVICE_UNAVAILABLE`.
### Side effects
The main Gateway stops the guild process. Every session subscribed to the guild receives a [Guild Delete](/gateway/events/#guild-delete) Dispatch with `unavailable` true and reconnects after one second, which starts the guild again from stored data. Stored guild data is untouched, and [Reload guild](#reload-guild) also starts a stopped guild.
One Admin audit entry is recorded with the action `shutdown_guild`, the target type `guild`, and the guild ID in both the target and the metadata.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:guild:modify` bucket.
## Create guild archive
<RouteHeader method="POST" path="/v1/admin/guilds/{guild_id}/archives" />
Queues an asynchronous archive of the guild's channels, messages, members, roles, and settings, and returns the created [archive](/admin-api/archives/#archive-object) object. Requires `archive:trigger:guild`.
Archive status and downloads are read through [Archives](/admin-api/archives/).
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the guild |
### JSON body
| Field | Type | Description |
| --- | --- | --- |
| include_attachments? | boolean | Whether uploaded files are included in the archive (default false) |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | [archive](/admin-api/archives/#archive-object) object | Archive was created and queued |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_GUILD`, because the guild does not exist |
| 500 | [error response](/admin-api/#error-response) | Archive creation or queueing fails |
### Side effects
The archive record is created with the acting Admin account as `requested_by`, a `progress_percent` of 0, a `progress_step` of `Queued`, and an `expires_at` that is never extended. The archive is built after the response is returned. A client reads progress through [Get archive](/admin-api/archives/#get-archive).
The operation emits no Gateway Dispatch and records no Admin audit entry.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.