feat(admin): audit admin reads and filter the log by access (#2811)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-16 17:23:03 +02:00
1 parent 03d1354562
commit 3276039e41
100 files changed
+5615 -307

No files matched your search

@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
An Admin API key is a long-lived credential for the [Admin API](/admin-api/). It authenticates as the account that created it, has its own [ACL](/admin-api/#acl-registry) set, and authenticates only requests to paths below `/v1/admin`.
Every operation on this page requires `admin_api_key:manage`. None records an Admin audit entry or reads the `X-Audit-Log-Reason` header. An operation that addresses one key returns 404 `ADMIN_API_KEY_NOT_FOUND` when any of these holds:
Every operation on this page requires `admin_api_key:manage`. A successful operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with target type `admin_api_key` and the `X-Audit-Log-Reason` header as its reason. A request that returns an error records no entry. An operation that addresses one key returns 404 `ADMIN_API_KEY_NOT_FOUND` when any of these holds:
- No key has that identifier.
- Another account created the key.
@@ -104,7 +104,7 @@ The secret cannot be retrieved or rotated later. If it is lost, revoke the key a
## List Admin API keys
<RouteHeader method="GET" path="/v1/admin/api-keys" />
<RouteHeader method="GET" path="/v1/admin/api-keys" auditReason />
Returns every [Admin API key](#admin-api-key-object) object the acting account created, as a bare JSON array with no ordering guarantee. Requires `admin_api_key:manage`.
@@ -118,13 +118,17 @@ A key past its expiry is never returned.
<sup>1</sup> An account that created no key receives an empty array
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_admin_api_keys`, target type `admin_api_key`, target ID `0`, and metadata key `result_count`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Create Admin API key
<RouteHeader method="POST" path="/v1/admin/api-keys" />
<RouteHeader method="POST" path="/v1/admin/api-keys" auditReason />
Creates a key and returns an [Admin API key creation](#admin-api-key-creation-object) object that has the raw credential, with HTTP 200, not 201. Requires `admin_api_key:manage`.
@@ -154,13 +158,17 @@ Fluxer rejects the request with 403 `MISSING_ACL` on the first ungrantable value
| 400 | [error response](/admin-api/#error-response) | Body validation fails, including an `acls` value outside the [ACL registry](/admin-api/#acl-registry), returned as `INVALID_FORM_BODY` |
| 403 | [error response](/admin-api/#error-response) | Credential type is refused, `admin_api_key:manage` is absent, or `acls` names a value the acting credential does not have |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `create_admin_api_key`, target type `admin_api_key`, target ID equal to the new `key_id`, and metadata keys `acls` and `expires_in_days`. `acls` is the stored ACL set joined with commas. `expires_in_days` is present only when the request supplied it. The entry has no name and no raw credential.
### Rate limit
30 requests per minute for each authenticated user, on the `admin:code:generation` bucket.
## Get Admin API key
<RouteHeader method="GET" path="/v1/admin/api-keys/{key_id}" />
<RouteHeader method="GET" path="/v1/admin/api-keys/{key_id}" auditReason />
Returns one [Admin API key](#admin-api-key-object) object. Requires `admin_api_key:manage`.
@@ -179,13 +187,17 @@ This operation never returns the raw credential.
| 200 | [Admin API key](#admin-api-key-object) object | Key was returned |
| 404 | [error response](/admin-api/#error-response) | `ADMIN_API_KEY_NOT_FOUND`, which also covers an expired key and a key created by another account |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_admin_api_key`, target type `admin_api_key`, target ID equal to `key_id`, and no metadata.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Update Admin API key
<RouteHeader method="PATCH" path="/v1/admin/api-keys/{key_id}" />
<RouteHeader method="PATCH" path="/v1/admin/api-keys/{key_id}" auditReason />
Renames a key or replaces its ACL set, and returns the updated [Admin API key](#admin-api-key-object) object. Requires `admin_api_key:manage`.
@@ -223,13 +235,17 @@ An update never rotates the credential, and no field on this route changes the e
<sup>1</sup> The ownership check runs before the ACL grant check, so a key belonging to another account returns 404, not 403
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_admin_api_key`, target type `admin_api_key`, target ID equal to `key_id`, and metadata keys `fields` and `acls`. `fields` lists the supplied field names joined with commas, and is empty for an empty body. `acls` is the stored ACL set joined with commas, present only when the request supplied `acls`. The entry has no name.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
## Revoke Admin API key
<RouteHeader method="DELETE" path="/v1/admin/api-keys/{key_id}" />
<RouteHeader method="DELETE" path="/v1/admin/api-keys/{key_id}" auditReason />
Revokes an Admin API key and returns HTTP 200 with a response body. Requires `admin_api_key:manage`.
@@ -258,6 +274,10 @@ Every later request presenting that credential fails authentication, and no oper
| 200 | response body | Key was revoked |
| 404 | [error response](/admin-api/#error-response) | `ADMIN_API_KEY_NOT_FOUND`, which also covers an expired key and a key created by another account |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `revoke_admin_api_key`, target type `admin_api_key`, target ID equal to `key_id`, and no metadata.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
@@ -94,7 +94,7 @@ Fluxer serves one synthetic application for its own Admin OAuth2 client. The app
## List applications
<RouteHeader method="GET" path="/v1/admin/applications" />
<RouteHeader method="GET" path="/v1/admin/applications" auditReason />
Returns the [Admin application](#admin-application-object) objects selected by exactly one of `owner_id` and `guild_id`. Requires `application:lookup` or `application:list:by_owner`.
@@ -130,13 +130,17 @@ The route returns the complete selection in one response. There is no cursor, no
| 200 | response body | The selected applications were returned |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_USER` when `owner_id` names no account, or `UNKNOWN_GUILD` when `guild_id` names no guild |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with a metadata key `application_count`. An `owner_id` request records action `list_user_applications`, target type `user`, and the owner as target ID. A `guild_id` request records action `list_guild_applications`, target type `guild`, and the guild as target ID. A failed request records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get application
<RouteHeader method="GET" path="/v1/admin/applications/{application_id}" />
<RouteHeader method="GET" path="/v1/admin/applications/{application_id}" auditReason />
Returns one [Admin application](#admin-application-object) object wrapped in a single-member object. Requires `application:lookup`.
@@ -162,6 +166,10 @@ Fluxer answers 200 with `application` set to null when the ID names no applicati
| --- | --- | --- |
| 200 | response body | The lookup completed, whether or not it resolved an application |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_application`, target type `application`, the path `application_id` as target ID, and a metadata key `found`. `found` is `true` when `application` is an object and `false` when it is null. A returned application adds `owner_user_id`, and `bot_user_id` when the application has a bot.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -103,7 +103,7 @@ This object uses camelCase field names, unlike every other Admin response body.
## List archives
<RouteHeader method="GET" path="/v1/admin/archives" />
<RouteHeader method="GET" path="/v1/admin/archives" auditReason />
Returns [archive](#archive-object) objects matching the supplied filters, newest first. Requires an ACL covering the requested `subject_type`.
@@ -145,13 +145,17 @@ Returns [archive](#archive-object) objects matching the supplied filters, newest
| 400 | [error response](/admin-api/#error-response) | `subject_id` is supplied while `subject_type` is `all`. `INVALID_FORM_BODY` has `INVALID_FORMAT` on `subject_type` |
| 403 | [error response](/admin-api/#error-response) | `MISSING_ACL` without an ACL covering the requested subject type |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_archives`, target type `archive`, target ID `0`, and metadata keys `subject_type`, `limit`, `include_expired`, and `result_count`. `subject_type` is the value after Fluxer narrows `all` to the account's ACLs. The entry also has `subject_user_id` or `subject_guild_id` when `subject_id` is supplied, and `requested_by_user_id` when `requested_by` is supplied.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get archive
<RouteHeader method="GET" path="/v1/admin/archives/{subject_type}/{subject_id}/{archive_id}" />
<RouteHeader method="GET" path="/v1/admin/archives/{subject_type}/{subject_id}/{archive_id}" auditReason />
Returns one [archive](#archive-object) object. Requires an ACL covering the subject type.
@@ -182,13 +186,15 @@ Returns one [archive](#archive-object) object. Requires an ACL covering the subj
The read issues no download grant.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_archive`, target type equal to `subject_type`, target ID equal to `subject_id`, and metadata keys `archive_id` and `found`. `found` is `false` when the response has a null archive.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Create archive download
<RouteHeader method="GET" path="/v1/admin/archives/{subject_type}/{subject_id}/{archive_id}/download" />
<RouteHeader method="GET" path="/v1/admin/archives/{subject_type}/{subject_id}/{archive_id}/download" auditReason />
Issues a temporary download URL for a completed archive. Returns an [archive download](#archive-download-object) object on success. Requires an ACL covering the subject type.
@@ -219,7 +225,9 @@ The URL has its own signature and works for seven days for anyone who holds it.
### Side effects
The operation creates a presigned download URL valid for seven days and changes no field of the archive, including `download_url_expires_at`. It records no Admin audit entry.
The operation creates a presigned download URL valid for seven days and changes no field of the archive, including `download_url_expires_at`.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_archive_download_url`, target type equal to `subject_type`, target ID equal to `subject_id`, and metadata key `archive_id`. The entry has no URL. A request that returns 400 or 404 records no entry.
### Rate limit
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
Fluxer has nine blocklists for account access and user content. Each defines its accepted values, matching rules and metadata. Fluxer normalises a value when it adds the value and again when it checks the value.
Each list has its own [Admin ACLs](/admin-api/#acl-registry). A read needs the selected list's `check` permission, an addition or an update needs its `add` permission, and a removal needs its `remove` permission. An account holding `ban:ip:add` writes to the `ip` list and to no other. Every write records the audit reason on the [Admin audit entries](/admin-api/#admin-audit-entry-object) it produces. The reads record nothing.
Each list has its own [Admin ACLs](/admin-api/#acl-registry). A read needs the selected list's `check` permission, an addition or an update needs its `add` permission, and a removal needs its `remove` permission. An account holding `ban:ip:add` writes to the `ip` list and to no other. Every operation records the audit reason on the [Admin audit entries](/admin-api/#admin-audit-entry-object) it produces, and each read produces one entry. An entry about one list has a target type built from the list name with each hyphen written as an underscore, so `url-domain` entries use `url_domain`. `email-domain-suspicious` is the one exception and uses `email_domain`.
Fluxer builds each permission name from `ban:`, the list name with each hyphen written as an underscore, and the verb, so the `url-domain` list uses `ban:url_domain:check`, `ban:url_domain:add`, and `ban:url_domain:remove`. The `email-domain-suspicious` list is the one exception and uses `suspicious_email_domain:check`, `suspicious_email_domain:add`, and `suspicious_email_domain:remove`.
@@ -265,7 +265,7 @@ Each field is a string, except `hashes` and `substrings`, which are `array[strin
## List blocklists
<RouteHeader method="GET" path="/v1/admin/blocklists" />
<RouteHeader method="GET" path="/v1/admin/blocklists" auditReason />
Returns one [blocklist](#blocklist-object) object for every blocklist this instance keeps. Requires any one of the `check` permissions.
@@ -285,13 +285,17 @@ The response is identical for every Admin.
| --- | --- | --- |
| 200 | response body | Catalogue was returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_blocklists`, target type `blocklist`, target ID `0`, and metadata key `result_count`.
### Rate limit
60 requests per minute for each authenticated user, on the `admin:ban:operation` bucket.
## List blocklist entries
<RouteHeader method="GET" path="/v1/admin/blocklists/{list_type}/entries" />
<RouteHeader method="GET" path="/v1/admin/blocklists/{list_type}/entries" auditReason />
Returns a value-ordered cursor page of [blocklist entry](#blocklist-entry-object) objects. Requires the selected list's `check` permission.
@@ -332,6 +336,10 @@ Returns a value-ordered cursor page of [blocklist entry](#blocklist-entry-object
The cost of a page does not fall as the cursor advances, so walk a large list with the largest `limit` the operation accepts.
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_blocklist_entries`, the target type of the selected list, target ID `0`, and metadata keys `list_type`, `limit`, `result_count` for the entries in this page, and `has_more`. `profile-substring` adds `scope`, and a supplied `after` adds `has_after`. The cursor value and the entry values are not recorded. A 400 records nothing.
### Rate limit
60 requests per minute for each authenticated user, on the `admin:ban:operation` bucket.
@@ -422,6 +430,8 @@ Every hash is written with the category `manual`, the severity `2`, and a null c
Each hash is lowercased and replaces any existing entry. Changes become visible across the instance when the job stops, whether it ran to the end or was cancelled, so a cancelled job still enforces the hashes it already wrote. No Gateway Dispatch is emitted.
The request records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `queue_bulk_job`, target type `bulk_job`, the job ID as target ID, and metadata keys `task` and `entity_count`. `task` is `ban_file_shas`, and `entity_count` is the number of submitted hashes.
The job records one [Admin audit entry](/admin-api/#admin-audit-entry-object) per written hash under the action `ban_file_sha`, with that hash in its metadata, exactly as [Add blocklist entry](#add-blocklist-entry) does. It then records one aggregate entry under the action `bulk_ban_file_shas`, with the submitted, processed, successful, and failed counts. A cancelled job records the entries for the hashes it wrote and an aggregate entry marked `cancelled`.
### Rate limit
@@ -479,7 +489,7 @@ The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-obje
## Check blocklist entry
<RouteHeader method="GET" path="/v1/admin/blocklists/{list_type}/entries/{entry_value}" />
<RouteHeader method="GET" path="/v1/admin/blocklists/{list_type}/entries/{entry_value}" auditReason />
Reports whether one value is currently blocked by the selected blocklist and returns a [blocklist check](#blocklist-check-object) object. Requires the selected list's `check` permission.
@@ -533,6 +543,10 @@ A value with no row and no covering match returns 200 with `banned` false, so th
For every list except `email` and `email-domain-suspicious`, a row written on another node becomes visible here after a short propagation delay.
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `check_blocklist_entry`, the target type of the selected list, target ID `0`, and metadata keys `list_type` and `banned`. `profile-substring` adds `scope`. The checked value is not recorded. A 400 records nothing.
### Rate limit
60 requests per minute for each authenticated user, on the `admin:ban:operation` bucket.
@@ -144,7 +144,7 @@ The `task` discriminator selects one of these structures. Every ID array has an
### Side effects
The [job](/admin-api/jobs/) records the acting Admin and audit reason. Queueing writes no Admin audit entry. Failure to create the job returns 500 `INTERNAL_SERVER_ERROR` without starting the operation.
The [job](/admin-api/jobs/) records the acting Admin and audit reason. Queueing records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `queue_bulk_job`, target type `bulk_job`, target ID equal to the job identifier, and metadata keys `task` and `entity_count`. `entity_count` is the length of `guild_ids` for `update_guild_features` and of `user_ids` for every other task. An `add_guild_members` entry also has `guild_id`. Failure to create the job returns 500 `INTERNAL_SERVER_ERROR` without starting the operation or recording an entry.
Entities are processed in the submitted order. [Cancel job](/admin-api/jobs/#cancel-job) stops the run between entities and sets its status to `cancelled`. Completed changes remain in place. A failed or unknown entity counts as failed without stopping the remaining work.
@@ -10,7 +10,7 @@ Admin discovery is the review side of the public guild directory. An Admin decid
Every operation except [Remove discovery listing](#remove-discovery-listing) requires `discovery:review`, which covers the reads, [Review discovery application](#review-discovery-application), [Move discovery listings to a category](#move-discovery-listings-to-a-category), and [Update discovery listing](#update-discovery-listing). [Remove discovery listing](#remove-discovery-listing) requires `discovery:remove` instead. Neither implies the other.
[Move discovery listings to a category](#move-discovery-listings-to-a-category) reads `X-Audit-Log-Reason` and records one `update_discovery_categories` Admin audit entry with the audit reason, the category, and the requested, updated, and failed counts. No other operation here reads that header or records an Admin audit entry. An operation that stores a reason takes it in its request body.
Every operation here records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with the audit reason from `X-Audit-Log-Reason` when it succeeds. An operation that stores a reason on the application takes it in its request body, and that reason is not recorded in the entry.
:::note[Review runs regardless of the discovery setting]
An operator can disable discovery for the whole instance. Only the public [Discovery](/http-api/discovery/) routes read that state, and they return 400 `DISCOVERY_DISABLED` while it is off, so an application can be reviewed into a directory no account can see.
@@ -145,7 +145,7 @@ Every `category_type` and `category_id` here is an integer of 0 through 8. A val
## List discovery applications
<RouteHeader method="GET" path="/v1/admin/discovery/applications" />
<RouteHeader method="GET" path="/v1/admin/discovery/applications" auditReason />
Returns every pending application as an array of [Admin pending application](#admin-pending-application-object) objects. Requires `discovery:review`.
@@ -159,13 +159,17 @@ A guild approved automatically on submission never appears here. An application
| --- | --- | --- |
| 200 | array[[Admin pending application](#admin-pending-application-object) object] | Pending applications were returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_discovery_applications`, target type `guild`, target ID `0`, and metadata key `result_count`.
### Rate limit
30 requests per 10 seconds for each authenticated user, on the `discovery:admin:list` bucket.
## Review discovery application
<RouteHeader method="PATCH" path="/v1/admin/discovery/applications/{guild_id}" />
<RouteHeader method="PATCH" path="/v1/admin/discovery/applications/{guild_id}" auditReason />
Approves or rejects a pending application and returns the updated [discovery application](#discovery-application-object) object. Requires `discovery:review`.
@@ -210,13 +214,15 @@ A rejection moves the application to `rejected` and records the review time, the
An approval whose guild can no longer be resolved still succeeds. Fluxer skips the feature grant and the Dispatch.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `approve_discovery_application` or `reject_discovery_application`, target type `guild`, the guild ID as target ID, and metadata key `status`.
### Rate limit
20 requests per 10 seconds for each authenticated user, on the `discovery:admin:action` bucket.
## List discovery categories
<RouteHeader method="GET" path="/v1/admin/discovery/categories" />
<RouteHeader method="GET" path="/v1/admin/discovery/categories" auditReason />
Returns the fixed [discovery category](/http-api/discovery/#discovery-categories) set as an array of [discovery category](/http-api/discovery/#discovery-category-object) objects, in ascending ID order. Requires `discovery:review`.
@@ -228,13 +234,17 @@ It reports the same set on every instance running the same release and has no li
| --- | --- | --- |
| 200 | array[[discovery category](/http-api/discovery/#discovery-category-object) object] | Categories were returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_discovery_categories`, target type `discovery_category`, target ID `0`, and metadata key `result_count`.
### Rate limit
30 requests per 10 seconds for each authenticated user, on the `discovery:admin:list` bucket.
## List guilds in a discovery category
<RouteHeader method="GET" path="/v1/admin/discovery/categories/{category_id}/listings" />
<RouteHeader method="GET" path="/v1/admin/discovery/categories/{category_id}/listings" auditReason />
Returns an offset page of the approved listings filed under one category, most members first, as an array of [Admin discovery listing](#admin-discovery-listing-object) objects. Requires `discovery:review`.
@@ -261,13 +271,17 @@ A rebuild of the discovery search index does not change what this operation answ
| --- | --- | --- |
| 200 | array[[Admin discovery listing](#admin-discovery-listing-object) object] | The page was returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_discovery_category_listings`, target type `discovery_category`, target ID `0`, and metadata keys `category_id`, `limit`, `offset`, `result_count` for the listings in this page, and `total` for every approved listing in the category.
### Rate limit
30 requests per 10 seconds for each authenticated user, on the `discovery:admin:list` bucket.
## List discovery listings
<RouteHeader method="GET" path="/v1/admin/discovery/listings" />
<RouteHeader method="GET" path="/v1/admin/discovery/listings" auditReason />
Returns every approved listing as an array of [Admin discovery listing](#admin-discovery-listing-object) objects. Requires `discovery:review`.
@@ -279,13 +293,17 @@ The response is a bare array with no guaranteed order, cursor or limit. Pending,
| --- | --- | --- |
| 200 | array[[Admin discovery listing](#admin-discovery-listing-object) object] | Approved listings were returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_discovery_listings`, target type `guild`, target ID `0`, and metadata key `result_count`.
### Rate limit
30 requests per 10 seconds for each authenticated user, on the `discovery:admin:list` bucket.
## Move discovery listings to a category
<RouteHeader method="PATCH" path="/v1/admin/discovery/listings" />
<RouteHeader method="PATCH" path="/v1/admin/discovery/listings" auditReason />
Files every named guild under one [discovery category](/http-api/discovery/#discovery-categories) and reports how many moved. Requires `discovery:review`.
@@ -323,13 +341,15 @@ Repeating the request is safe. The operation moves a pending application the sam
A move replaces the application's `category_type` and preserves its description, language, tags, status, and timestamps. A moved listing that is already approved appears under the new category in the next public search. No guild field changes and no Gateway Dispatch is emitted.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_discovery_categories`, target type `guild`, target ID `0`, and metadata keys `category_type`, `guild_count`, `updated`, and `failed`. It records the entry when every guild fails as well. The guild IDs are not recorded.
### Rate limit
20 requests per 10 seconds for each authenticated user, on the `discovery:admin:action` bucket.
## Update discovery listing
<RouteHeader method="PATCH" path="/v1/admin/discovery/listings/{guild_id}" />
<RouteHeader method="PATCH" path="/v1/admin/discovery/listings/{guild_id}" auditReason />
Updates the stored listing of one guild and returns the updated [discovery application](#discovery-application-object) object. Requires `discovery:review`.
@@ -375,13 +395,15 @@ The supplied members update the application while preserving its status, submiss
No guild field changes, so no [Guild Update](/gateway/events/#guild-update) fires.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_discovery_listing`, target type `guild`, the guild ID as target ID, and metadata key `status` with the stored [application status](/http-api/discovery/#discovery-application-statuses). `fields` names the supplied members joined by commas and is absent for an empty body. A supplied `category_type` or `primary_language` adds a key of the same name with its value. The description and tags are not recorded.
### Rate limit
20 requests per 10 seconds for each authenticated user, on the `discovery:admin:action` bucket.
## Remove discovery listing
<RouteHeader method="DELETE" path="/v1/admin/discovery/listings/{guild_id}" />
<RouteHeader method="DELETE" path="/v1/admin/discovery/listings/{guild_id}" auditReason />
Removes an approved listing from discovery, strips the guild's discoverable feature, and returns the updated [discovery application](#discovery-application-object) object. Requires `discovery:remove`.
@@ -419,6 +441,8 @@ The application moves to `removed` and records the removal time, the removing Ad
A removal whose guild can no longer be resolved still succeeds. Fluxer skips the feature strip and the Dispatch.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `remove_discovery_listing`, target type `guild`, the guild ID as target ID, and no metadata.
### Rate limit
20 requests per 10 seconds for each authenticated user, on the `discovery:admin:action` bucket.
@@ -173,7 +173,7 @@ One entry for each voice server that holds at least one voice state.
## Get Gateway node statistics
<RouteHeader method="GET" path="/v1/admin/gateway/stats" />
<RouteHeader method="GET" path="/v1/admin/gateway/stats" auditReason />
Returns the [node statistics](#node-statistics-object) object for the whole Gateway cluster. Requires `gateway:memory_stats`.
@@ -183,13 +183,17 @@ Returns the [node statistics](#node-statistics-object) object for the whole Gate
| --- | --- | --- |
| 200 | [node statistics](#node-statistics-object) object | Cluster state was returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_gateway_stats`, target type `gateway`, target ID `0`, and metadata key `node_count`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get guild memory statistics
<RouteHeader method="GET" path="/v1/admin/gateway/memory-stats" />
<RouteHeader method="GET" path="/v1/admin/gateway/memory-stats" auditReason />
Returns the heaviest live guild processes as [guild memory statistics](#guild-memory-statistics-object) objects. Requires `gateway:memory_stats`.
@@ -217,13 +221,15 @@ Returns the heaviest live guild processes as [guild memory statistics](#guild-me
A node that does not answer within 5 seconds contributes no entries, and the request still returns 200.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_guild_memory_stats`, target type `guild`, target ID `0`, and metadata keys `limit` and `result_count` for the entries in this response.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get voice state counts
<RouteHeader method="GET" path="/v1/admin/gateway/voice-state-counts" />
<RouteHeader method="GET" path="/v1/admin/gateway/voice-state-counts" auditReason />
Returns the [voice state count](#voice-state-count-object) object. Requires `gateway:memory_stats`.
@@ -237,13 +243,15 @@ Returns the [voice state count](#voice-state-count-object) object. Requires `gat
The counts cover voice states in guild channels and in calls. A node that does not answer within 10 seconds contributes zero, and the request still returns 200.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_voice_state_counts`, target type `gateway`, target ID `0`, and metadata keys `total_voice_states`, `region_count`, and `server_count`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Reload Gateway guilds
<RouteHeader method="POST" path="/v1/admin/gateway/reloads" />
<RouteHeader method="POST" path="/v1/admin/gateway/reloads" auditReason />
Fetches the guild data for the supplied guilds from the database again and sends it to each live guild process and returns how many live guild processes were selected. Requires `gateway:reload_all`.
@@ -281,7 +289,9 @@ Every reloaded guild process fires one [Guild Update](/gateway/events/#guild-upd
### Side effects
A guild whose owner node cannot be resolved is not counted. Reloads can still be in progress when the response arrives. No guild data is changed. The operation records one `reload_guilds` Admin audit entry with the audit reason, the requested guild count, and the reloaded count.
A guild whose owner node cannot be resolved is not counted. Reloads can still be in progress when the response arrives. No guild data is changed.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `reload_guilds`, target type `guild`, target ID `0`, and metadata keys `guild_count` for the requested guilds and `reloaded` for the returned `count`.
### Rate limit
@@ -29,7 +29,7 @@ The unit and the quantity together define the span a redeemed code grants. At re
## Create gift codes
<RouteHeader method="POST" path="/v1/admin/gift-codes" />
<RouteHeader method="POST" path="/v1/admin/gift-codes" auditReason />
Generates the requested number of unredeemed gift codes. Requires `gift_codes:generate`. Returns the complete redemption links on success.
@@ -77,7 +77,7 @@ Each code is 32 characters drawn from the uppercase letters, the lowercase lette
A failed request can leave some codes redeemable without returning them. Retrying can therefore create additional codes.
The operation records one `generate_gift_codes` Admin audit entry with the audit reason, the code count, and the requested duration. The codes themselves are not recorded. It emits no Gateway Dispatch.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `generate_gift_codes`, target type `gift_code`, target ID `0`, and metadata keys `count`, `duration_type`, and `duration_quantity`. The codes are not recorded. It emits no Gateway Dispatch.
### Rate limit
@@ -232,7 +232,7 @@ An ID owned by a different guild appears in `errors` with no asset type.
## List guilds
<RouteHeader method="GET" path="/v1/admin/guilds" />
<RouteHeader method="GET" path="/v1/admin/guilds" auditReason />
Searches guilds through the guild search index and returns [Admin guild](#admin-guild-object) objects. Requires `guild:lookup`.
@@ -264,13 +264,17 @@ The index matches `q` against the guild name, the discovery tags, the custom inv
| 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 |
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `search_guilds`, the target type `guild`, and the target ID `0`. Its metadata keys are `limit`, `offset`, `result_count` for the guilds in this page, and `total`. `has_query` is added as `true` when `q` is supplied, and the query text is not recorded. A 403 records nothing.
### 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}" />
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}" auditReason />
Returns one guild with its channels and roles. Requires `guild:lookup`.
@@ -294,6 +298,10 @@ Returns one guild with its channels and roles. Requires `guild:lookup`.
| --- | --- | --- |
| 200 | response body | A lookup was done, whether or not it resolved |
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `get_guild`, the target type `guild`, and the requested guild ID as the target. Its metadata key `found` is `true` when the guild resolved and `false` when `guild` is null.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -314,7 +322,7 @@ Fluxer evaluates authorisation in two stages and reads the body between them. Th
- `vanity_url_code` selects `guild:update:vanity`.
- `new_owner_id` selects `guild:transfer_ownership`.
A body with no field at all selects nothing, so an empty patch applies no change.
A body with no field at all selects nothing, so an empty patch applies no change and records one `update_guild` entry.
### Path parameters
@@ -385,7 +393,7 @@ Supplying `add_features` or `remove_features` reconciles an existing discovery a
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.
Each applied group records one Admin audit entry with the target type `guild` and the guild ID as the target. A body that applies no group records one entry with action `update_guild` and no metadata instead.
- `clear_fields` records the cleared field names.
- `update_settings` records each applied setting.
@@ -439,7 +447,7 @@ One Admin audit entry is recorded with the action `delete_guild`, the target typ
## List guild members
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/members" />
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/members" auditReason />
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`.
@@ -480,6 +488,10 @@ A guild the main Gateway cannot load answers 404 `UNKNOWN_GUILD`.
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`.
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `list_guild_members`, the target type `guild`, and the guild ID as the target. Its metadata keys are `limit` and `offset` as applied, `result_count` for the members in this page, and `total`. An error response records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -628,7 +640,7 @@ One Admin audit entry is recorded with the action `ban_member`, the target type
## List guild emojis
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/emojis" />
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/emojis" auditReason />
Returns every custom emoji of a guild as [Admin guild expression](#admin-guild-expression-object) objects. Requires `asset:purge`.
@@ -655,13 +667,17 @@ Returns every custom emoji of a guild as [Admin guild expression](#admin-guild-e
The operation does not verify that the guild exists, so an unknown guild ID returns an empty listing.
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `list_guild_emojis`, the target type `guild`, and the guild ID as the target. Its metadata key `result_count` is the number of emojis returned.
### 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" />
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/stickers" auditReason />
Returns every sticker of a guild as [Admin guild expression](#admin-guild-expression-object) objects. Requires `asset:purge`.
@@ -688,6 +704,10 @@ Returns every sticker of a guild as [Admin guild expression](#admin-guild-expres
The operation does not verify that the guild exists, so an unknown guild ID returns an empty listing.
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `list_guild_stickers`, the target type `guild`, and the guild ID as the target. Its metadata key `result_count` is the number of stickers returned.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -745,7 +765,7 @@ Every entry in `processed` records its own Admin audit entry, with the numeric I
## List guild audit logs
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/audit-logs" />
<RouteHeader method="GET" path="/v1/admin/guilds/{guild_id}/audit-logs" auditReason />
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`.
@@ -790,7 +810,9 @@ The operation does not verify that the guild exists, so an unknown guild ID retu
### 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.
The read 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.
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `list_guild_audit_logs`, the target type `guild`, and the guild ID as the target. Its metadata key `result_count` is the number of entries returned. The supplied query parameters add `limit`, `before`, `after`, `filter_user_id` for `user_id`, and `action_type`. A 400 records nothing.
### Rate limit
@@ -874,7 +896,7 @@ One Admin audit entry is recorded with the action `shutdown_guild`, the target t
## Create guild archive
<RouteHeader method="POST" path="/v1/admin/guilds/{guild_id}/archives" />
<RouteHeader method="POST" path="/v1/admin/guilds/{guild_id}/archives" auditReason />
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`.
@@ -904,7 +926,9 @@ Archive status and downloads are read through [Archives](/admin-api/archives/).
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.
The operation emits no Gateway Dispatch.
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `trigger_guild_archive`, the target type `guild`, and the guild ID as the target. Its metadata keys are `archive_id` for the new archive and `include_attachments`, which is `true` or `false`.
### Rate limit
+130 -16
View File
@@ -74,11 +74,15 @@ The [ACL registry](#acl-registry) below lists every value the instance recognise
An Admin audit entry records one Admin action against one target.
Not every Admin operation records an entry. Each Admin resource page states, per operation, whether it records one and what that entry stores.
Every Admin operation records at least one entry, reads included. Five reads are the exception and record nothing. They are [Get current Admin user](/admin-api/users/#get-current-admin-user), [List jobs](/admin-api/jobs/#list-jobs), [List active jobs](/admin-api/jobs/#list-active-jobs), [Get job](/admin-api/jobs/#get-job), and [Get search index refresh](/admin-api/search-indexes/#get-search-index-refresh). Each Admin resource page states, per operation, what its entry stores, and names the requests that change nothing and so record nothing, such as a purge whose every ID fails.
A request refused by authentication, ACL evaluation, rate limiting, or body validation records nothing.
A read records its entry after the data is loaded and before the response is sent. A read that returns 200 with no match still records one. A read entry stores identifiers, filter values from a fixed set, counts, and whether a free-text query was supplied. It never stores query text, email addresses, IP addresses, blocklist values, or other free-form input.
Fluxer can record more than one entry for one request. [Update guild](/admin-api/guilds/#update-guild) records one entry for each mapped field group it applies. A queued bulk job records one summary entry when it finishes, with the creating Admin and that request's audit reason. The `update_user_flags`, `update_guild_features`, and `schedule_user_deletion` tasks also record one ordinary entry for each account or guild the worker changes, with `audit_log_reason` null.
An entry is a read entry when its action is one of the [read actions](#read-actions), and a write entry otherwise. The `access` field reports which, and the `access` query parameter of [List audit log entries](#list-audit-log-entries) filters on it.
A request refused by authentication, ACL evaluation, rate limiting, or path, query, or body validation records nothing.
Fluxer can record more than one entry for one request. [Update guild](/admin-api/guilds/#update-guild) records one entry for each mapped field group it applies. A bulk job records a `queue_bulk_job` entry when it is queued and one summary entry when it finishes, both with the creating Admin and that request's audit reason. The `update_user_flags`, `update_guild_features`, and `schedule_user_deletion` tasks also record one ordinary entry for each account or guild the worker changes, with `audit_log_reason` null.
An entry stores an acting Admin, a target type, a target ID, an action name, an optional audit reason, and a string metadata map. Fluxer fills every other field of the entry when the entry is read.
@@ -98,13 +102,14 @@ An entry stores an acting Admin, a target type, a target ID, an action name, an
| related_guilds<sup>6</sup> | map[snowflake, [Admin guild summary](#admin-guild-summary-object) object] | The guilds the entry references, keyed by ID |
| related_channels<sup>6</sup> | map[snowflake, [Admin channel summary](#admin-channel-summary-object) object] | The channels the entry references, keyed by ID |
| action | string | The [audit action](#audit-actions) the service recorded (1-256 characters) |
| access | string | `read` for an entry recorded by a read, `write` for any other entry |
| audit_log_reason | ?string | The normalised [audit reason](#audit-reasons) (1-4,000 characters), or null when the request supplied none |
| metadata<sup>7</sup> | map[string, string] | The additional values the operation recorded |
| created_at | ISO8601 timestamp | The time the entry was recorded |
<sup>1</sup> Resolved from `admin_user_id` at read time, so an account deleted after the entry was written reports null while `admin_user_id` still has the ID
<sup>2</sup> Serialised as a decimal string, like a [snowflake](/snowflakes/). A blocklist entry, a voice region, and a voice server record `0` here and their textual identity in `metadata`
<sup>2</sup> Serialised as a decimal string, like a [snowflake](/snowflakes/). A read that is not scoped to one account, guild, channel, or message, a blocklist entry, a voice region, and a voice server record `0` here, and any identity they have is in `metadata`
<sup>3</sup> Resolved only when `target_type` is `user`, `guild_member`, `message_deletion`, or `message_shred`
@@ -146,6 +151,7 @@ An entry has no request method, request path, status, duration, IP address, or f
"related_guilds": {},
"related_channels": {},
"action": "change_username",
"access": "write",
"audit_log_reason": "Impersonation report 4821",
"metadata": {
"old_username": "subj3ct",
@@ -201,11 +207,25 @@ An entry has no request method, request path, status, duration, IP address, or f
| message | Message |
| message_deletion<sup>1</sup> | Bulk deletion of every message one account sent |
| message_shred<sup>1</sup> | Message shred job for one account |
| channel | Channel |
| asset | Stored media asset |
| application | OAuth2 application |
| report | User report |
| search_index | Search index |
| system_dm | System DM broadcast |
| gift_code<sup>2</sup> | Batch of generated gift codes |
| bulk_job | Background job |
| admin_api_key | Admin API key |
| audit_log | Admin audit entry |
| archive<sup>2</sup> | Archive list |
| admin_acl<sup>2</sup> | Admin ACL registry |
| blocklist<sup>2</sup> | Blocklist catalogue |
| discovery_category<sup>2</sup> | Discovery category |
| instance_config<sup>2</sup> | Instance configuration |
| limit_config<sup>2</sup> | Limit configuration |
| registration_url<sup>3</sup> | Admin-issued registration URL |
| gateway<sup>2</sup> | Gateway cluster |
| system<sup>2</sup> | API process that served the request |
| voice_region<sup>2</sup> | Voice region |
| voice_server<sup>2</sup> | Voice server registration |
| ip<sup>2</sup> | IP blocklist entry or suspicious IP marker |
@@ -218,28 +238,90 @@ An entry has no request method, request path, status, duration, IP address, or f
| avatar_hash<sup>2</sup> | Avatar hash blocklist entry |
| profile_substring<sup>2</sup> | Profile substring blocklist entry |
<sup>1</sup> The target ID is the account the job acts on, and the entry resolves that account into `target_user`
<sup>1</sup> The target ID is the account the job acts on, and the entry resolves that account into `target_user`. [Get message shred job](/admin-api/messages/#get-message-shred-job) is the exception and records `0`
<sup>2</sup> The target has no snowflake, so `target_id` is `0` and the entry's textual identity is in `metadata`
<sup>2</sup> The target has no snowflake, so `target_id` is `0`, and any identity the entry has is in `metadata`
<sup>3</sup> The ID of a registration URL is its redeemable code, so `target_id` is `0` and the entry stores no ID
The field is a free-form string, so a client MUST accept a value outside this table.
### Audit actions
An operation records one of the following actions. The field is a free-form string of 1 to 256 characters, so a client MUST accept a value outside this table.
An operation records one of the following actions. The field is a free-form string of 1 to 256 characters, so a client MUST accept a value outside these tables.
#### Read actions
An entry with one of these actions has `access` set to `read`.
| Value | Description |
| --- | --- |
| check_blocklist_entry | A value was checked against a blocklist |
| get_admin_api_key | An Admin API key was read |
| get_application | An application was read |
| get_archive | An archive was read |
| get_archive_download_url | A download URL for an archive was issued |
| get_audit_log | An Admin audit entry was read |
| get_gateway_stats | Gateway node statistics were read |
| get_guild | A guild was read |
| get_instance_config | The instance configuration was read |
| get_limit_config | The limit configuration was read |
| get_message | A message and its surrounding messages were read |
| get_message_shred_status | The state of a message shred job was read |
| get_report | A report was read |
| get_user | An account was read |
| get_voice_region | A voice region was read |
| get_voice_server | A voice server was read |
| get_voice_state_counts | Gateway voice state counts were read |
| list_admin_acls | The Admin ACL registry was read |
| list_admin_api_keys | The acting Admin's API keys were listed |
| list_archives | Archives were listed |
| list_audit_logs | A page of the Admin audit log was read |
| list_blocklist_entries | A page of blocklist entries was read |
| list_blocklists | The blocklist catalogue was read |
| list_channel_messages | The messages of a channel were listed |
| list_discovery_applications | Pending discovery applications were read |
| list_discovery_categories | The discovery categories were read |
| list_discovery_category_listings | A page of the discovery listings in one category was read |
| list_discovery_listings | Approved discovery listings were read |
| list_guild_applications | The applications whose bots are members of a guild were read |
| list_guild_audit_logs | The audit log of a guild was read |
| list_guild_emojis | The custom emojis of a guild were listed |
| list_guild_members | The members of a guild were listed |
| list_guild_memory_stats | Guild memory statistics were listed |
| list_guild_stickers | The stickers of a guild were listed |
| list_user_applications | The applications an account owns were read |
| list_user_change_log | The contact change log of an account was read |
| list_user_dm_channels | The direct message channels of an account were read |
| list_user_guilds | The guild memberships of an account were read |
| list_user_relationships | The relationships of an account were read |
| list_user_sessions | An account's login sessions were read |
| list_voice_regions | Voice regions were listed |
| list_voice_servers | The voice servers of a region were listed |
| list_webauthn_credentials | An account's WebAuthn credentials were read |
| search_audit_logs | The Admin audit log was searched |
| search_guilds | Guilds were listed or searched |
| search_messages | The messages of a channel were searched, or one message was looked up |
| search_reports | Reports were listed or searched |
| search_users | Accounts were listed or searched |
#### Write actions
An entry with any other action has `access` set to `write`.
| Value | Description |
| --- | --- |
| NCMEC Report | An attachment was submitted through the NCMEC reporting integration |
| add_suspicious_email_domain | A domain was added to the suspicious email domain list |
| approve_discovery_application | A discovery application was approved |
| approve_registration | A pending registration was approved |
| auto_resolve_reports_on_deletion | Reports against an account were resolved automatically as part of its deletion |
| ban_avatar_hash | An avatar hash was added to the avatar blocklist |
| ban_email | An address was added to the email blocklist |
| ban_file_sha | A file hash was added to the attachment blocklist |
| ban_ip | An address was added to the IP blocklist |
| ban_ip_skipped_exempt | An IP blocklist entry was declined by the instance exemption list |
| ban_ip_skipped_cgnat | An IP blocklist entry was declined because IP intelligence reports the address as a mobile carrier network |
| ban_ip_skipped_exempt | An IP blocklist entry was declined by the instance exemption list |
| ban_member | An account was banned from a guild |
| ban_phrase | A phrase was added to the message phrase blocklist |
| ban_profile_substring | A substring was added to the profile substring blocklist |
@@ -247,16 +329,21 @@ An operation records one of the following actions. The field is a free-form stri
| ban_url_domain | A domain was added to the URL domain blocklist |
| bulk_add_guild_members | A bulk guild member addition was queued or applied |
| bulk_ban_file_shas | A queued bulk file hash blocklist job was applied |
| bulk_delete_user_messages | A queued bulk message deletion job was applied |
| bulk_schedule_deletion | A bulk account deletion schedule was queued or applied |
| bulk_update_guild_features | A bulk guild feature change was queued or applied |
| bulk_update_suspicious_activity_flags | A bulk suspicious activity flag change was queued or applied |
| bulk_update_user_flags | A bulk account flag change was queued or applied |
| cancel_bulk_message_deletion | A bulk message deletion an account scheduled for itself was cancelled |
| cancel_deletion | A scheduled account deletion was cancelled |
| cancel_job | Cancellation of a job was requested |
| change_dob | A date of birth was changed |
| change_email | An email address was changed |
| change_username | A username or discriminator was changed |
| clear_fields<sup>1</sup> | Profile fields of an account, or fields of a guild, were cleared |
| create_admin_api_key | An Admin API key was created |
| create_heap_snapshot | A heap snapshot of the API process was written |
| create_registration_url | A registration URL was issued |
| create_voice_region | A voice region was created |
| create_voice_server | A voice server was registered |
| delete_all_user_messages | Deletion of every message an account sent was queued |
@@ -269,26 +356,31 @@ An operation records one of the following actions. The field is a free-form stri
| disable_mfa | Multi-factor authentication was disabled on an account |
| disable_suspicious_activity | An account flagged for suspicious activity was disabled |
| force_add_to_guild | An account was added to a guild while bypassing the ban check |
| generate_gift_codes | Gift codes were generated |
| kick_member | A member was removed from a guild |
| list_user_sessions | An account's login sessions were read |
| list_webauthn_credentials | An account's WebAuthn credentials were read |
| mark_suspicious_ip | An IP address was marked as suspicious |
| mark_suspicious_ip_skipped_high_blast_radius | IP intelligence reports the address as a mobile, anycast, satellite, or education network, so no marker was written |
| mark_suspicious_ip_skipped_invalid | The address could not be parsed, so no marker was written |
| mark_suspicious_ip_skipped_ipinfo_unavailable | IP intelligence was unavailable, so no marker was written |
| mark_suspicious_ip_skipped_trusted_commercial_privacy_provider | The address belongs to a trusted commercial privacy provider, so no marker was written |
| mark_suspicious_ip_skipped_high_blast_radius | IP intelligence reports the address as a mobile, anycast, satellite, or education network, so no marker was written |
| purge_asset | A stored media asset was purged |
| purge_guild_emoji_asset | A guild emoji asset was purged |
| purge_guild_sticker_asset | A guild sticker asset was purged |
| queue_bulk_job | A bulk job was queued |
| queue_message_shred | A message shred job was queued |
| queue_refresh_index | A search index rebuild was requested |
| reject_discovery_application | A discovery application was rejected |
| reject_registration | A pending registration was rejected |
| reload_guild | One guild was reloaded on the main Gateway |
| reload_guilds | Many guilds were reloaded on the main Gateway |
| remove_discovery_listing | A guild was removed from discovery |
| remove_relationship | One relationship was removed from an account |
| remove_relationships_by_category | Every relationship in one category was removed from an account |
| remove_suspicious_email_domain | A domain was removed from the suspicious email domain list |
| resend_verification_email | An email verification message was requested again for an account |
| resolve_report | A report was updated or resolved |
| revoke_admin_api_key | An Admin API key was revoked |
| revoke_registration_url | A registration URL was revoked |
| schedule_deletion | Account deletion was scheduled |
| send_password_reset | A password reset message was requested for an account |
| set_acls | The Admin ACL set of an account was replaced |
@@ -299,7 +391,9 @@ An operation records one of the following actions. The field is a free-form stri
| system_dm.send | A system DM broadcast was created |
| temp_ban | A temporary or permanent account ban was applied |
| terminate_sessions | Every login session of an account was revoked |
| test_smtp_connection | An SMTP connection test was run |
| transfer_ownership<sup>1</sup> | Guild ownership or OAuth2 application ownership was transferred |
| trigger_guild_archive | A guild archive was created |
| trigger_user_archive | A user archive was created |
| unban | An account ban was lifted |
| unban_avatar_hash | An avatar hash was removed from the avatar blocklist |
@@ -310,9 +404,15 @@ An operation records one of the following actions. The field is a free-form stri
| unban_profile_substring | A substring was removed from the profile substring blocklist |
| unban_url | A URL was removed from the URL blocklist |
| unban_url_domain | A domain was removed from the URL domain blocklist |
| update_admin_api_key | An Admin API key was renamed or its ACLs were replaced |
| update_discovery_categories | Guilds were moved to a discovery category |
| update_discovery_listing | A discovery listing was edited |
| update_features | Guild features were added or removed |
| update_guild | A guild update with no field group was sent |
| update_flags | Account flags were changed |
| update_has_verified_phone | Verified phone state was changed |
| update_instance_config | The instance configuration was updated |
| update_limit_config | The limit configuration was replaced |
| update_name | A guild name was changed |
| update_premium_flags | Premium flags were changed |
| update_settings | General guild settings were changed |
@@ -320,6 +420,7 @@ An operation records one of the following actions. The field is a free-form stri
| update_vanity | A guild custom invite code was changed |
| update_voice_region | A voice region was updated |
| update_voice_server | A registered voice server was updated |
| upload_branding_asset | An instance branding asset was uploaded or cleared |
| verify_email | An email address was marked verified |
<sup>1</sup> The same action name is recorded for two different resources, so `target_type` tells them apart
@@ -380,7 +481,7 @@ A successful Admin request from an account that is not a bot has no informationa
## List ACLs
<RouteHeader method="GET" path="/v1/admin/acls" />
<RouteHeader method="GET" path="/v1/admin/acls" auditReason />
Returns every Admin permission string the instance recognises, in [ACL registry](#acl-registry) order. Requires `admin:authenticate`.
@@ -399,6 +500,10 @@ Returns every Admin permission string the instance recognises, in [ACL registry]
| 200 | response body | ACL registry was returned |
| 403 | [error response](#error-response) | Credential type is refused, or the account holds neither `admin:authenticate` nor `*` |
### Side effects
The operation records one [Admin audit entry](#admin-audit-entry-object) with action `list_admin_acls`, target type `admin_acl`, target ID `0`, and metadata key `acl_count`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -538,12 +643,10 @@ The registry is returned in this order by [List ACLs](#list-acls). A value outsi
## List audit log entries
<RouteHeader method="GET" path="/v1/admin/audit-logs" />
<RouteHeader method="GET" path="/v1/admin/audit-logs" auditReason />
Returns a page of [Admin audit entry](#admin-audit-entry-object) objects with their resolved user, guild, and channel summaries. Requires `audit_log:view`.
Reading the log records no entry of its own.
### Query parameters
| Field | Type | Description |
@@ -552,6 +655,7 @@ Reading the log records no entry of its own.
| admin_user_id? | snowflake | Return only entries recorded for this acting Admin |
| target_type? | string | Return only entries recorded against this [audit target type](#audit-target-types) (1-64 characters) |
| target_id? | string | Return only entries recorded against this target ID |
| access? | string | `read` to return only read entries, `write` to return only write entries |
| sort_by?<sup>2</sup> | string | `createdAt` or `relevance` (default `createdAt`) |
| sort_order?<sup>2</sup> | string | `asc` or `desc` (default `desc`) |
| limit? | integer | The maximum number of entries to return (1-200, default 50) |
@@ -581,13 +685,19 @@ Without an index, `q` is ignored, and a filtered page holds only the matches amo
| 200 | response body | Audit page was returned |
| 403 | [error response](#error-response) | Credential type is refused, or the account holds neither `audit_log:view` nor `*` |
### Side effects
The operation records one [Admin audit entry](#admin-audit-entry-object) with target type `audit_log` and target ID `0`. Its action is `search_audit_logs` when `q` is supplied and `list_audit_logs` otherwise. The metadata keys are `limit`, `offset`, `result_count` for the entries in this page, and `total`. The supplied filters add `filter_admin_user_id` and `access`, `filter_target_id` when `target_id` is a snowflake, and `filter_target_type` when `target_type` is lowercase letters and underscores. Any other `target_type` value adds `has_target_type_filter` instead. A search also records `sort_by` and `sort_order`. The query text is not recorded.
The entry is written after the page is built, so it is never part of the page that recorded it.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:audit_log` bucket.
## Get audit log entry
<RouteHeader method="GET" path="/v1/admin/audit-logs/{log_id}" />
<RouteHeader method="GET" path="/v1/admin/audit-logs/{log_id}" auditReason />
Returns one [Admin audit entry](#admin-audit-entry-object) object with the same resolved summaries the listing returns. Requires `audit_log:view`.
@@ -605,6 +715,10 @@ Returns one [Admin audit entry](#admin-audit-entry-object) object with the same
| 403 | [error response](#error-response) | Credential type is refused, or the account holds neither `audit_log:view` nor `*` |
| 404 | [error response](#error-response) | No entry has that identifier and the request returns `NOT_FOUND` |
### Side effects
The operation records one [Admin audit entry](#admin-audit-entry-object) with action `get_audit_log`, target type `audit_log`, the read entry's ID as target ID, and no metadata. A 404 records nothing.
### Rate limit
100 requests per minute for each authenticated user, on the shared `admin:audit_log` bucket.
@@ -494,7 +494,7 @@ Display metadata for one [limit key](/http-api/instance/#limit-keys), which an e
## Get instance configuration
<RouteHeader method="GET" path="/v1/admin/instance/config" />
<RouteHeader method="GET" path="/v1/admin/instance/config" auditReason />
Returns the [instance configuration](#instance-configuration-object) object. Requires `instance:config:view`, or a session credential until setup is marked complete.
@@ -504,13 +504,17 @@ Returns the [instance configuration](#instance-configuration-object) object. Req
| --- | --- | --- |
| 200 | [instance configuration](#instance-configuration-object) object | Configuration was returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_instance_config`, target type `instance_config`, target ID `0`, and metadata keys `registration_url_count` and `pending_registration_count`. Before setup is complete, a session credential holding no Admin ACL records the entry with its own account as the acting Admin.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Update instance configuration
<RouteHeader method="PATCH" path="/v1/admin/instance/config" />
<RouteHeader method="PATCH" path="/v1/admin/instance/config" auditReason />
Applies a merge patch to the stored configuration and returns the resulting [instance configuration](#instance-configuration-object) object. Requires `instance:config:update`, or a session credential until setup is marked complete.
@@ -583,7 +587,7 @@ Fluxer publishes a `gateway_rollout` change to the Gateway cluster. Premium mode
Initial setup completes on the first update that sets `app_public.setup.configured` to true from a session credential whose account holds neither `admin:authenticate` nor the wildcard. That update grants the account the wildcard Admin ACL and marks the deployment as bootstrapped.
The operation records no Admin audit entry of its own.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_instance_config`, target type `instance_config`, and target ID `0`. The metadata key `sections` lists the names of the non-null top-level sections in the body, comma-separated in alphabetical order, and is absent when the body supplies none. The update that completes initial setup and grants the wildcard ACL also records `granted_acls` as `*`. No value from the body is recorded. A request that fails part way records no entry.
### Rate limit
@@ -591,7 +595,7 @@ The operation records no Admin audit entry of its own.
## Create branding asset
<RouteHeader method="POST" path="/v1/admin/instance/config/branding-assets" />
<RouteHeader method="POST" path="/v1/admin/instance/config/branding-assets" auditReason />
Uploads an image, stores its [Media Proxy](/media-proxy/overview/) URL in the selected branding slot, and returns the resulting [instance configuration](#instance-configuration-object) object. Requires `instance:config:update`, or a session credential until setup is marked complete.
@@ -619,13 +623,15 @@ Uploads an image, stores its [Media Proxy](/media-proxy/overview/) URL in the se
The stored image is served through the Media Proxy. Non-JPEG images are re-encoded to strip metadata and JPEG images are re-encoded at quality 100. The previously referenced image is not deleted.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `upload_branding_asset`, target type `instance_config`, target ID `0`, and metadata keys `kind` and `cleared`. `cleared` is `true` when the request cleared the slot.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
## Create SMTP test
<RouteHeader method="POST" path="/v1/admin/instance/config/smtp-tests" />
<RouteHeader method="POST" path="/v1/admin/instance/config/smtp-tests" auditReason />
Opens a connection to the supplied SMTP server, authenticates against it, and reports the outcome as an [SMTP test result](#smtp-test-result-object) object without storing anything. Requires `instance:config:update`, or a session credential until setup is marked complete.
@@ -653,13 +659,15 @@ A test that runs to completion answers 200 whether or not the server accepted th
No configuration is written and the supplied credentials are discarded when the request completes. The connection, greeting, and socket each have a ten-second deadline.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `test_smtp_connection`, target type `instance_config`, target ID `0`, and metadata keys `port`, `secure`, and `ok`. The host, username, password, and failure text are not recorded.
### Rate limit
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
## Create registration URL
<RouteHeader method="POST" path="/v1/admin/instance/registration-urls" />
<RouteHeader method="POST" path="/v1/admin/instance/registration-urls" auditReason />
Issues a registration URL an Admin can hand out while the registration mode is `closed` or `approval`, and returns a [registration URL creation](#registration-url-creation-object) object. Requires `instance:config:update`.
@@ -686,7 +694,9 @@ The returned `code` is the same value as `registration_url.id`, and every [Get i
### Side effects
A newly issued URL has a use count of zero, no revocation time, and the acting Admin as its issuer. It is the first entry of `registration.urls` on the next configuration read. The operation records no Admin audit entry and changes no account.
A newly issued URL has a use count of zero, no revocation time, and the acting Admin as its issuer. It is the first entry of `registration.urls` on the next configuration read. The operation changes no account.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `create_registration_url`, target type `registration_url`, target ID `0`, and metadata key `approval_required`, plus `max_uses` when the body sets it. The label is not recorded. The entry has no registration URL identifier, because the identifier is the bearer code.
### Rate limit
@@ -694,7 +704,7 @@ A newly issued URL has a use count of zero, no revocation time, and the acting A
## Revoke registration URL
<RouteHeader method="DELETE" path="/v1/admin/instance/registration-urls/{registration_url_id}" />
<RouteHeader method="DELETE" path="/v1/admin/instance/registration-urls/{registration_url_id}" auditReason />
Revokes one registration URL so it can no longer be redeemed and returns the resulting [instance configuration](#instance-configuration-object) object. Requires `instance:config:update`.
@@ -718,7 +728,9 @@ An account that registered through the URL keeps its access, membership, and dec
### Side effects
The URL records its revocation time and stays in the configuration response afterwards. The operation records no Admin audit entry and changes no account.
The URL records its revocation time and stays in the configuration response afterwards. The operation changes no account.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `revoke_registration_url`, target type `registration_url`, target ID `0`, and no metadata, including for an identifier that names no registration URL. The entry has no registration URL identifier, because the identifier is the bearer code.
### Rate limit
@@ -748,7 +760,7 @@ Approves or rejects one pending registration and returns the resulting [instance
| --- | --- | --- |
| 200<sup>1</sup> | [instance configuration](#instance-configuration-object) object | The decision was applied |
<sup>1</sup> An account that no longer exists still answers 200. The pending registration is removed, no trait is written, and no audit entry is recorded
<sup>1</sup> An account that no longer exists still answers 200. The pending registration is removed and no trait is written
:::caution[Decisions apply even without a pending entry]
Both decisions remove the pending registration, but the endpoint does not require the account to be in that list. Rejecting writes the `registration_rejected` trait. A later approval can clear it again. The trait can also be changed through [Set user traits](/admin-api/users/#set-user-traits).
@@ -760,7 +772,7 @@ Approval removes both the `registration_pending_approval` trait and the `registr
When the stored pending registration list fails validation, the request fails before Fluxer changes the account. A failure after Fluxer writes the account traits leaves those traits written and can leave the entry in the pending list. Check the account traits and the pending list before retrying.
One Admin audit entry with the action `approve_registration` or `reject_registration` targets the account, records the audit reason, and has no metadata.
One Admin audit entry with the action `approve_registration` or `reject_registration` targets the account and records the audit reason. It has no metadata, except for an account that no longer exists, whose entry has the metadata key `account_found` set to `false`.
### Rate limit
@@ -768,7 +780,7 @@ One Admin audit entry with the action `approve_registration` or `reject_registra
## Get limit configuration
<RouteHeader method="GET" path="/v1/admin/limit-config" />
<RouteHeader method="GET" path="/v1/admin/limit-config" auditReason />
Returns the [limit configuration response](#limit-configuration-response-object) object. Requires `instance:limit_config:view`.
@@ -782,13 +794,15 @@ Returns the [limit configuration response](#limit-configuration-response-object)
The response reflects the configuration in force on the node that serves the request, so two nodes can report different values while a change propagates.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_limit_config`, target type `limit_config`, target ID `0`, and metadata key `rule_count`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Replace limit configuration
<RouteHeader method="PUT" path="/v1/admin/limit-config" />
<RouteHeader method="PUT" path="/v1/admin/limit-config" auditReason />
Replaces the stored limit configuration with the supplied document and returns the resulting [limit configuration response](#limit-configuration-response-object) object. Requires `instance:limit_config:update`.
@@ -821,7 +835,7 @@ On a self-hosted deployment whose premium mode is `everyone`, Fluxer drops the `
Each rule's `modifiedFields` is then recomputed and the merged document replaces the stored one. Every API node adopts the new configuration. Clients observe it through the published [limit configuration](/http-api/instance/#limit-configuration-object).
The operation records no Admin audit entry.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `update_limit_config`, target type `limit_config`, target ID `0`, and metadata keys `rule_count`, `changed_rule_count`, and `trait_definition_count`, each taken from the stored document. `changed_rule_count` is the number of rule identifiers added, removed, or stored with different filters or limits, compared with the configuration the serving node held before the write.
### Rate limit
@@ -829,7 +843,7 @@ The operation records no Admin audit entry.
## Create heap snapshot
<RouteHeader method="POST" path="/v1/admin/system/heap-snapshots" />
<RouteHeader method="POST" path="/v1/admin/system/heap-snapshots" auditReason />
Writes a V8 heap snapshot of the API process that serves the request and returns the file. Requires `system:heap_snapshot`.
@@ -855,7 +869,7 @@ A deployment behind a load balancer needs several attempts to reach a particular
### Side effects
No configuration is changed and no Admin audit entry is recorded.
No configuration is changed. The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `create_heap_snapshot`, target type `system`, target ID `0`, and metadata key `size_bytes`, after the snapshot is written and before the file is sent. A snapshot that fails to write records no entry.
### Rate limit
@@ -8,7 +8,7 @@ import RouteHeader from '@/components/RouteHeader.astro';
These routes list recorded background jobs and request cancellation. Create jobs through [Queue bulk job](/admin-api/bulk-jobs/#queue-bulk-job), [Create system DM broadcast](/admin-api/system-dms/#create-system-dm-broadcast), [Refresh search index](/admin-api/search-indexes/#refresh-search-index), or an [archive](/admin-api/archives/) request.
Reads require `jobs:view` and cancellation requires `jobs:cancel`. Every operation on this page shares the `admin:jobs:view` bucket.
Reads require `jobs:view` and cancellation requires `jobs:cancel`. Every operation on this page shares the `admin:jobs:view` bucket. The three reads record no [Admin audit entry](/admin-api/#admin-audit-entry-object), and cancellation records one.
:::note[Job updates vary by task]
Mention processing, link previews, and every scheduled task except `syncDisposableEmailDomains`, `syncUrlBlocklists` and `syncFileShaBlocklists` run with no job record and never appear here. A job also runs with no record when Fluxer fails to write that record. When a later write of status, progress or attempts fails, Fluxer logs the failure and the job continues, so the stored values can be behind the real run. Use the [archive routes](/admin-api/archives/) to track archive progress and failures.
@@ -160,6 +160,10 @@ Supply all three cursor parameters together or omit all three. An incomplete or
| 200 | response body | The job page was returned |
| 400 | [error response](/admin-api/#error-response) | `INVALID_FORM_BODY` because a query parameter is invalid |
### Side effects
The operation records no [Admin audit entry](/admin-api/#admin-audit-entry-object).
### Rate limit
600 requests per minute for each authenticated user, on the `admin:jobs:view` bucket.
@@ -184,6 +188,10 @@ This operation has no filters or pagination. The stored status and progress can
| --- | --- | --- |
| 200 | response body | The active jobs were returned |
### Side effects
The operation records no [Admin audit entry](/admin-api/#admin-audit-entry-object).
### Rate limit
600 requests per minute for each authenticated user, on the `admin:jobs:view` bucket.
@@ -215,13 +223,17 @@ Returns one [Admin job](#admin-job-object) object. Requires `jobs:view`.
A 404 response uses this single-field body rather than the standard error response. A missing or expired record does not mean the work never ran.
### Side effects
The operation records no [Admin audit entry](/admin-api/#admin-audit-entry-object).
### Rate limit
600 requests per minute for each authenticated user, on the `admin:jobs:view` bucket.
## Cancel job
<RouteHeader method="PUT" path="/v1/admin/jobs/{job_id}/cancellation" />
<RouteHeader method="PUT" path="/v1/admin/jobs/{job_id}/cancellation" auditReason />
Requests cooperative cancellation of a job and reports whether the request was recorded. Requires `jobs:cancel`.
@@ -249,6 +261,8 @@ Returns false for a missing or terminal job. Repeating a request for a queued or
Sets `cancel_requested` to true for a queued or running job. `sendSystemDm`, `syncDisposableEmailDomains`, `bulkBanFileShas`, `bulkDeleteMessagesForUsers`, and the tasks queued by [Bulk jobs](/admin-api/bulk-jobs/) check for the request while they run and stop at the next check. Other tasks run to the end. Completed work is not undone. A status of `cancelled` means the task stopped because of the request.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `cancel_job`, target type `bulk_job`, target ID equal to `job_id`, and metadata key `cancelled`. The entry uses target type `bulk_job` for every task type, and Fluxer records it when `cancelled` is false too.
### Rate limit
600 requests per minute for each authenticated user, on the `admin:jobs:view` bucket.
@@ -139,7 +139,7 @@ A job identifier that never existed reports `not_found` too, so the two are indi
## List channel messages
<RouteHeader method="GET" path="/v1/admin/channels/{channel_id}/messages" />
<RouteHeader method="GET" path="/v1/admin/channels/{channel_id}/messages" auditReason />
Returns one page of [Admin message](#admin-message-object) objects for one channel. Requires `message:lookup`.
@@ -187,13 +187,17 @@ The response has no cursor member. Page further by sending the ID of the oldest
A channel that does not exist returns an empty `messages` array with `has_more` false, so this operation never answers 404.
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `list_channel_messages`, the target type `channel`, and the channel ID as the target. Its metadata keys are `limit`, `result_count` for the messages in this page, and `has_more`. A supplied `before` or `after` cursor is added under the same key.
### Rate limit
100 requests per minute for each authenticated user, on the shared `admin:message:operation` bucket.
## Get message
<RouteHeader method="GET" path="/v1/admin/channels/{channel_id}/messages/{message_id}" />
<RouteHeader method="GET" path="/v1/admin/channels/{channel_id}/messages/{message_id}" auditReason />
Returns the requested message together with the messages surrounding it, as one array of [Admin message](#admin-message-object) objects, oldest first. Requires `message:lookup`.
@@ -232,6 +236,10 @@ Returns the requested message together with the messages surrounding it, as one
An unknown message returns 200 with the window surrounding the position the missing ID would have occupied, so this operation never returns `UNKNOWN_MESSAGE`. An unknown channel returns 200 with an empty `messages` array.
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `get_message`, the target type `message`, and the message ID as the target. Its metadata keys are `channel_id`, `context_limit`, `result_count` for the messages returned, and `found`, which is `true` when the requested message is in `messages` and `false` otherwise.
### Rate limit
100 requests per minute for each authenticated user, on the shared `admin:message:operation` bucket.
@@ -281,7 +289,7 @@ One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with t
## Search messages
<RouteHeader method="GET" path="/v1/admin/messages" />
<RouteHeader method="GET" path="/v1/admin/messages" auditReason />
Searches the message index of one channel, or resolves one message in that channel by message ID or by attachment identity. Requires `message:lookup`.
@@ -345,13 +353,23 @@ With no message search service configured the search mode returns an empty `mess
A hit whose message no longer exists is dropped from the page and excluded from `total`.
:::
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `search_messages`. Its metadata key `mode` is `message` for a `message_id` lookup, `attachment` for an attachment lookup, and `search` otherwise, and `result_count` is the number of messages returned.
- A `message_id` lookup has the target type `message` and the message ID as the target. It adds `channel_id`, `context_limit`, and `found`, which is `true` when the message is in `messages`.
- An attachment lookup has the target type `channel` and the channel ID as the target. It adds `attachment_id`, `context_limit`, and `found`, which is `true` when the attachment resolved. A resolved attachment also adds `message_id`.
- A search has the target type `channel` and the channel ID as the target. It adds `limit` and `total`, and `has_query` as `true` when `q` is supplied.
The query text and the filename are not recorded. A 400 records nothing.
### Rate limit
100 requests per minute for each authenticated user, on the shared `admin:message:operation` bucket.
## Get message shred job
<RouteHeader method="GET" path="/v1/admin/messages/shreds/{job_id}" />
<RouteHeader method="GET" path="/v1/admin/messages/shreds/{job_id}" auditReason />
Returns one [message shred status](#message-shred-status-object) object. Requires `message:shred`.
@@ -371,6 +389,10 @@ Shred jobs are created by [Shred user messages](/admin-api/users/#shred-user-mes
| --- | --- | --- |
| 200 | [message shred status](#message-shred-status-object) object | Job state was returned, including `not_found` for an unknown or expired job |
### Side effects
One [Admin audit entry](/admin-api/#admin-audit-entry-object) is recorded with the action `get_message_shred_status`, the target type `message_shred`, and the target ID `0`. Its metadata keys are `job_id` and `status`, which is the returned job state.
### Rate limit
100 requests per minute for each authenticated user, on the shared `admin:message:operation` bucket.
@@ -254,7 +254,7 @@ A report resolution is the reduced view [Update report](#update-report) returns.
## List reports
<RouteHeader method="GET" path="/v1/admin/reports" />
<RouteHeader method="GET" path="/v1/admin/reports" auditReason />
Returns a page of [Admin report](#admin-report-object) objects without message context. Requires `report:view`.
@@ -313,13 +313,17 @@ An instance with no search backend returns 403 `FEATURE_TEMPORARILY_DISABLED` on
| 200 | response body | Report page was returned |
| 403 | [error response](/admin-api/#error-response) | No search backend is configured for the instance and the request returns `FEATURE_TEMPORARILY_DISABLED` |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `search_reports`, target type `report`, and target ID `0` on both branches. The metadata keys are `limit`, `offset`, `result_count` for the reports in this page, and `total`. The search branch adds `sort_by` and `sort_order`. A supplied `q` adds `has_query` with the value `true`, and the query text is not recorded. The supplied filters add `status`, `report_type`, `reported_user_id`, `reported_guild_id`, and `reported_channel_id` under their own names, `reporter_id` as `reporter_user_id`, `guild_context_id` as `context_guild_id`, and `resolved_by_admin_id` as `resolved_by_admin_user_id`. `category` is not recorded. A 403 records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get report
<RouteHeader method="GET" path="/v1/admin/reports/{report_id}" />
<RouteHeader method="GET" path="/v1/admin/reports/{report_id}" auditReason />
Returns one [Admin report detail](#admin-report-detail-object) object, which adds the captured message context and the live state of those messages. Requires `report:view`.
@@ -338,6 +342,10 @@ The operation answers even when the report search index is unavailable. Reporter
| 200 | [Admin report detail](#admin-report-detail-object) object | Report was returned |
| 404 | [error response](/admin-api/#error-response) | Report does not exist and the request returns `UNKNOWN_REPORT` |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_report`, target type `report`, the report ID as target ID, and metadata keys `report_type` and `status` with the integer values of the report. The attachment download URLs are not recorded. A 404 records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -183,6 +183,10 @@ Returns the [search index refresh progress](#search-index-refresh-progress-objec
Each write of the progress record renews a one-hour lifetime, so a completed or failed outcome stays readable for one hour after its final update.
:::
### Side effects
The operation records no [Admin audit entry](/admin-api/#admin-audit-entry-object).
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -10,7 +10,7 @@ These routes read and edit any account on the instance. An account has more fiel
Each mutable field group has its own route, its own [ACL](/admin-api/#acl-registry), and its own [audit action](/admin-api/#audit-actions), so there is no combined update.
A read records no audit entry and reads no audit reason. [List user sessions](#list-user-sessions) and [List user WebAuthn credentials](#list-user-webauthn-credentials) are the exceptions. `POST /v1/admin/users/{user_id}/avatar-block` addresses a user path but belongs to [Blocklists](/admin-api/blocklists/#block-a-users-current-avatar).
Every operation on this page records an [Admin audit entry](/admin-api/#admin-audit-entry-object), reads included. [Get current Admin user](#get-current-admin-user) is the exception and records nothing. `POST /v1/admin/users/{user_id}/avatar-block` addresses a user path but belongs to [Blocklists](/admin-api/blocklists/#block-a-users-current-avatar).
:::caution[A missing PII ACL nulls the protected fields]
A caller without `user:view:email` receives a null `email`. A caller without `user:view:dob` receives a null `date_of_birth`, and a caller without `user:view:ip` receives null `last_active_ip`, `last_active_ip_reverse`, and `last_active_location`. The read still succeeds, so a null field is no evidence that the account has none.
@@ -449,7 +449,7 @@ One recorded change to the account's email address, phone verification state, or
## List users
<RouteHeader method="GET" path="/v1/admin/users" />
<RouteHeader method="GET" path="/v1/admin/users" auditReason />
Lists and searches accounts. Requires `user:lookup`, and `user:view:email` or `user:view:ip` for the selectors marked below. The response has an array of [Admin user](#admin-user-object) objects and a total.
@@ -502,6 +502,12 @@ A `q` value that is entirely digits also resolves that exact account ID and plac
<sup>1</sup> A selector that matches nothing returns an empty `users` array and a `total` of zero
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `search_users`, target type `user`, and target ID `0`. The metadata keys are `selector`, `result_count` for the accounts in `users`, and `total`. `selector` is the honoured selector, one of `user_id`, `resolve`, `email`, `last_active_ip`, or `search` for the indexed search.
The `user_id` selector adds `user_id_count` for the number of IDs supplied, and `user_id` when exactly one ID was supplied. The `last_active_ip` and `search` selectors add `limit` and `offset`. The indexed search adds `has_query` as `true` when `q` is not blank. The `resolve`, `email`, `last_active_ip`, and `q` values are not recorded. A failed request records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -527,13 +533,17 @@ Every Admin credential holds `admin:authenticate`, so every account that can rea
| 200 | response body | Current Admin was returned |
| 403 | [error response](/admin-api/#error-response) | Credential type is refused, or the account does not hold `admin:authenticate` |
### Side effects
The operation records no [Admin audit entry](/admin-api/#admin-audit-entry-object).
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get user
<RouteHeader method="GET" path="/v1/admin/users/{user_id}" />
<RouteHeader method="GET" path="/v1/admin/users/{user_id}" auditReason />
Returns one account as a single-element array. Requires `user:lookup`.
@@ -561,6 +571,10 @@ Returns one account as a single-element array. Requires `user:lookup`.
An account with a pending or completed deletion is still returned, with its lifecycle state visible through `pending_deletion_at`, `deletion_reason_code`, and `deletion_public_reason`.
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_user`, target type `user`, the path `user_id` as target ID, and a metadata key `found`. `found` is `true` when `users` has the account and `false` when it is empty.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -1516,7 +1530,7 @@ Fluxer emails the account holder when the account has an email address. The emai
## List user guilds
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/guilds" />
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/guilds" auditReason />
Lists the guilds the account is a member of, in ascending guild ID order. Requires `user:list:guilds`. Both cursors apply to the account's own sorted membership list.
@@ -1556,13 +1570,17 @@ Lists the guilds the account is a member of, in ascending guild ID order. Requir
The response has no paging state. Derive the next cursor from the last returned guild ID, and treat a short page as the end of the list.
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_user_guilds`, target type `user`, the path `user_id` as target ID, and the metadata keys `limit`, `with_counts`, and `guild_count` for the guilds returned. A supplied cursor adds `before_guild_id` or `after_guild_id`. A failed request records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## List user direct message channels
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/dm-channels" />
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/dm-channels" auditReason />
Lists the direct message channels the account has taken part in. Requires `user:list:dm_channels`. The read does not open, reopen, or acknowledge a channel for the target account.
@@ -1606,13 +1624,17 @@ The group branch ignores `before`, `after`, and `limit`, and returns every group
The response has no paging state. Derive the next cursor from the last returned channel ID.
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_user_dm_channels`, target type `user`, the path `user_id` as target ID, and the metadata keys `type` and `channel_count` for the channels returned. A `dm` request also records `limit`, and `before_channel_id` or `after_channel_id` when a cursor is supplied. A 404 records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## List user applications
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/applications" />
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/applications" auditReason />
Lists the OAuth2 applications the account owns. Requires `application:lookup` or `application:list:by_owner`, the same pair [List applications](/admin-api/applications/#list-applications) accepts.
@@ -1637,13 +1659,17 @@ Lists the OAuth2 applications the account owns. Requires `application:lookup` or
| 200 | response body | The applications were returned |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_USER`, because the account does not exist |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_user_applications`, target type `user`, the path `user_id` as target ID, and a metadata key `application_count`. A 404 records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## List user relationships
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/relationships" />
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/relationships" auditReason />
Lists the friends, friend requests, and blocked accounts of the account, split into one array for each [relationship category](#relationship-categories). Requires `user:list:relationships`.
@@ -1673,6 +1699,10 @@ The operation accepts no cursor, category filter, or limit, and returns every ca
| 200 | response body | The relationships were returned |
| 404 | [error response](/admin-api/#error-response) | `UNKNOWN_USER`, because the account does not exist |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_user_relationships`, target type `user`, the path `user_id` as target ID, and the metadata keys `friend_count`, `incoming_request_count`, `outgoing_request_count`, and `blocked_count`. A 404 records nothing.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -1778,8 +1808,8 @@ The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-obje
Lists the account's active and terminated authentication sessions. Requires `user:list:sessions`. IP, reverse DNS and location fields also require `user:view:ip`.
:::caution[Listing sessions writes an audit entry]
The `X-Audit-Log-Reason` header value is stored on the recorded entry.
:::caution[Listing sessions records an audit entry]
The entry stores the `X-Audit-Log-Reason` value whether or not the caller holds `user:view:ip`, so a listing with redacted addresses is recorded too.
:::
### Path parameters
@@ -1867,8 +1897,6 @@ The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-obje
Lists the passkeys and security keys registered on the account, as a bare JSON array. Requires `user:update:mfa`, the same ACL as [Disable user MFA](#disable-user-mfa).
Like [List user sessions](#list-user-sessions), this read records an audit entry and stores the `X-Audit-Log-Reason` value on it.
### Path parameters
| Field | Type | Description |
@@ -2120,7 +2148,7 @@ No [User Update](/gateway/events/#user-update) is emitted, unlike every other mu
## Create user archive
<RouteHeader method="POST" path="/v1/admin/users/{user_id}/archives" />
<RouteHeader method="POST" path="/v1/admin/users/{user_id}/archives" auditReason />
Creates an asynchronous archive of the account's data and returns the created [archive](/admin-api/archives/#archive-object) object. Requires `archive:trigger:user`.
@@ -2155,13 +2183,15 @@ Fluxer creates the archive record and queues a build job, so the response return
No Gateway Dispatch is emitted.
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `trigger_user_archive`, target type `user`, the path `user_id` as target ID, and the metadata keys `archive_id` for the created archive and `include_attachments` as `true` or `false`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## List user change log
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/change-log" />
<RouteHeader method="GET" path="/v1/admin/users/{user_id}/change-log" auditReason />
Lists the identity and contact field changes recorded for the account, newest first. Requires `user:view:contact_log`. Email values also require `user:view:email`.
@@ -2201,6 +2231,10 @@ Lists the identity and contact field changes recorded for the account, newest fi
Without `user:view:email`, an `email` entry is still returned with both value fields replaced by the literal string `[redacted]`, so the fact and the time of the change stay visible while the addresses do not.
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_user_change_log`, target type `user`, the path `user_id` as target ID, and the metadata keys `limit` and `entry_count` for the entries returned. A supplied `page_token` adds `has_page_token` as `true`, and the cursor value is not recorded.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -167,7 +167,7 @@ The limit is read when a channel is first placed and the server is pinned for it
## List voice regions
<RouteHeader method="GET" path="/v1/admin/voice/regions" />
<RouteHeader method="GET" path="/v1/admin/voice/regions" auditReason />
Returns every [Admin voice region](#admin-voice-region-object) object in ascending display name order. Requires `voice:region:list`.
@@ -191,13 +191,17 @@ Returns every [Admin voice region](#admin-voice-region-object) object in ascendi
| --- | --- | --- |
| 200 | response body | The regions were returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_voice_regions`, target type `voice_region`, target ID `0`, and metadata keys `include_servers` and `result_count`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get voice region
<RouteHeader method="GET" path="/v1/admin/voice/regions/{region_id}" />
<RouteHeader method="GET" path="/v1/admin/voice/regions/{region_id}" auditReason />
Returns one [Admin voice region](#admin-voice-region-object) object. Requires `voice:region:list`.
@@ -231,6 +235,10 @@ Returns one [Admin voice region](#admin-voice-region-object) object. Requires `v
| --- | --- | --- |
| 200 | response body | A lookup was done, whether or not it resolved |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_voice_region`, target type `voice_region`, target ID `0`, and metadata keys `region_id`, `include_servers`, and `found`. An unknown region records `found` as `false`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
@@ -380,7 +388,7 @@ One [Admin audit entry](/admin-api/#admin-audit-entry-object) with the action `d
## List voice servers
<RouteHeader method="GET" path="/v1/admin/voice/regions/{region_id}/servers" />
<RouteHeader method="GET" path="/v1/admin/voice/regions/{region_id}/servers" auditReason />
Returns every [Admin voice server](#admin-voice-server-object) object registered in one region. Requires `voice:server:list`.
@@ -404,13 +412,17 @@ Returns every [Admin voice server](#admin-voice-server-object) object registered
| --- | --- | --- |
| 200 | response body | The servers were returned |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `list_voice_servers`, target type `voice_server`, target ID `0`, and metadata keys `region_id` and `result_count`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
## Get voice server
<RouteHeader method="GET" path="/v1/admin/voice/regions/{region_id}/servers/{server_id}" />
<RouteHeader method="GET" path="/v1/admin/voice/regions/{region_id}/servers/{server_id}" auditReason />
Returns one [Admin voice server](#admin-voice-server-object) object. Requires `voice:server:list`.
@@ -435,6 +447,10 @@ Returns one [Admin voice server](#admin-voice-server-object) object. Requires `v
| --- | --- | --- |
| 200 | response body | A lookup was done, whether or not it resolved |
### Side effects
The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `get_voice_server`, target type `voice_server`, target ID `0`, and metadata keys `region_id`, `server_id`, and `found`. A pair that resolves to no server records `found` as `false`.
### Rate limit
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.