diff --git a/docs/administration/admin_api/README.md b/docs/administration/admin_api/README.md
new file mode 100644
index 0000000000..f11e0b19a6
--- /dev/null
+++ b/docs/administration/admin_api/README.md
@@ -0,0 +1,47 @@
+# The Admin API
+
+## Authenticate as a server admin
+
+Many of the API calls in the admin api will require an `access_token` for a
+server admin. (Note that a server admin is distinct from a room admin.)
+
+An existing user can be marked as a server admin by updating the database directly.
+
+Check your [database settings](config_documentation.md#database) in the configuration file, connect to the correct database using either `psql [database name]` (if using PostgreSQL) or `sqlite3 path/to/your/database.db` (if using SQLite) and elevate the user `@foo:bar.com` to administrator.
+```sql
+UPDATE users SET admin = 1 WHERE name = '@foo:bar.com';
+```
+
+A new server admin user can also be created using the `register_new_matrix_user`
+command. This is a script that is distributed as part of synapse. It is possibly
+already on your `$PATH` depending on how Synapse was installed.
+
+Finding your user's `access_token` is client-dependent, but will usually be shown in the client's settings.
+
+## Making an Admin API request
+For security reasons, we [recommend](reverse_proxy.md#synapse-administration-endpoints)
+that the Admin API (`/_synapse/admin/...`) should be hidden from public view using a
+reverse proxy. This means you should typically query the Admin API from a terminal on
+the machine which runs Synapse.
+
+Once you have your `access_token`, you will need to authenticate each request to an Admin API endpoint by
+providing the token as either a query parameter or a request header. To add it as a request header in cURL:
+
+```sh
+curl --header "Authorization: Bearer <access_token>" <the_rest_of_your_API_request>
+```
+
+For example, suppose we want to
+[query the account](user_admin_api.md#query-user-account) of the user
+`@foo:bar.com`. We need an admin access token (e.g.
+`syt_AjfVef2_L33JNpafeif_0feKJfeaf0CQpoZk`), and we need to know which port
+Synapse's [`client` listener](config_documentation.md#listeners) is listening
+on (e.g. `8008`). Then we can use the following command to request the account
+information from the Admin API.
+
+```sh
+curl --header "Authorization: Bearer syt_AjfVef2_L33JNpafeif_0feKJfeaf0CQpoZk" -X GET http://127.0.0.1:8008/_synapse/admin/v2/users/@foo:bar.com
+```
+
+For more details on access tokens in Matrix, please refer to the complete
+[matrix spec documentation](https://matrix.org/docs/spec/client_server/r0.6.1#using-access-tokens).
diff --git a/docs/administration/admin_api/account_validity.md b/docs/administration/admin_api/account_validity.md
new file mode 100644
index 0000000000..d878bf7451
--- /dev/null
+++ b/docs/administration/admin_api/account_validity.md
@@ -0,0 +1,45 @@
+# Account validity API
+
+This API allows a server administrator to manage the validity of an account. To
+use it, you must enable the account validity feature (under
+`account_validity`) in Synapse's configuration.
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+## Renew account
+
+This API extends the validity of an account by as much time as configured in the
+`period` parameter from the `account_validity` configuration.
+
+The API is:
+
+```
+POST /_synapse/admin/v1/account_validity/validity
+```
+
+with the following body:
+
+```json
+{
+ "user_id": "<user ID for the account to renew>",
+ "expiration_ts": 0,
+ "enable_renewal_emails": true
+}
+```
+
+
+`expiration_ts` is an optional parameter and overrides the expiration date,
+which otherwise defaults to now + validity period.
+
+`enable_renewal_emails` is also an optional parameter and enables/disables
+sending renewal emails to the user. Defaults to true.
+
+The API returns with the new expiration date for this account, as a timestamp in
+milliseconds since epoch:
+
+```json
+{
+ "expiration_ts": 0
+}
+```
diff --git a/docs/administration/admin_api/background_updates.md b/docs/administration/admin_api/background_updates.md
new file mode 100644
index 0000000000..3bfd254ffc
--- /dev/null
+++ b/docs/administration/admin_api/background_updates.md
@@ -0,0 +1,109 @@
+# Background Updates API
+
+This API allows a server administrator to manage the background updates being
+run against the database.
+
+## Status
+
+This API gets the current status of the background updates.
+
+
+The API is:
+
+```
+GET /_synapse/admin/v1/background_updates/status
+```
+
+Returning:
+
+```json
+{
+ "enabled": true,
+ "current_updates": {
+ "<db_name>": {
+ "name": "<background_update_name>",
+ "total_item_count": 50,
+ "total_duration_ms": 10000.0,
+ "average_items_per_ms": 2.2,
+ },
+ }
+}
+```
+
+`enabled` whether the background updates are enabled or disabled.
+
+`db_name` the database name (usually Synapse is configured with a single database named 'master').
+
+For each update:
+
+`name` the name of the update.
+`total_item_count` total number of "items" processed (the meaning of 'items' depends on the update in question).
+`total_duration_ms` how long the background process has been running, not including time spent sleeping.
+`average_items_per_ms` how many items are processed per millisecond based on an exponential average.
+
+
+## Enabled
+
+This API allow pausing background updates.
+
+Background updates should *not* be paused for significant periods of time, as
+this can affect the performance of Synapse.
+
+*Note*: This won't persist over restarts.
+
+*Note*: This won't cancel any update query that is currently running. This is
+usually fine since most queries are short lived, except for `CREATE INDEX`
+background updates which won't be cancelled once started.
+
+
+The API is:
+
+```
+POST /_synapse/admin/v1/background_updates/enabled
+```
+
+with the following body:
+
+```json
+{
+ "enabled": false
+}
+```
+
+`enabled` sets whether the background updates are enabled or disabled.
+
+The API returns the `enabled` param.
+
+```json
+{
+ "enabled": false
+}
+```
+
+There is also a `GET` version which returns the `enabled` state.
+
+
+## Run
+
+This API schedules a specific background update to run. The job starts immediately after calling the API.
+
+
+The API is:
+
+```
+POST /_synapse/admin/v1/background_updates/start_job
+```
+
+with the following body:
+
+```json
+{
+ "job_name": "populate_stats_process_rooms"
+}
+```
+
+The following JSON body parameters are available:
+
+- `job_name` - A string which job to run. Valid values are:
+ - `populate_stats_process_rooms` - Recalculate the stats for all rooms.
+ - `regenerate_directory` - Recalculate the [user directory](../../usage/configuration/user_directory.md) if it is stale or out of sync.
diff --git a/docs/administration/admin_api/event_reports.md b/docs/administration/admin_api/event_reports.md
new file mode 100644
index 0000000000..be6f0961bf
--- /dev/null
+++ b/docs/administration/admin_api/event_reports.md
@@ -0,0 +1,171 @@
+# Show reported events
+
+This API returns information about reported events.
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+The api is:
+```
+GET /_synapse/admin/v1/event_reports?from=0&limit=10
+```
+
+It returns a JSON body like the following:
+
+```json
+{
+ "event_reports": [
+ {
+ "event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
+ "id": 2,
+ "reason": "foo",
+ "score": -100,
+ "received_ts": 1570897107409,
+ "canonical_alias": "#alias1:matrix.org",
+ "room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
+ "name": "Matrix HQ",
+ "sender": "@foobar:matrix.org",
+ "user_id": "@foo:matrix.org"
+ },
+ {
+ "event_id": "$3IcdZsDaN_En-S1DF4EMCy3v4gNRKeOJs8W5qTOKj4I",
+ "id": 3,
+ "reason": "bar",
+ "score": -100,
+ "received_ts": 1598889612059,
+ "canonical_alias": "#alias2:matrix.org",
+ "room_id": "!eGvUQuTCkHGVwNMOjv:matrix.org",
+ "name": "Your room name here",
+ "sender": "@foobar:matrix.org",
+ "user_id": "@bar:matrix.org"
+ }
+ ],
+ "next_token": 2,
+ "total": 4
+}
+```
+
+To paginate, check for `next_token` and if present, call the endpoint again with `from`
+set to the value of `next_token`. This will return a new page.
+
+If the endpoint does not return a `next_token` then there are no more reports to
+paginate through.
+
+**URL parameters:**
+
+* `limit`: integer - Is optional but is used for pagination, denoting the maximum number
+ of items to return in this call. Defaults to `100`.
+* `from`: integer - Is optional but used for pagination, denoting the offset in the
+ returned results. This should be treated as an opaque value and not explicitly set to
+ anything other than the return value of `next_token` from a previous call. Defaults to `0`.
+* `dir`: string - Direction of event report order. Whether to fetch the most recent
+ first (`b`) or the oldest first (`f`). Defaults to `b`.
+* `user_id`: string - Is optional and filters to only return users with user IDs that
+ contain this value. This is the user who reported the event and wrote the reason.
+* `room_id`: string - Is optional and filters to only return rooms with room IDs that
+ contain this value.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+* `id`: integer - ID of event report.
+* `received_ts`: integer - The timestamp (in milliseconds since the unix epoch) when this
+ report was sent.
+* `room_id`: string - The ID of the room in which the event being reported is located.
+* `name`: string - The name of the room.
+* `event_id`: string - The ID of the reported event.
+* `user_id`: string - This is the user who reported the event and wrote the reason.
+* `reason`: string - Comment made by the `user_id` in this report. May be blank or `null`.
+* `score`: integer - Content is reported based upon a negative score, where -100 is
+ "most offensive" and 0 is "inoffensive". May be `null`.
+* `sender`: string - This is the ID of the user who sent the original message/event that
+ was reported.
+* `canonical_alias`: string - The canonical alias of the room. `null` if the room does not
+ have a canonical alias set.
+* `next_token`: integer - Indication for pagination. See above.
+* `total`: integer - Total number of event reports related to the query
+ (`user_id` and `room_id`).
+
+# Show details of a specific event report
+
+This API returns information about a specific event report.
+
+The api is:
+```
+GET /_synapse/admin/v1/event_reports/<report_id>
+```
+
+It returns a JSON body like the following:
+
+```json
+{
+ "event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
+ "event_json": {
+ "auth_events": [
+ "$YK4arsKKcc0LRoe700pS8DSjOvUT4NDv0HfInlMFw2M",
+ "$oggsNXxzPFRE3y53SUNd7nsj69-QzKv03a1RucHu-ws"
+ ],
+ "content": {
+ "body": "matrix.org: This Week in Matrix",
+ "format": "org.matrix.custom.html",
+ "formatted_body": "<strong>matrix.org</strong>:<br><a href=\"https://matrix.org/blog/\"><strong>This Week in Matrix</strong></a>",
+ "msgtype": "m.notice"
+ },
+ "depth": 546,
+ "hashes": {
+ "sha256": "xK1//xnmvHJIOvbgXlkI8eEqdvoMmihVDJ9J4SNlsAw"
+ },
+ "origin": "matrix.org",
+ "origin_server_ts": 1592291711430,
+ "prev_events": [
+ "$YK4arsKKcc0LRoe700pS8DSjOvUT4NDv0HfInlMFw2M"
+ ],
+ "prev_state": [],
+ "room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
+ "sender": "@foobar:matrix.org",
+ "signatures": {
+ "matrix.org": {
+ "ed25519:a_JaEG": "cs+OUKW/iHx5pEidbWxh0UiNNHwe46Ai9LwNz+Ah16aWDNszVIe2gaAcVZfvNsBhakQTew51tlKmL2kspXk/Dg"
+ }
+ },
+ "type": "m.room.message",
+ "unsigned": {
+ "age_ts": 1592291711430
+ }
+ },
+ "id": <report_id>,
+ "reason": "foo",
+ "score": -100,
+ "received_ts": 1570897107409,
+ "canonical_alias": "#alias1:matrix.org",
+ "room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
+ "name": "Matrix HQ",
+ "sender": "@foobar:matrix.org",
+ "user_id": "@foo:matrix.org"
+}
+```
+
+**URL parameters:**
+
+* `report_id`: string - The ID of the event report.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+* `id`: integer - ID of event report.
+* `received_ts`: integer - The timestamp (in milliseconds since the unix epoch) when this
+ report was sent.
+* `room_id`: string - The ID of the room in which the event being reported is located.
+* `name`: string - The name of the room.
+* `event_id`: string - The ID of the reported event.
+* `user_id`: string - This is the user who reported the event and wrote the reason.
+* `reason`: string - Comment made by the `user_id` in this report. May be blank.
+* `score`: integer - Content is reported based upon a negative score, where -100 is
+ "most offensive" and 0 is "inoffensive".
+* `sender`: string - This is the ID of the user who sent the original message/event that
+ was reported.
+* `canonical_alias`: string - The canonical alias of the room. `null` if the room does not
+ have a canonical alias set.
+* `event_json`: object - Details of the original event that was reported.
diff --git a/docs/administration/admin_api/federation.md b/docs/administration/admin_api/federation.md
new file mode 100644
index 0000000000..60cbc5265e
--- /dev/null
+++ b/docs/administration/admin_api/federation.md
@@ -0,0 +1,212 @@
+# Federation API
+
+This API allows a server administrator to manage Synapse's federation with other homeservers.
+
+Note: This API is new, experimental and "subject to change".
+
+## List of destinations
+
+This API gets the current destination retry timing info for all remote servers.
+
+The list contains all the servers with which the server federates,
+regardless of whether an error occurred or not.
+If an error occurs, it may take up to 20 minutes for the error to be displayed here,
+as a complete retry must have failed.
+
+The API is:
+
+A standard request with no filtering:
+
+```
+GET /_synapse/admin/v1/federation/destinations
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "destinations":[
+ {
+ "destination": "matrix.org",
+ "retry_last_ts": 1557332397936,
+ "retry_interval": 3000000,
+ "failure_ts": 1557329397936,
+ "last_successful_stream_ordering": null
+ }
+ ],
+ "total": 1
+}
+```
+
+To paginate, check for `next_token` and if present, call the endpoint again
+with `from` set to the value of `next_token`. This will return a new page.
+
+If the endpoint does not return a `next_token` then there are no more destinations
+to paginate through.
+
+**Parameters**
+
+The following query parameters are available:
+
+- `from` - Offset in the returned list. Defaults to `0`.
+- `limit` - Maximum amount of destinations to return. Defaults to `100`.
+- `order_by` - The method in which to sort the returned list of destinations.
+ Valid values are:
+ - `destination` - Destinations are ordered alphabetically by remote server name.
+ This is the default.
+ - `retry_last_ts` - Destinations are ordered by time of last retry attempt in ms.
+ - `retry_interval` - Destinations are ordered by how long until next retry in ms.
+ - `failure_ts` - Destinations are ordered by when the server started failing in ms.
+ - `last_successful_stream_ordering` - Destinations are ordered by the stream ordering
+ of the most recent successfully-sent PDU.
+- `dir` - Direction of room order. Either `f` for forwards or `b` for backwards. Setting
+ this value to `b` will reverse the above sort order. Defaults to `f`.
+
+*Caution:* The database only has an index on the column `destination`.
+This means that if a different sort order is used,
+this can cause a large load on the database, especially for large environments.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `destinations` - An array of objects, each containing information about a destination.
+ Destination objects contain the following fields:
+ - `destination` - string - Name of the remote server to federate.
+ - `retry_last_ts` - integer - The last time Synapse tried and failed to reach the
+ remote server, in ms. This is `0` if the last attempt to communicate with the
+ remote server was successful.
+ - `retry_interval` - integer - How long since the last time Synapse tried to reach
+ the remote server before trying again, in ms. This is `0` if no further retrying occuring.
+ - `failure_ts` - nullable integer - The first time Synapse tried and failed to reach the
+ remote server, in ms. This is `null` if communication with the remote server has never failed.
+ - `last_successful_stream_ordering` - nullable integer - The stream ordering of the most
+ recent successfully-sent [PDU](understanding_synapse_through_grafana_graphs.md#federation)
+ to this destination, or `null` if this information has not been tracked yet.
+- `next_token`: string representing a positive integer - Indication for pagination. See above.
+- `total` - integer - Total number of destinations.
+
+## Destination Details API
+
+This API gets the retry timing info for a specific remote server.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/federation/destinations/<destination>
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "destination": "matrix.org",
+ "retry_last_ts": 1557332397936,
+ "retry_interval": 3000000,
+ "failure_ts": 1557329397936,
+ "last_successful_stream_ordering": null
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `destination` - Name of the remote server.
+
+**Response**
+
+The response fields are the same like in the `destinations` array in
+[List of destinations](#list-of-destinations) response.
+
+## Destination rooms
+
+This API gets the rooms that federate with a specific remote server.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/federation/destinations/<destination>/rooms
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "rooms":[
+ {
+ "room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
+ "stream_ordering": 8326
+ },
+ {
+ "room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
+ "stream_ordering": 93534
+ }
+ ],
+ "total": 2
+}
+```
+
+To paginate, check for `next_token` and if present, call the endpoint again
+with `from` set to the value of `next_token`. This will return a new page.
+
+If the endpoint does not return a `next_token` then there are no more destinations
+to paginate through.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `destination` - Name of the remote server.
+
+The following query parameters are available:
+
+- `from` - Offset in the returned list. Defaults to `0`.
+- `limit` - Maximum amount of destinations to return. Defaults to `100`.
+- `dir` - Direction of room order by `room_id`. Either `f` for forwards or `b` for
+ backwards. Defaults to `f`.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `rooms` - An array of objects, each containing information about a room.
+ Room objects contain the following fields:
+ - `room_id` - string - The ID of the room.
+ - `stream_ordering` - integer - The stream ordering of the most recent
+ successfully-sent [PDU](understanding_synapse_through_grafana_graphs.md#federation)
+ to this destination in this room.
+- `next_token`: string representing a positive integer - Indication for pagination. See above.
+- `total` - integer - Total number of destinations.
+
+## Reset connection timeout
+
+Synapse makes federation requests to other homeservers. If a federation request fails,
+Synapse will mark the destination homeserver as offline, preventing any future requests
+to that server for a "cooldown" period. This period grows over time if the server
+continues to fail its responses
+([exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)).
+
+Admins can cancel the cooldown period with this API.
+
+This API resets the retry timing for a specific remote server and tries to connect to
+the remote server again. It does not wait for the next `retry_interval`.
+The connection must have previously run into an error and `retry_last_ts`
+([Destination Details API](#destination-details-api)) must not be equal to `0`.
+
+The connection attempt is carried out in the background and can take a while
+even if the API already returns the http status 200.
+
+The API is:
+
+```
+POST /_synapse/admin/v1/federation/destinations/<destination>/reset_connection
+
+{}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `destination` - Name of the remote server.
diff --git a/docs/administration/admin_api/media_admin_api.md b/docs/administration/admin_api/media_admin_api.md
new file mode 100644
index 0000000000..d6bbee5349
--- /dev/null
+++ b/docs/administration/admin_api/media_admin_api.md
@@ -0,0 +1,308 @@
+# Querying media
+
+These APIs allow extracting media information from the homeserver.
+
+Details about the format of the `media_id` and storage of the media in the file system
+are documented under [media repository](../../development/internal_documentation/media_repository.md).
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+## List all media in a room
+
+This API gets a list of known media in a room.
+However, it only shows media from unencrypted events or rooms.
+
+The API is:
+```
+GET /_synapse/admin/v1/room/<room_id>/media
+```
+
+The API returns a JSON body like the following:
+```json
+{
+ "local": [
+ "mxc://localhost/xwvutsrqponmlkjihgfedcba",
+ "mxc://localhost/abcdefghijklmnopqrstuvwx"
+ ],
+ "remote": [
+ "mxc://matrix.org/xwvutsrqponmlkjihgfedcba",
+ "mxc://matrix.org/abcdefghijklmnopqrstuvwx"
+ ]
+}
+```
+
+## List all media uploaded by a user
+
+Listing all media that has been uploaded by a local user can be achieved through
+the use of the
+[List media uploaded by a user](user_admin_api.md#list-media-uploaded-by-a-user)
+Admin API.
+
+# Quarantine media
+
+Quarantining media means that it is marked as inaccessible by users. It applies
+to any local media, and any locally-cached copies of remote media.
+
+The media file itself (and any thumbnails) is not deleted from the server.
+
+## Quarantining media by ID
+
+This API quarantines a single piece of local or remote media.
+
+Request:
+
+```
+POST /_synapse/admin/v1/media/quarantine/<server_name>/<media_id>
+
+{}
+```
+
+Where `server_name` is in the form of `example.org`, and `media_id` is in the
+form of `abcdefg12345...`.
+
+Response:
+
+```json
+{}
+```
+
+## Remove media from quarantine by ID
+
+This API removes a single piece of local or remote media from quarantine.
+
+Request:
+
+```
+POST /_synapse/admin/v1/media/unquarantine/<server_name>/<media_id>
+
+{}
+```
+
+Where `server_name` is in the form of `example.org`, and `media_id` is in the
+form of `abcdefg12345...`.
+
+Response:
+
+```json
+{}
+```
+
+## Quarantining media in a room
+
+This API quarantines all local and remote media in a room.
+
+Request:
+
+```
+POST /_synapse/admin/v1/room/<room_id>/media/quarantine
+
+{}
+```
+
+Where `room_id` is in the form of `!roomid12345:example.org`.
+
+Response:
+
+```json
+{
+ "num_quarantined": 10
+}
+```
+
+The following fields are returned in the JSON response body:
+
+* `num_quarantined`: integer - The number of media items successfully quarantined
+
+Note that there is a legacy endpoint, `POST
+/_synapse/admin/v1/quarantine_media/<room_id>`, that operates the same.
+However, it is deprecated and may be removed in a future release.
+
+## Quarantining all media of a user
+
+This API quarantines all *local* media that a *local* user has uploaded. That is to say, if
+you would like to quarantine media uploaded by a user on a remote homeserver, you should
+instead use one of the other APIs.
+
+Request:
+
+```
+POST /_synapse/admin/v1/user/<user_id>/media/quarantine
+
+{}
+```
+
+URL Parameters
+
+* `user_id`: string - User ID in the form of `@bob:example.org`
+
+Response:
+
+```json
+{
+ "num_quarantined": 10
+}
+```
+
+The following fields are returned in the JSON response body:
+
+* `num_quarantined`: integer - The number of media items successfully quarantined
+
+## Protecting media from being quarantined
+
+This API protects a single piece of local media from being quarantined using the
+above APIs. This is useful for sticker packs and other shared media which you do
+not want to get quarantined, especially when
+[quarantining media in a room](#quarantining-media-in-a-room).
+
+Request:
+
+```
+POST /_synapse/admin/v1/media/protect/<media_id>
+
+{}
+```
+
+Where `media_id` is in the form of `abcdefg12345...`.
+
+Response:
+
+```json
+{}
+```
+
+## Unprotecting media from being quarantined
+
+This API reverts the protection of a media.
+
+Request:
+
+```
+POST /_synapse/admin/v1/media/unprotect/<media_id>
+
+{}
+```
+
+Where `media_id` is in the form of `abcdefg12345...`.
+
+Response:
+
+```json
+{}
+```
+
+# Delete local media
+This API deletes the *local* media from the disk of your own server.
+This includes any local thumbnails and copies of media downloaded from
+remote homeservers.
+This API will not affect media that has been uploaded to external
+media repositories (e.g https://github.com/turt2live/matrix-media-repo/).
+See also [Purge Remote Media API](#purge-remote-media-api).
+
+## Delete a specific local media
+Delete a specific `media_id`.
+
+Request:
+
+```
+DELETE /_synapse/admin/v1/media/<server_name>/<media_id>
+
+{}
+```
+
+URL Parameters
+
+* `server_name`: string - The name of your local server (e.g `matrix.org`)
+* `media_id`: string - The ID of the media (e.g `abcdefghijklmnopqrstuvwx`)
+
+Response:
+
+```json
+{
+ "deleted_media": [
+ "abcdefghijklmnopqrstuvwx"
+ ],
+ "total": 1
+}
+```
+
+The following fields are returned in the JSON response body:
+
+* `deleted_media`: an array of strings - List of deleted `media_id`
+* `total`: integer - Total number of deleted `media_id`
+
+## Delete local media by date or size
+
+Request:
+
+```
+POST /_synapse/admin/v1/media/<server_name>/delete?before_ts=<before_ts>
+
+{}
+```
+
+URL Parameters
+
+* `server_name`: string - The name of your local server (e.g `matrix.org`).
+* `before_ts`: string representing a positive integer - Unix timestamp in milliseconds.
+Files that were last used before this timestamp will be deleted. It is the timestamp of
+last access, not the timestamp when the file was created.
+* `size_gt`: Optional - string representing a positive integer - Size of the media in bytes.
+Files that are larger will be deleted. Defaults to `0`.
+* `keep_profiles`: Optional - string representing a boolean - Switch to also delete files
+that are still used in image data (e.g user profile, room avatar).
+If `false` these files will be deleted. Defaults to `true`.
+
+Response:
+
+```json
+{
+ "deleted_media": [
+ "abcdefghijklmnopqrstuvwx",
+ "abcdefghijklmnopqrstuvwz"
+ ],
+ "total": 2
+}
+```
+
+The following fields are returned in the JSON response body:
+
+* `deleted_media`: an array of strings - List of deleted `media_id`
+* `total`: integer - Total number of deleted `media_id`
+
+## Delete media uploaded by a user
+
+You can find details of how to delete multiple media uploaded by a user in
+[User Admin API](user_admin_api.md#delete-media-uploaded-by-a-user).
+
+# Purge Remote Media API
+
+The purge remote media API allows server admins to purge old cached remote media.
+
+The API is:
+
+```
+POST /_synapse/admin/v1/purge_media_cache?before_ts=<unix_timestamp_in_ms>
+
+{}
+```
+
+URL Parameters
+
+* `before_ts`: string representing a positive integer - Unix timestamp in milliseconds.
+All cached media that was last accessed before this timestamp will be removed.
+
+Response:
+
+```json
+{
+ "deleted": 10
+}
+```
+
+The following fields are returned in the JSON response body:
+
+* `deleted`: integer - The number of media items successfully deleted
+
+If the user re-requests purged remote media, synapse will re-request the media
+from the originating server.
diff --git a/docs/administration/admin_api/purge_history_api.md b/docs/administration/admin_api/purge_history_api.md
new file mode 100644
index 0000000000..2527e2758b
--- /dev/null
+++ b/docs/administration/admin_api/purge_history_api.md
@@ -0,0 +1,77 @@
+# Purge History API
+
+The purge history API allows server admins to purge historic events from their
+database, reclaiming disk space.
+
+Depending on the amount of history being purged a call to the API may take
+several minutes or longer. During this period users will not be able to
+paginate further back in the room from the point being purged from.
+
+Note that Synapse requires at least one message in each room, so it will never
+delete the last message in a room.
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+The API is:
+
+```
+POST /_synapse/admin/v1/purge_history/<room_id>[/<event_id>]
+```
+
+By default, events sent by local users are not deleted, as they may represent
+the only copies of this content in existence. (Events sent by remote users are
+deleted.)
+
+Room state data (such as joins, leaves, topic) is always preserved.
+
+To delete local message events as well, set `delete_local_events` in the body:
+
+```json
+{
+ "delete_local_events": true
+}
+```
+
+The caller must specify the point in the room to purge up to. This can be
+specified by including an event_id in the URI, or by setting a
+`purge_up_to_event_id` or `purge_up_to_ts` in the request body. If an event
+id is given, that event (and others at the same graph depth) will be retained.
+If `purge_up_to_ts` is given, it should be a timestamp since the unix epoch,
+in milliseconds.
+
+The API starts the purge running, and returns immediately with a JSON body with
+a purge id:
+
+```json
+{
+ "purge_id": "<opaque id>"
+}
+```
+
+## Purge status query
+
+It is possible to poll for updates on recent purges with a second API;
+
+```
+GET /_synapse/admin/v1/purge_history_status/<purge_id>
+```
+
+This API returns a JSON body like the following:
+
+```json
+{
+ "status": "active"
+}
+```
+
+The status will be one of `active`, `complete`, or `failed`.
+
+If `status` is `failed` there will be a string `error` with the error message.
+
+## Reclaim disk space (Postgres)
+
+To reclaim the disk space and return it to the operating system, you need to run
+`VACUUM FULL;` on the database.
+
+<https://www.postgresql.org/docs/current/sql-vacuum.html>
diff --git a/docs/administration/admin_api/register_api.md b/docs/administration/admin_api/register_api.md
new file mode 100644
index 0000000000..99b79e284a
--- /dev/null
+++ b/docs/administration/admin_api/register_api.md
@@ -0,0 +1,90 @@
+# Shared-Secret Registration
+
+This API allows for the creation of users in an administrative and
+non-interactive way. This is generally used for bootstrapping a Synapse
+instance with administrator accounts.
+
+To authenticate yourself to the server, you will need both the shared secret
+([`registration_shared_secret`](../../usage/configuration/config_documentation.md#registration_shared_secret)
+in the homeserver configuration), and a one-time nonce. If the registration
+shared secret is not configured, this API is not enabled.
+
+To fetch the nonce, you need to request one from the API:
+
+```
+> GET /_synapse/admin/v1/register
+
+< {"nonce": "thisisanonce"}
+```
+
+Once you have the nonce, you can make a `POST` to the same URL with a JSON
+body containing the nonce, username, password, whether they are an admin
+(optional, False by default), and a HMAC digest of the content. Also you can
+set the displayname (optional, `username` by default).
+
+As an example:
+
+```
+> POST /_synapse/admin/v1/register
+> {
+ "nonce": "thisisanonce",
+ "username": "pepper_roni",
+ "displayname": "Pepper Roni",
+ "password": "pizza",
+ "admin": true,
+ "mac": "mac_digest_here"
+ }
+
+< {
+ "access_token": "token_here",
+ "user_id": "@pepper_roni:localhost",
+ "home_server": "test",
+ "device_id": "device_id_here"
+ }
+```
+
+The MAC is the hex digest output of the HMAC-SHA1 algorithm, with the key being
+the shared secret and the content being the nonce, user, password, either the
+string "admin" or "notadmin", and optionally the user_type
+each separated by NULs.
+
+Here is an easy way to generate the HMAC digest if you have Bash and OpenSSL:
+
+```bash
+# Update these values and then paste this code block into a bash terminal
+nonce='thisisanonce'
+username='pepper_roni'
+password='pizza'
+admin='admin'
+secret='shared_secret'
+
+printf '%s\0%s\0%s\0%s' "$nonce" "$username" "$password" "$admin" |
+ openssl sha1 -hmac "$secret" |
+ awk '{print $2}'
+```
+
+For an example of generation in Python:
+
+```python
+import hmac, hashlib
+
+def generate_mac(nonce, user, password, admin=False, user_type=None):
+
+ mac = hmac.new(
+ key=shared_secret,
+ digestmod=hashlib.sha1,
+ )
+
+ mac.update(nonce.encode('utf8'))
+ mac.update(b"\x00")
+ mac.update(user.encode('utf8'))
+ mac.update(b"\x00")
+ mac.update(password.encode('utf8'))
+ mac.update(b"\x00")
+ mac.update(b"admin" if admin else b"notadmin")
+ if user_type:
+ mac.update(b"\x00")
+ mac.update(user_type.encode('utf8'))
+
+ return mac.hexdigest()
+```
diff --git a/docs/administration/admin_api/registration_tokens.md b/docs/administration/admin_api/registration_tokens.md
new file mode 100644
index 0000000000..d18b45010d
--- /dev/null
+++ b/docs/administration/admin_api/registration_tokens.md
@@ -0,0 +1,296 @@
+# Registration Tokens
+
+This API allows you to manage tokens which can be used to authenticate
+registration requests, as proposed in
+[MSC3231](https://github.com/matrix-org/matrix-doc/blob/main/proposals/3231-token-authenticated-registration.md)
+and stabilised in version 1.2 of the Matrix specification.
+To use it, you will need to enable the `registration_requires_token` config
+option, and authenticate by providing an `access_token` for a server admin:
+see [Admin API](../usage/administration/admin_api).
+
+
+## Registration token objects
+
+Most endpoints make use of JSON objects that contain details about tokens.
+These objects have the following fields:
+- `token`: The token which can be used to authenticate registration.
+- `uses_allowed`: The number of times the token can be used to complete a
+ registration before it becomes invalid.
+- `pending`: The number of pending uses the token has. When someone uses
+ the token to authenticate themselves, the pending counter is incremented
+ so that the token is not used more than the permitted number of times.
+ When the person completes registration the pending counter is decremented,
+ and the completed counter is incremented.
+- `completed`: The number of times the token has been used to successfully
+ complete a registration.
+- `expiry_time`: The latest time the token is valid. Given as the number of
+ milliseconds since 1970-01-01 00:00:00 UTC (the start of the Unix epoch).
+ To convert this into a human-readable form you can remove the milliseconds
+ and use the `date` command. For example, `date -d '@1625394937'`.
+
+
+## List all tokens
+
+Lists all tokens and details about them. If the request is successful, the top
+level JSON object will have a `registration_tokens` key which is an array of
+registration token objects.
+
+```
+GET /_synapse/admin/v1/registration_tokens
+```
+
+Optional query parameters:
+- `valid`: `true` or `false`. If `true`, only valid tokens are returned.
+ If `false`, only tokens that have expired or have had all uses exhausted are
+ returned. If omitted, all tokens are returned regardless of validity.
+
+Example:
+
+```
+GET /_synapse/admin/v1/registration_tokens
+```
+```
+200 OK
+
+{
+ "registration_tokens": [
+ {
+ "token": "abcd",
+ "uses_allowed": 3,
+ "pending": 0,
+ "completed": 1,
+ "expiry_time": null
+ },
+ {
+ "token": "pqrs",
+ "uses_allowed": 2,
+ "pending": 1,
+ "completed": 1,
+ "expiry_time": null
+ },
+ {
+ "token": "wxyz",
+ "uses_allowed": null,
+ "pending": 0,
+ "completed": 9,
+ "expiry_time": 1625394937000 // 2021-07-04 10:35:37 UTC
+ }
+ ]
+}
+```
+
+Example using the `valid` query parameter:
+
+```
+GET /_synapse/admin/v1/registration_tokens?valid=false
+```
+```
+200 OK
+
+{
+ "registration_tokens": [
+ {
+ "token": "pqrs",
+ "uses_allowed": 2,
+ "pending": 1,
+ "completed": 1,
+ "expiry_time": null
+ },
+ {
+ "token": "wxyz",
+ "uses_allowed": null,
+ "pending": 0,
+ "completed": 9,
+ "expiry_time": 1625394937000 // 2021-07-04 10:35:37 UTC
+ }
+ ]
+}
+```
+
+
+## Get one token
+
+Get details about a single token. If the request is successful, the response
+body will be a registration token object.
+
+```
+GET /_synapse/admin/v1/registration_tokens/<token>
+```
+
+Path parameters:
+- `token`: The registration token to return details of.
+
+Example:
+
+```
+GET /_synapse/admin/v1/registration_tokens/abcd
+```
+```
+200 OK
+
+{
+ "token": "abcd",
+ "uses_allowed": 3,
+ "pending": 0,
+ "completed": 1,
+ "expiry_time": null
+}
+```
+
+
+## Create token
+
+Create a new registration token. If the request is successful, the newly created
+token will be returned as a registration token object in the response body.
+
+```
+POST /_synapse/admin/v1/registration_tokens/new
+```
+
+The request body must be a JSON object and can contain the following fields:
+- `token`: The registration token. A string of no more than 64 characters that
+ consists only of characters matched by the regex `[A-Za-z0-9._~-]`.
+ Default: randomly generated.
+- `uses_allowed`: The integer number of times the token can be used to complete
+ a registration before it becomes invalid.
+ Default: `null` (unlimited uses).
+- `expiry_time`: The latest time the token is valid. Given as the number of
+ milliseconds since 1970-01-01 00:00:00 UTC (the start of the Unix epoch).
+ You could use, for example, `date '+%s000' -d 'tomorrow'`.
+ Default: `null` (token does not expire).
+- `length`: The length of the token randomly generated if `token` is not
+ specified. Must be between 1 and 64 inclusive. Default: `16`.
+
+If a field is omitted the default is used.
+
+Example using defaults:
+
+```
+POST /_synapse/admin/v1/registration_tokens/new
+
+{}
+```
+```
+200 OK
+
+{
+ "token": "0M-9jbkf2t_Tgiw1",
+ "uses_allowed": null,
+ "pending": 0,
+ "completed": 0,
+ "expiry_time": null
+}
+```
+
+Example specifying some fields:
+
+```
+POST /_synapse/admin/v1/registration_tokens/new
+
+{
+ "token": "defg",
+ "uses_allowed": 1
+}
+```
+```
+200 OK
+
+{
+ "token": "defg",
+ "uses_allowed": 1,
+ "pending": 0,
+ "completed": 0,
+ "expiry_time": null
+}
+```
+
+
+## Update token
+
+Update the number of allowed uses or expiry time of a token. If the request is
+successful, the updated token will be returned as a registration token object
+in the response body.
+
+```
+PUT /_synapse/admin/v1/registration_tokens/<token>
+```
+
+Path parameters:
+- `token`: The registration token to update.
+
+The request body must be a JSON object and can contain the following fields:
+- `uses_allowed`: The integer number of times the token can be used to complete
+ a registration before it becomes invalid. By setting `uses_allowed` to `0`
+ the token can be easily made invalid without deleting it.
+ If `null` the token will have an unlimited number of uses.
+- `expiry_time`: The latest time the token is valid. Given as the number of
+ milliseconds since 1970-01-01 00:00:00 UTC (the start of the Unix epoch).
+ If `null` the token will not expire.
+
+If a field is omitted its value is not modified.
+
+Example:
+
+```
+PUT /_synapse/admin/v1/registration_tokens/defg
+
+{
+ "expiry_time": 4781243146000 // 2121-07-06 11:05:46 UTC
+}
+```
+```
+200 OK
+
+{
+ "token": "defg",
+ "uses_allowed": 1,
+ "pending": 0,
+ "completed": 0,
+ "expiry_time": 4781243146000
+}
+```
+
+
+## Delete token
+
+Delete a registration token. If the request is successful, the response body
+will be an empty JSON object.
+
+```
+DELETE /_synapse/admin/v1/registration_tokens/<token>
+```
+
+Path parameters:
+- `token`: The registration token to delete.
+
+Example:
+
+```
+DELETE /_synapse/admin/v1/registration_tokens/wxyz
+```
+```
+200 OK
+
+{}
+```
+
+
+## Errors
+
+If a request fails a "standard error response" will be returned as defined in
+the [Matrix Client-Server API specification](https://matrix.org/docs/spec/client_server/r0.6.1#api-standards).
+
+For example, if the token specified in a path parameter does not exist a
+`404 Not Found` error will be returned.
+
+```
+GET /_synapse/admin/v1/registration_tokens/1234
+```
+```
+404 Not Found
+
+{
+ "errcode": "M_NOT_FOUND",
+ "error": "No such registration token: 1234"
+}
+```
diff --git a/docs/administration/admin_api/room_membership.md b/docs/administration/admin_api/room_membership.md
new file mode 100644
index 0000000000..310d6ae628
--- /dev/null
+++ b/docs/administration/admin_api/room_membership.md
@@ -0,0 +1,35 @@
+# Edit Room Membership API
+
+This API allows an administrator to join an user account with a given `user_id`
+to a room with a given `room_id_or_alias`. You can only modify the membership of
+local users. The server administrator must be in the room and have permission to
+invite users.
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+## Parameters
+
+The following parameters are available:
+
+* `user_id` - Fully qualified user: for example, `@user:server.com`.
+* `room_id_or_alias` - The room identifier or alias to join: for example,
+ `!636q39766251:server.com`.
+
+## Usage
+
+```
+POST /_synapse/admin/v1/join/<room_id_or_alias>
+
+{
+ "user_id": "@user:server.com"
+}
+```
+
+Response:
+
+```json
+{
+ "room_id": "!636q39766251:server.com"
+}
+```
diff --git a/docs/administration/admin_api/rooms.md b/docs/administration/admin_api/rooms.md
new file mode 100644
index 0000000000..8f727b363e
--- /dev/null
+++ b/docs/administration/admin_api/rooms.md
@@ -0,0 +1,1100 @@
+# List Room API
+
+The List Room admin API allows server admins to get a list of rooms on their
+server. There are various parameters available that allow for filtering and
+sorting the returned list. This API supports pagination.
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+**Parameters**
+
+The following query parameters are available:
+
+* `from` - Offset in the returned list. Defaults to `0`.
+* `limit` - Maximum amount of rooms to return. Defaults to `100`.
+* `order_by` - The method in which to sort the returned list of rooms. Valid values are:
+ - `alphabetical` - Same as `name`. This is deprecated.
+ - `size` - Same as `joined_members`. This is deprecated.
+ - `name` - Rooms are ordered alphabetically by room name. This is the default.
+ - `canonical_alias` - Rooms are ordered alphabetically by main alias address of the room.
+ - `joined_members` - Rooms are ordered by the number of members. Largest to smallest.
+ - `joined_local_members` - Rooms are ordered by the number of local members. Largest to smallest.
+ - `version` - Rooms are ordered by room version. Largest to smallest.
+ - `creator` - Rooms are ordered alphabetically by creator of the room.
+ - `encryption` - Rooms are ordered alphabetically by the end-to-end encryption algorithm.
+ - `federatable` - Rooms are ordered by whether the room is federatable.
+ - `public` - Rooms are ordered by visibility in room list.
+ - `join_rules` - Rooms are ordered alphabetically by join rules of the room.
+ - `guest_access` - Rooms are ordered alphabetically by guest access option of the room.
+ - `history_visibility` - Rooms are ordered alphabetically by visibility of history of the room.
+ - `state_events` - Rooms are ordered by number of state events. Largest to smallest.
+* `dir` - Direction of room order. Either `f` for forwards or `b` for backwards. Setting
+ this value to `b` will reverse the above sort order. Defaults to `f`.
+* `search_term` - Filter rooms by their room name, canonical alias and room id.
+ Specifically, rooms are selected if the search term is contained in
+ - the room's name,
+ - the local part of the room's canonical alias, or
+ - the complete (local and server part) room's id (case sensitive).
+
+ Defaults to no filtering.
+
+**Response**
+
+The following fields are possible in the JSON response body:
+
+* `rooms` - An array of objects, each containing information about a room.
+ - Room objects contain the following fields:
+ - `room_id` - The ID of the room.
+ - `name` - The name of the room.
+ - `canonical_alias` - The canonical (main) alias address of the room.
+ - `joined_members` - How many users are currently in the room.
+ - `joined_local_members` - How many local users are currently in the room.
+ - `version` - The version of the room as a string.
+ - `creator` - The `user_id` of the room creator.
+ - `encryption` - Algorithm of end-to-end encryption of messages. Is `null` if encryption is not active.
+ - `federatable` - Whether users on other servers can join this room.
+ - `public` - Whether the room is visible in room directory.
+ - `join_rules` - The type of rules used for users wishing to join this room. One of: ["public", "knock", "invite", "private"].
+ - `guest_access` - Whether guests can join the room. One of: ["can_join", "forbidden"].
+ - `history_visibility` - Who can see the room history. One of: ["invited", "joined", "shared", "world_readable"].
+ - `state_events` - Total number of state_events of a room. Complexity of the room.
+ - `room_type` - The type of the room taken from the room's creation event; for example "m.space" if the room is a space. If the room does not define a type, the value will be `null`.
+* `offset` - The current pagination offset in rooms. This parameter should be
+ used instead of `next_token` for room offset as `next_token` is
+ not intended to be parsed.
+* `total_rooms` - The total number of rooms this query can return. Using this
+ and `offset`, you have enough information to know the current
+ progression through the list.
+* `next_batch` - If this field is present, we know that there are potentially
+ more rooms on the server that did not all fit into this response.
+ We can use `next_batch` to get the "next page" of results. To do
+ so, simply repeat your request, setting the `from` parameter to
+ the value of `next_batch`.
+* `prev_batch` - If this field is present, it is possible to paginate backwards.
+ Use `prev_batch` for the `from` value in the next request to
+ get the "previous page" of results.
+
+The API is:
+
+A standard request with no filtering:
+
+```
+GET /_synapse/admin/v1/rooms
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "rooms": [
+ {
+ "room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
+ "name": "Matrix HQ",
+ "canonical_alias": "#matrix:matrix.org",
+ "joined_members": 8326,
+ "joined_local_members": 2,
+ "version": "1",
+ "creator": "@foo:matrix.org",
+ "encryption": null,
+ "federatable": true,
+ "public": true,
+ "join_rules": "invite",
+ "guest_access": null,
+ "history_visibility": "shared",
+ "state_events": 93534,
+ "room_type": "m.space"
+ },
+ ... (8 hidden items) ...
+ {
+ "room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
+ "name": "This Week In Matrix (TWIM)",
+ "canonical_alias": "#twim:matrix.org",
+ "joined_members": 314,
+ "joined_local_members": 20,
+ "version": "4",
+ "creator": "@foo:matrix.org",
+ "encryption": "m.megolm.v1.aes-sha2",
+ "federatable": true,
+ "public": false,
+ "join_rules": "invite",
+ "guest_access": null,
+ "history_visibility": "shared",
+ "state_events": 8345,
+ "room_type": null
+ }
+ ],
+ "offset": 0,
+ "total_rooms": 10
+}
+```
+
+Filtering by room name:
+
+```
+GET /_synapse/admin/v1/rooms?search_term=TWIM
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "rooms": [
+ {
+ "room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
+ "name": "This Week In Matrix (TWIM)",
+ "canonical_alias": "#twim:matrix.org",
+ "joined_members": 314,
+ "joined_local_members": 20,
+ "version": "4",
+ "creator": "@foo:matrix.org",
+ "encryption": "m.megolm.v1.aes-sha2",
+ "federatable": true,
+ "public": false,
+ "join_rules": "invite",
+ "guest_access": null,
+ "history_visibility": "shared",
+ "state_events": 8,
+ "room_type": null
+ }
+ ],
+ "offset": 0,
+ "total_rooms": 1
+}
+```
+
+Paginating through a list of rooms:
+
+```
+GET /_synapse/admin/v1/rooms?order_by=size
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "rooms": [
+ {
+ "room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
+ "name": "Matrix HQ",
+ "canonical_alias": "#matrix:matrix.org",
+ "joined_members": 8326,
+ "joined_local_members": 2,
+ "version": "1",
+ "creator": "@foo:matrix.org",
+ "encryption": null,
+ "federatable": true,
+ "public": true,
+ "join_rules": "invite",
+ "guest_access": null,
+ "history_visibility": "shared",
+ "state_events": 93534,
+ "room_type": null
+ },
+ ... (98 hidden items) ...
+ {
+ "room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
+ "name": "This Week In Matrix (TWIM)",
+ "canonical_alias": "#twim:matrix.org",
+ "joined_members": 314,
+ "joined_local_members": 20,
+ "version": "4",
+ "creator": "@foo:matrix.org",
+ "encryption": "m.megolm.v1.aes-sha2",
+ "federatable": true,
+ "public": false,
+ "join_rules": "invite",
+ "guest_access": null,
+ "history_visibility": "shared",
+ "state_events": 8345,
+ "room_type": "m.space"
+ }
+ ],
+ "offset": 0,
+ "total_rooms": 150,
+ "next_token": 100
+}
+```
+
+The presence of the `next_token` parameter tells us that there are more rooms
+than returned in this request, and we need to make another request to get them.
+To get the next batch of room results, we repeat our request, setting the `from`
+parameter to the value of `next_token`.
+
+```
+GET /_synapse/admin/v1/rooms?order_by=size&from=100
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "rooms": [
+ {
+ "room_id": "!mscvqgqpHYjBGDxNym:matrix.org",
+ "name": "Music Theory",
+ "canonical_alias": "#musictheory:matrix.org",
+ "joined_members": 127,
+ "joined_local_members": 2,
+ "version": "1",
+ "creator": "@foo:matrix.org",
+ "encryption": null,
+ "federatable": true,
+ "public": true,
+ "join_rules": "invite",
+ "guest_access": null,
+ "history_visibility": "shared",
+ "state_events": 93534,
+ "room_type": "m.space"
+
+ },
+ ... (48 hidden items) ...
+ {
+ "room_id": "!twcBhHVdZlQWuuxBhN:termina.org.uk",
+ "name": "weechat-matrix",
+ "canonical_alias": "#weechat-matrix:termina.org.uk",
+ "joined_members": 137,
+ "joined_local_members": 20,
+ "version": "4",
+ "creator": "@foo:termina.org.uk",
+ "encryption": null,
+ "federatable": true,
+ "public": true,
+ "join_rules": "invite",
+ "guest_access": null,
+ "history_visibility": "shared",
+ "state_events": 8345,
+ "room_type": null
+
+ }
+ ],
+ "offset": 100,
+ "prev_batch": 0,
+ "total_rooms": 150
+}
+```
+
+Once the `next_token` parameter is no longer present, we know we've reached the
+end of the list.
+
+# Room Details API
+
+The Room Details admin API allows server admins to get all details of a room.
+
+The following fields are possible in the JSON response body:
+
+* `room_id` - The ID of the room.
+* `name` - The name of the room.
+* `topic` - The topic of the room.
+* `avatar` - The `mxc` URI to the avatar of the room.
+* `canonical_alias` - The canonical (main) alias address of the room.
+* `joined_members` - How many users are currently in the room.
+* `joined_local_members` - How many local users are currently in the room.
+* `joined_local_devices` - How many local devices are currently in the room.
+* `version` - The version of the room as a string.
+* `creator` - The `user_id` of the room creator.
+* `encryption` - Algorithm of end-to-end encryption of messages. Is `null` if encryption is not active.
+* `federatable` - Whether users on other servers can join this room.
+* `public` - Whether the room is visible in room directory.
+* `join_rules` - The type of rules used for users wishing to join this room. One of: ["public", "knock", "invite", "private"].
+* `guest_access` - Whether guests can join the room. One of: ["can_join", "forbidden"].
+* `history_visibility` - Who can see the room history. One of: ["invited", "joined", "shared", "world_readable"].
+* `state_events` - Total number of state_events of a room. Complexity of the room.
+* `room_type` - The type of the room taken from the room's creation event; for example "m.space" if the room is a space.
+ If the room does not define a type, the value will be `null`.
+* `forgotten` - Whether all local users have
+ [forgotten](https://spec.matrix.org/latest/client-server-api/#leaving-rooms) the room.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/rooms/<room_id>
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "room_id": "!mscvqgqpHYjBGDxNym:matrix.org",
+ "name": "Music Theory",
+ "avatar": "mxc://matrix.org/AQDaVFlbkQoErdOgqWRgiGSV",
+ "topic": "Theory, Composition, Notation, Analysis",
+ "canonical_alias": "#musictheory:matrix.org",
+ "joined_members": 127,
+ "joined_local_members": 2,
+ "joined_local_devices": 2,
+ "version": "1",
+ "creator": "@foo:matrix.org",
+ "encryption": null,
+ "federatable": true,
+ "public": true,
+ "join_rules": "invite",
+ "guest_access": null,
+ "history_visibility": "shared",
+ "state_events": 93534,
+ "room_type": "m.space",
+ "forgotten": false
+}
+```
+
+_Changed in Synapse 1.66:_ Added the `forgotten` key to the response body.
+
+# Room Members API
+
+The Room Members admin API allows server admins to get a list of all members of a room.
+
+The response includes the following fields:
+
+* `members` - A list of all the members that are present in the room, represented by their ids.
+* `total` - Total number of members in the room.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/rooms/<room_id>/members
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "members": [
+ "@foo:matrix.org",
+ "@bar:matrix.org",
+ "@foobar:matrix.org"
+ ],
+ "total": 3
+}
+```
+
+# Room State API
+
+The Room State admin API allows server admins to get a list of all state events in a room.
+
+The response includes the following fields:
+
+* `state` - The current state of the room at the time of request.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/rooms/<room_id>/state
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "state": [
+ {"type": "m.room.create", "state_key": "", "etc": true},
+ {"type": "m.room.power_levels", "state_key": "", "etc": true},
+ {"type": "m.room.name", "state_key": "", "etc": true}
+ ]
+}
+```
+
+# Room Messages API
+
+The Room Messages admin API allows server admins to get all messages
+sent to a room in a given timeframe. There are various parameters available
+that allow for filtering and ordering the returned list. This API supports pagination.
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+This endpoint mirrors the [Matrix Spec defined Messages API](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3roomsroomidmessages).
+
+The API is:
+```
+GET /_synapse/admin/v1/rooms/<room_id>/messages
+```
+
+**Parameters**
+
+The following path parameters are required:
+
+* `room_id` - The ID of the room you wish you fetch messages from.
+
+The following query parameters are available:
+
+* `from` (required) - The token to start returning events from. This token can be obtained from a prev_batch
+ or next_batch token returned by the /sync endpoint, or from an end token returned by a previous request to this endpoint.
+* `to` - The token to spot returning events at.
+* `limit` - The maximum number of events to return. Defaults to `10`.
+* `filter` - A JSON RoomEventFilter to filter returned events with.
+* `dir` - The direction to return events from. Either `f` for forwards or `b` for backwards. Setting
+ this value to `b` will reverse the above sort order. Defaults to `f`.
+
+**Response**
+
+The following fields are possible in the JSON response body:
+
+* `chunk` - A list of room events. The order depends on the dir parameter.
+ Note that an empty chunk does not necessarily imply that no more events are available. Clients should continue to paginate until no end property is returned.
+* `end` - A token corresponding to the end of chunk. This token can be passed back to this endpoint to request further events.
+ If no further events are available, this property is omitted from the response.
+* `start` - A token corresponding to the start of chunk.
+* `state` - A list of state events relevant to showing the chunk.
+
+**Example**
+
+For more details on each chunk, read [the Matrix specification](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3roomsroomidmessages).
+
+```json
+{
+ "chunk": [
+ {
+ "content": {
+ "body": "This is an example text message",
+ "format": "org.matrix.custom.html",
+ "formatted_body": "<b>This is an example text message</b>",
+ "msgtype": "m.text"
+ },
+ "event_id": "$143273582443PhrSn:example.org",
+ "origin_server_ts": 1432735824653,
+ "room_id": "!636q39766251:example.com",
+ "sender": "@example:example.org",
+ "type": "m.room.message",
+ "unsigned": {
+ "age": 1234
+ }
+ },
+ {
+ "content": {
+ "name": "The room name"
+ },
+ "event_id": "$143273582443PhrSn:example.org",
+ "origin_server_ts": 1432735824653,
+ "room_id": "!636q39766251:example.com",
+ "sender": "@example:example.org",
+ "state_key": "",
+ "type": "m.room.name",
+ "unsigned": {
+ "age": 1234
+ }
+ },
+ {
+ "content": {
+ "body": "Gangnam Style",
+ "info": {
+ "duration": 2140786,
+ "h": 320,
+ "mimetype": "video/mp4",
+ "size": 1563685,
+ "thumbnail_info": {
+ "h": 300,
+ "mimetype": "image/jpeg",
+ "size": 46144,
+ "w": 300
+ },
+ "thumbnail_url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
+ "w": 480
+ },
+ "msgtype": "m.video",
+ "url": "mxc://example.org/a526eYUSFFxlgbQYZmo442"
+ },
+ "event_id": "$143273582443PhrSn:example.org",
+ "origin_server_ts": 1432735824653,
+ "room_id": "!636q39766251:example.com",
+ "sender": "@example:example.org",
+ "type": "m.room.message",
+ "unsigned": {
+ "age": 1234
+ }
+ }
+ ],
+ "end": "t47409-4357353_219380_26003_2265",
+ "start": "t47429-4392820_219380_26003_2265"
+}
+```
+
+# Room Timestamp to Event API
+
+The Room Timestamp to Event API endpoint fetches the `event_id` of the closest event to the given
+timestamp (`ts` query parameter) in the given direction (`dir` query parameter).
+
+Useful for cases like jump to date so you can start paginating messages from
+a given date in the archive.
+
+The API is:
+```
+ GET /_synapse/admin/v1/rooms/<room_id>/timestamp_to_event
+```
+
+**Parameters**
+
+The following path parameters are required:
+
+* `room_id` - The ID of the room you wish to check.
+
+The following query parameters are available:
+
+* `ts` - a timestamp in milliseconds where we will find the closest event in
+ the given direction.
+* `dir` - can be `f` or `b` to indicate forwards and backwards in time from the
+ given timestamp. Defaults to `f`.
+
+**Response**
+
+* `event_id` - converted from timestamp
+
+# Block Room API
+The Block Room admin API allows server admins to block and unblock rooms,
+and query to see if a given room is blocked.
+This API can be used to pre-emptively block a room, even if it's unknown to this
+homeserver. Users will be prevented from joining a blocked room.
+
+## Block or unblock a room
+
+The API is:
+
+```
+PUT /_synapse/admin/v1/rooms/<room_id>/block
+```
+
+with a body of:
+
+```json
+{
+ "block": true
+}
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "block": true
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `room_id` - The ID of the room.
+
+The following JSON body parameters are available:
+
+- `block` - If `true` the room will be blocked and if `false` the room will be unblocked.
+
+**Response**
+
+The following fields are possible in the JSON response body:
+
+- `block` - A boolean. `true` if the room is blocked, otherwise `false`
+
+## Get block status
+
+The API is:
+
+```
+GET /_synapse/admin/v1/rooms/<room_id>/block
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "block": true,
+ "user_id": "<user_id>"
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `room_id` - The ID of the room.
+
+**Response**
+
+The following fields are possible in the JSON response body:
+
+- `block` - A boolean. `true` if the room is blocked, otherwise `false`
+- `user_id` - An optional string. If the room is blocked (`block` is `true`) shows
+ the user who has add the room to blocking list. Otherwise it is not displayed.
+
+# Delete Room API
+
+The Delete Room admin API allows server admins to remove rooms from the server
+and block these rooms.
+
+Shuts down a room. Moves all local users and room aliases automatically to a
+new room if `new_room_user_id` is set. Otherwise local users only
+leave the room without any information.
+
+The new room will be created with the user specified by the `new_room_user_id` parameter
+as room administrator and will contain a message explaining what happened. Users invited
+to the new room will have power level `-10` by default, and thus be unable to speak.
+
+If `block` is `true`, users will be prevented from joining the old room.
+This option can in [Version 1](#version-1-old-version) also be used to pre-emptively
+block a room, even if it's unknown to this homeserver. In this case, the room will be
+blocked, and no further action will be taken. If `block` is `false`, attempting to
+delete an unknown room is invalid and will be rejected as a bad request.
+
+This API will remove all trace of the old room from your database after removing
+all local users. If `purge` is `true` (the default), all traces of the old room will
+be removed from your database after removing all local users. If you do not want
+this to happen, set `purge` to `false`.
+Depending on the amount of history being purged, a call to the API may take
+several minutes or longer.
+
+The local server will only have the power to move local user and room aliases to
+the new room. Users on other servers will be unaffected.
+
+## Version 1 (old version)
+
+This version works synchronously. That means you only get the response once the server has
+finished the action, which may take a long time. If you request the same action
+a second time, and the server has not finished the first one, the second request will block.
+This is fixed in version 2 of this API. The parameters are the same in both APIs.
+This API will become deprecated in the future.
+
+The API is:
+
+```
+DELETE /_synapse/admin/v1/rooms/<room_id>
+```
+
+with a body of:
+
+```json
+{
+ "new_room_user_id": "@someuser:example.com",
+ "room_name": "Content Violation Notification",
+ "message": "Bad Room has been shutdown due to content violations on this server. Please review our Terms of Service.",
+ "block": true,
+ "purge": true
+}
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "kicked_users": [
+ "@foobar:example.com"
+ ],
+ "failed_to_kick_users": [],
+ "local_aliases": [
+ "#badroom:example.com",
+ "#evilsaloon:example.com"
+ ],
+ "new_room_id": "!newroomid:example.com"
+}
+```
+
+The parameters and response values have the same format as
+[version 2](#version-2-new-version) of the API.
+
+## Version 2 (new version)
+
+**Note**: This API is new, experimental and "subject to change".
+
+This version works asynchronously, meaning you get the response from server immediately
+while the server works on that task in background. You can then request the status of the action
+to check if it has completed.
+
+The API is:
+
+```
+DELETE /_synapse/admin/v2/rooms/<room_id>
+```
+
+with a body of:
+
+```json
+{
+ "new_room_user_id": "@someuser:example.com",
+ "room_name": "Content Violation Notification",
+ "message": "Bad Room has been shutdown due to content violations on this server. Please review our Terms of Service.",
+ "block": true,
+ "purge": true
+}
+```
+
+The API starts the shut down and purge running, and returns immediately with a JSON body with
+a purge id:
+
+```json
+{
+ "delete_id": "<opaque id>"
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+* `room_id` - The ID of the room.
+
+The following JSON body parameters are available:
+
+* `new_room_user_id` - Optional. If set, a new room will be created with this user ID
+ as the creator and admin, and all users in the old room will be moved into that
+ room. If not set, no new room will be created and the users will just be removed
+ from the old room. The user ID must be on the local server, but does not necessarily
+ have to belong to a registered user.
+* `room_name` - Optional. A string representing the name of the room that new users will be
+ invited to. Defaults to `Content Violation Notification`
+* `message` - Optional. A string containing the first message that will be sent as
+ `new_room_user_id` in the new room. Ideally this will clearly convey why the
+ original room was shut down. Defaults to `Sharing illegal content on this server
+ is not permitted and rooms in violation will be blocked.`
+* `block` - Optional. If set to `true`, this room will be added to a blocking list,
+ preventing future attempts to join the room. Rooms can be blocked
+ even if they're not yet known to the homeserver (only with
+ [Version 1](#version-1-old-version) of the API). Defaults to `false`.
+* `purge` - Optional. If set to `true`, it will remove all traces of the room from your database.
+ Defaults to `true`.
+* `force_purge` - Optional, and ignored unless `purge` is `true`. If set to `true`, it
+ will force a purge to go ahead even if there are local users still in the room. Do not
+ use this unless a regular `purge` operation fails, as it could leave those users'
+ clients in a confused state.
+
+The JSON body must not be empty. The body must be at least `{}`.
+
+## Status of deleting rooms
+
+**Note**: This API is new, experimental and "subject to change".
+
+It is possible to query the status of the background task for deleting rooms.
+The status can be queried up to 24 hours after completion of the task,
+or until Synapse is restarted (whichever happens first).
+
+### Query by `room_id`
+
+With this API you can get the status of all active deletion tasks, and all those completed in the last 24h,
+for the given `room_id`.
+
+The API is:
+
+```
+GET /_synapse/admin/v2/rooms/<room_id>/delete_status
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "results": [
+ {
+ "delete_id": "delete_id1",
+ "status": "failed",
+ "error": "error message",
+ "shutdown_room": {
+ "kicked_users": [],
+ "failed_to_kick_users": [],
+ "local_aliases": [],
+ "new_room_id": null
+ }
+ }, {
+ "delete_id": "delete_id2",
+ "status": "purging",
+ "shutdown_room": {
+ "kicked_users": [
+ "@foobar:example.com"
+ ],
+ "failed_to_kick_users": [],
+ "local_aliases": [
+ "#badroom:example.com",
+ "#evilsaloon:example.com"
+ ],
+ "new_room_id": "!newroomid:example.com"
+ }
+ }
+ ]
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+* `room_id` - The ID of the room.
+
+### Query by `delete_id`
+
+With this API you can get the status of one specific task by `delete_id`.
+
+The API is:
+
+```
+GET /_synapse/admin/v2/rooms/delete_status/<delete_id>
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "status": "purging",
+ "shutdown_room": {
+ "kicked_users": [
+ "@foobar:example.com"
+ ],
+ "failed_to_kick_users": [],
+ "local_aliases": [
+ "#badroom:example.com",
+ "#evilsaloon:example.com"
+ ],
+ "new_room_id": "!newroomid:example.com"
+ }
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+* `delete_id` - The ID for this delete.
+
+### Response
+
+The following fields are returned in the JSON response body:
+
+- `results` - An array of objects, each containing information about one task.
+ This field is omitted from the result when you query by `delete_id`.
+ Task objects contain the following fields:
+ - `delete_id` - The ID for this purge if you query by `room_id`.
+ - `status` - The status will be one of:
+ - `shutting_down` - The process is removing users from the room.
+ - `purging` - The process is purging the room and event data from database.
+ - `complete` - The process has completed successfully.
+ - `failed` - The process is aborted, an error has occurred.
+ - `error` - A string that shows an error message if `status` is `failed`.
+ Otherwise this field is hidden.
+ - `shutdown_room` - An object containing information about the result of shutting down the room.
+ *Note:* The result is shown after removing the room members.
+ The delete process can still be running. Please pay attention to the `status`.
+ - `kicked_users` - An array of users (`user_id`) that were kicked.
+ - `failed_to_kick_users` - An array of users (`user_id`) that that were not kicked.
+ - `local_aliases` - An array of strings representing the local aliases that were
+ migrated from the old room to the new.
+ - `new_room_id` - A string representing the room ID of the new room, or `null` if
+ no such room was created.
+
+## Undoing room deletions
+
+*Note*: This guide may be outdated by the time you read it. By nature of room deletions being performed at the database level,
+the structure can and does change without notice.
+
+First, it's important to understand that a room deletion is very destructive. Undoing a deletion is not as simple as pretending it
+never happened - work has to be done to move forward instead of resetting the past. In fact, in some cases it might not be possible
+to recover at all:
+
+* If the room was invite-only, your users will need to be re-invited.
+* If the room no longer has any members at all, it'll be impossible to rejoin.
+* The first user to rejoin will have to do so via an alias on a different
+ server (or receive an invite from a user on a different server).
+
+With all that being said, if you still want to try and recover the room:
+
+1. If the room was `block`ed, you must unblock it on your server. This can be
+ accomplished as follows:
+
+ 1. For safety reasons, shut down Synapse.
+ 2. In the database, run `DELETE FROM blocked_rooms WHERE room_id = '!example:example.org';`
+ * For caution: it's recommended to run this in a transaction: `BEGIN; DELETE ...;`, verify you got 1 result, then `COMMIT;`.
+ * The room ID is the same one supplied to the delete room API, not the Content Violation room.
+ 3. Restart Synapse.
+
+ This step is unnecessary if `block` was not set.
+
+2. Any room aliases on your server that pointed to the deleted room may have
+ been deleted, or redirected to the Content Violation room. These will need
+ to be restored manually.
+
+3. Users on your server that were in the deleted room will have been kicked
+ from the room. Consider whether you want to update their membership
+ (possibly via the [Edit Room Membership API](room_membership.md)) or let
+ them handle rejoining themselves.
+
+4. If `new_room_user_id` was given, a 'Content Violation' will have been
+ created. Consider whether you want to delete that roomm.
+
+# Make Room Admin API
+
+Grants another user the highest power available to a local user who is in the room.
+If the user is not in the room, and it is not publicly joinable, then invite the user.
+
+By default the server admin (the caller) is granted power, but another user can
+optionally be specified, e.g.:
+
+```
+POST /_synapse/admin/v1/rooms/<room_id_or_alias>/make_room_admin
+{
+ "user_id": "@foo:example.com"
+}
+```
+
+# Forward Extremities Admin API
+
+Enables querying and deleting forward extremities from rooms. When a lot of forward
+extremities accumulate in a room, performance can become degraded. For details, see
+[#1760](https://github.com/matrix-org/synapse/issues/1760).
+
+## Check for forward extremities
+
+To check the status of forward extremities for a room:
+
+```
+GET /_synapse/admin/v1/rooms/<room_id_or_alias>/forward_extremities
+```
+
+A response as follows will be returned:
+
+```json
+{
+ "count": 1,
+ "results": [
+ {
+ "event_id": "$M5SP266vsnxctfwFgFLNceaCo3ujhRtg_NiiHabcdefgh",
+ "state_group": 439,
+ "depth": 123,
+ "received_ts": 1611263016761
+ }
+ ]
+}
+```
+
+## Deleting forward extremities
+
+**WARNING**: Please ensure you know what you're doing and have read
+the related issue [#1760](https://github.com/matrix-org/synapse/issues/1760).
+Under no situations should this API be executed as an automated maintenance task!
+
+If a room has lots of forward extremities, the extra can be
+deleted as follows:
+
+```
+DELETE /_synapse/admin/v1/rooms/<room_id_or_alias>/forward_extremities
+```
+
+A response as follows will be returned, indicating the amount of forward extremities
+that were deleted.
+
+```json
+{
+ "deleted": 1
+}
+```
+
+# Event Context API
+
+This API lets a client find the context of an event. This is designed primarily to investigate abuse reports.
+
+```
+GET /_synapse/admin/v1/rooms/<room_id>/context/<event_id>
+```
+
+This API mimmicks [GET /_matrix/client/r0/rooms/{roomId}/context/{eventId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-context-eventid). Please refer to the link for all details on parameters and reseponse.
+
+Example response:
+
+```json
+{
+ "end": "t29-57_2_0_2",
+ "events_after": [
+ {
+ "content": {
+ "body": "This is an example text message",
+ "msgtype": "m.text",
+ "format": "org.matrix.custom.html",
+ "formatted_body": "<b>This is an example text message</b>"
+ },
+ "type": "m.room.message",
+ "event_id": "$143273582443PhrSn:example.org",
+ "room_id": "!636q39766251:example.com",
+ "sender": "@example:example.org",
+ "origin_server_ts": 1432735824653,
+ "unsigned": {
+ "age": 1234
+ }
+ }
+ ],
+ "event": {
+ "content": {
+ "body": "filename.jpg",
+ "info": {
+ "h": 398,
+ "w": 394,
+ "mimetype": "image/jpeg",
+ "size": 31037
+ },
+ "url": "mxc://example.org/JWEIFJgwEIhweiWJE",
+ "msgtype": "m.image"
+ },
+ "type": "m.room.message",
+ "event_id": "$f3h4d129462ha:example.com",
+ "room_id": "!636q39766251:example.com",
+ "sender": "@example:example.org",
+ "origin_server_ts": 1432735824653,
+ "unsigned": {
+ "age": 1234
+ }
+ },
+ "events_before": [
+ {
+ "content": {
+ "body": "something-important.doc",
+ "filename": "something-important.doc",
+ "info": {
+ "mimetype": "application/msword",
+ "size": 46144
+ },
+ "msgtype": "m.file",
+ "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe"
+ },
+ "type": "m.room.message",
+ "event_id": "$143273582443PhrSn:example.org",
+ "room_id": "!636q39766251:example.com",
+ "sender": "@example:example.org",
+ "origin_server_ts": 1432735824653,
+ "unsigned": {
+ "age": 1234
+ }
+ }
+ ],
+ "start": "t27-54_2_0_2",
+ "state": [
+ {
+ "content": {
+ "creator": "@example:example.org",
+ "room_version": "1",
+ "m.federate": true,
+ "predecessor": {
+ "event_id": "$something:example.org",
+ "room_id": "!oldroom:example.org"
+ }
+ },
+ "type": "m.room.create",
+ "event_id": "$143273582443PhrSn:example.org",
+ "room_id": "!636q39766251:example.com",
+ "sender": "@example:example.org",
+ "origin_server_ts": 1432735824653,
+ "unsigned": {
+ "age": 1234
+ },
+ "state_key": ""
+ },
+ {
+ "content": {
+ "membership": "join",
+ "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
+ "displayname": "Alice Margatroid"
+ },
+ "type": "m.room.member",
+ "event_id": "$143273582443PhrSn:example.org",
+ "room_id": "!636q39766251:example.com",
+ "sender": "@example:example.org",
+ "origin_server_ts": 1432735824653,
+ "unsigned": {
+ "age": 1234
+ },
+ "state_key": "@alice:example.org"
+ }
+ ]
+}
+```
diff --git a/docs/administration/admin_api/server_notices.md b/docs/administration/admin_api/server_notices.md
new file mode 100644
index 0000000000..5e2527a38b
--- /dev/null
+++ b/docs/administration/admin_api/server_notices.md
@@ -0,0 +1,48 @@
+# Server Notices
+
+The API to send notices is as follows:
+
+```
+POST /_synapse/admin/v1/send_server_notice
+```
+
+or:
+
+```
+PUT /_synapse/admin/v1/send_server_notice/{txnId}
+```
+
+You will need to authenticate with an access token for an admin user.
+
+When using the `PUT` form, retransmissions with the same transaction ID will be
+ignored in the same way as with `PUT
+/_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}`.
+
+The request body should look something like the following:
+
+```json
+{
+ "user_id": "@target_user:server_name",
+ "content": {
+ "msgtype": "m.text",
+ "body": "This is my message"
+ }
+}
+```
+
+You can optionally include the following additional parameters:
+
+* `type`: the type of event. Defaults to `m.room.message`.
+* `state_key`: Setting this will result in a state event being sent.
+
+
+Once the notice has been sent, the API will return the following response:
+
+```json
+{
+ "event_id": "<event_id>"
+}
+```
+
+Note that server notices must be enabled in `homeserver.yaml` before this API
+can be used. See [the server notices documentation](../../usage/configuration/server_notices.md) for more information.
diff --git a/docs/administration/admin_api/statistics.md b/docs/administration/admin_api/statistics.md
new file mode 100644
index 0000000000..a26c76f9f3
--- /dev/null
+++ b/docs/administration/admin_api/statistics.md
@@ -0,0 +1,83 @@
+# Users' media usage statistics
+
+Returns information about all local media usage of users. Gives the
+possibility to filter them by time and user.
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+The API is:
+
+```
+GET /_synapse/admin/v1/statistics/users/media
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "users": [
+ {
+ "displayname": "foo_user_0",
+ "media_count": 2,
+ "media_length": 134,
+ "user_id": "@foo_user_0:test"
+ },
+ {
+ "displayname": "foo_user_1",
+ "media_count": 2,
+ "media_length": 134,
+ "user_id": "@foo_user_1:test"
+ }
+ ],
+ "next_token": 3,
+ "total": 10
+}
+```
+
+To paginate, check for `next_token` and if present, call the endpoint
+again with `from` set to the value of `next_token`. This will return a new page.
+
+If the endpoint does not return a `next_token` then there are no more
+reports to paginate through.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+* `limit`: string representing a positive integer - Is optional but is
+ used for pagination, denoting the maximum number of items to return
+ in this call. Defaults to `100`.
+* `from`: string representing a positive integer - Is optional but used for pagination,
+ denoting the offset in the returned results. This should be treated as an opaque value
+ and not explicitly set to anything other than the return value of `next_token` from a
+ previous call. Defaults to `0`.
+* `order_by` - string - The method in which to sort the returned list of users. Valid values are:
+ - `user_id` - Users are ordered alphabetically by `user_id`. This is the default.
+ - `displayname` - Users are ordered alphabetically by `displayname`.
+ - `media_length` - Users are ordered by the total size of uploaded media in bytes.
+ Smallest to largest.
+ - `media_count` - Users are ordered by number of uploaded media. Smallest to largest.
+* `from_ts` - string representing a positive integer - Considers only
+ files created at this timestamp or later. Unix timestamp in ms.
+* `until_ts` - string representing a positive integer - Considers only
+ files created at this timestamp or earlier. Unix timestamp in ms.
+* `search_term` - string - Filter users by their user ID localpart **or** displayname.
+ The search term can be found in any part of the string.
+ Defaults to no filtering.
+* `dir` - string - Direction of order. Either `f` for forwards or `b` for backwards.
+ Setting this value to `b` will reverse the above sort order. Defaults to `f`.
+
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+* `users` - An array of objects, each containing information
+ about the user and their local media. Objects contain the following fields:
+ - `displayname` - string - Displayname of this user.
+ - `media_count` - integer - Number of uploaded media by this user.
+ - `media_length` - integer - Size of uploaded media in bytes by this user.
+ - `user_id` - string - Fully-qualified user ID (ex. `@user:server.com`).
+* `next_token` - integer - Opaque value used for pagination. See above.
+* `total` - integer - Total number of users after filtering.
diff --git a/docs/administration/admin_api/user_admin_api.md b/docs/administration/admin_api/user_admin_api.md
new file mode 100644
index 0000000000..cedcd1c294
--- /dev/null
+++ b/docs/administration/admin_api/user_admin_api.md
@@ -0,0 +1,1199 @@
+# User Admin API
+
+To use it, you will need to authenticate by providing an `access_token`
+for a server admin: see [Admin API](../usage/administration/admin_api).
+
+## Query User Account
+
+This API returns information about a specific user account.
+
+The api is:
+
+```
+GET /_synapse/admin/v2/users/<user_id>
+```
+
+It returns a JSON body like the following:
+
+```jsonc
+{
+ "name": "@user:example.com",
+ "displayname": "User", // can be null if not set
+ "threepids": [
+ {
+ "medium": "email",
+ "address": "<user_mail_1>",
+ "added_at": 1586458409743,
+ "validated_at": 1586458409743
+ },
+ {
+ "medium": "email",
+ "address": "<user_mail_2>",
+ "added_at": 1586458409743,
+ "validated_at": 1586458409743
+ }
+ ],
+ "avatar_url": "<avatar_url>", // can be null if not set
+ "is_guest": 0,
+ "admin": 0,
+ "deactivated": 0,
+ "erased": false,
+ "shadow_banned": 0,
+ "creation_ts": 1560432506,
+ "appservice_id": null,
+ "consent_server_notice_sent": null,
+ "consent_version": null,
+ "consent_ts": null,
+ "external_ids": [
+ {
+ "auth_provider": "<provider1>",
+ "external_id": "<user_id_provider_1>"
+ },
+ {
+ "auth_provider": "<provider2>",
+ "external_id": "<user_id_provider_2>"
+ }
+ ],
+ "user_type": null
+}
+```
+
+URL parameters:
+
+- `user_id`: fully-qualified user id: for example, `@user:server.com`.
+
+## Create or modify Account
+
+This API allows an administrator to create or modify a user account with a
+specific `user_id`.
+
+This api is:
+
+```
+PUT /_synapse/admin/v2/users/<user_id>
+```
+
+with a body of:
+
+```json
+{
+ "password": "user_password",
+ "displayname": "User",
+ "threepids": [
+ {
+ "medium": "email",
+ "address": "<user_mail_1>"
+ },
+ {
+ "medium": "email",
+ "address": "<user_mail_2>"
+ }
+ ],
+ "external_ids": [
+ {
+ "auth_provider": "<provider1>",
+ "external_id": "<user_id_provider_1>"
+ },
+ {
+ "auth_provider": "<provider2>",
+ "external_id": "<user_id_provider_2>"
+ }
+ ],
+ "avatar_url": "<avatar_url>",
+ "admin": false,
+ "deactivated": false,
+ "user_type": null
+}
+```
+
+Returns HTTP status code:
+- `201` - When a new user object was created.
+- `200` - When a user was modified.
+
+URL parameters:
+
+- `user_id`: fully-qualified user id: for example, `@user:server.com`.
+
+Body parameters:
+
+- `password` - string, optional. If provided, the user's password is updated and all
+ devices are logged out, unless `logout_devices` is set to `false`.
+- `logout_devices` - bool, optional, defaults to `true`. If set to false, devices aren't
+ logged out even when `password` is provided.
+- `displayname` - string, optional, defaults to the value of `user_id`.
+- `threepids` - array, optional, allows setting the third-party IDs (email, msisdn)
+ - `medium` - string. Kind of third-party ID, either `email` or `msisdn`.
+ - `address` - string. Value of third-party ID.
+ belonging to a user.
+- `external_ids` - array, optional. Allow setting the identifier of the external identity
+ provider for SSO (Single sign-on). Details in the configuration manual under the
+ sections [sso](../../usage/configuration/config_documentation.md#sso) and [oidc_providers](../../usage/configuration/config_documentation.md#oidc_providers).
+ - `auth_provider` - string. ID of the external identity provider. Value of `idp_id`
+ in the homeserver configuration. Note that no error is raised if the provided
+ value is not in the homeserver configuration.
+ - `external_id` - string, user ID in the external identity provider.
+- `avatar_url` - string, optional, must be a
+ [MXC URI](https://matrix.org/docs/spec/client_server/r0.6.0#matrix-content-mxc-uris).
+- `admin` - bool, optional, defaults to `false`.
+- `deactivated` - bool, optional. If unspecified, deactivation state will be left
+ unchanged on existing accounts and set to `false` for new accounts.
+ A user cannot be erased by deactivating with this API. For details on
+ deactivating users see [Deactivate Account](#deactivate-account).
+- `user_type` - string or null, optional. If provided, the user type will be
+ adjusted. If `null` given, the user type will be cleared. Other
+ allowed options are: `bot` and `support`.
+
+If the user already exists then optional parameters default to the current value.
+
+In order to re-activate an account `deactivated` must be set to `false`. If
+users do not login via single-sign-on, a new `password` must be provided.
+
+## List Accounts
+
+This API returns all local user accounts.
+By default, the response is ordered by ascending user ID.
+
+```
+GET /_synapse/admin/v2/users?from=0&limit=10&guests=false
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "users": [
+ {
+ "name": "<user_id1>",
+ "is_guest": 0,
+ "admin": 0,
+ "user_type": null,
+ "deactivated": 0,
+ "erased": false,
+ "shadow_banned": 0,
+ "displayname": "<User One>",
+ "avatar_url": null,
+ "creation_ts": 1560432668000
+ }, {
+ "name": "<user_id2>",
+ "is_guest": 0,
+ "admin": 1,
+ "user_type": null,
+ "deactivated": 0,
+ "erased": false,
+ "shadow_banned": 0,
+ "displayname": "<User Two>",
+ "avatar_url": "<avatar_url>",
+ "creation_ts": 1561550621000
+ }
+ ],
+ "next_token": "100",
+ "total": 200
+}
+```
+
+To paginate, check for `next_token` and if present, call the endpoint again
+with `from` set to the value of `next_token`. This will return a new page.
+
+If the endpoint does not return a `next_token` then there are no more users
+to paginate through.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - Is optional and filters to only return users with user IDs
+ that contain this value. This parameter is ignored when using the `name` parameter.
+- `name` - Is optional and filters to only return users with user ID localparts
+ **or** displaynames that contain this value.
+- `guests` - string representing a bool - Is optional and if `false` will **exclude** guest users.
+ Defaults to `true` to include guest users.
+- `deactivated` - string representing a bool - Is optional and if `true` will **include** deactivated users.
+ Defaults to `false` to exclude deactivated users.
+- `limit` - string representing a positive integer - Is optional but is used for pagination,
+ denoting the maximum number of items to return in this call. Defaults to `100`.
+- `from` - string representing a positive integer - Is optional but used for pagination,
+ denoting the offset in the returned results. This should be treated as an opaque value and
+ not explicitly set to anything other than the return value of `next_token` from a previous call.
+ Defaults to `0`.
+- `order_by` - The method by which to sort the returned list of users.
+ If the ordered field has duplicates, the second order is always by ascending `name`,
+ which guarantees a stable ordering. Valid values are:
+
+ - `name` - Users are ordered alphabetically by `name`. This is the default.
+ - `is_guest` - Users are ordered by `is_guest` status.
+ - `admin` - Users are ordered by `admin` status.
+ - `user_type` - Users are ordered alphabetically by `user_type`.
+ - `deactivated` - Users are ordered by `deactivated` status.
+ - `shadow_banned` - Users are ordered by `shadow_banned` status.
+ - `displayname` - Users are ordered alphabetically by `displayname`.
+ - `avatar_url` - Users are ordered alphabetically by avatar URL.
+ - `creation_ts` - Users are ordered by when the users was created in ms.
+
+- `dir` - Direction of media order. Either `f` for forwards or `b` for backwards.
+ Setting this value to `b` will reverse the above sort order. Defaults to `f`.
+
+Caution. The database only has indexes on the columns `name` and `creation_ts`.
+This means that if a different sort order is used (`is_guest`, `admin`,
+`user_type`, `deactivated`, `shadow_banned`, `avatar_url` or `displayname`),
+this can cause a large load on the database, especially for large environments.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `users` - An array of objects, each containing information about an user.
+ User objects contain the following fields:
+
+ - `name` - string - Fully-qualified user ID (ex. `@user:server.com`).
+ - `is_guest` - bool - Status if that user is a guest account.
+ - `admin` - bool - Status if that user is a server administrator.
+ - `user_type` - string - Type of the user. Normal users are type `None`.
+ This allows user type specific behaviour. There are also types `support` and `bot`.
+ - `deactivated` - bool - Status if that user has been marked as deactivated.
+ - `erased` - bool - Status if that user has been marked as erased.
+ - `shadow_banned` - bool - Status if that user has been marked as shadow banned.
+ - `displayname` - string - The user's display name if they have set one.
+ - `avatar_url` - string - The user's avatar URL if they have set one.
+ - `creation_ts` - integer - The user's creation timestamp in ms.
+
+- `next_token`: string representing a positive integer - Indication for pagination. See above.
+- `total` - integer - Total number of media.
+
+
+## Query current sessions for a user
+
+This API returns information about the active sessions for a specific user.
+
+The endpoints are:
+
+```
+GET /_synapse/admin/v1/whois/<user_id>
+```
+
+and:
+
+```
+GET /_matrix/client/r0/admin/whois/<userId>
+```
+
+See also: [Client Server
+API Whois](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-admin-whois-userid).
+
+It returns a JSON body like the following:
+
+```json
+{
+ "user_id": "<user_id>",
+ "devices": {
+ "": {
+ "sessions": [
+ {
+ "connections": [
+ {
+ "ip": "1.2.3.4",
+ "last_seen": 1417222374433,
+ "user_agent": "Mozilla/5.0 ..."
+ },
+ {
+ "ip": "1.2.3.10",
+ "last_seen": 1417222374500,
+ "user_agent": "Dalvik/2.1.0 ..."
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
+```
+
+`last_seen` is measured in milliseconds since the Unix epoch.
+
+## Deactivate Account
+
+This API deactivates an account. It removes active access tokens, resets the
+password, and deletes third-party IDs (to prevent the user requesting a
+password reset).
+
+It can also mark the user as GDPR-erased. This means messages sent by the
+user will still be visible by anyone that was in the room when these messages
+were sent, but hidden from users joining the room afterwards.
+
+The api is:
+
+```
+POST /_synapse/admin/v1/deactivate/<user_id>
+```
+
+with a body of:
+
+```json
+{
+ "erase": true
+}
+```
+
+The erase parameter is optional and defaults to `false`.
+An empty body may be passed for backwards compatibility.
+
+The following actions are performed when deactivating an user:
+
+- Try to unbind 3PIDs from the identity server
+- Remove all 3PIDs from the homeserver
+- Delete all devices and E2EE keys
+- Delete all access tokens
+- Delete all pushers
+- Delete the password hash
+- Removal from all rooms the user is a member of
+- Remove the user from the user directory
+- Reject all pending invites
+- Remove all account validity information related to the user
+- Remove the arbitrary data store known as *account data*. For example, this includes:
+ - list of ignored users;
+ - push rules;
+ - secret storage keys; and
+ - cross-signing keys.
+
+The following additional actions are performed during deactivation if `erase`
+is set to `true`:
+
+- Remove the user's display name
+- Remove the user's avatar URL
+- Mark the user as erased
+
+The following actions are **NOT** performed. The list may be incomplete.
+
+- Remove mappings of SSO IDs
+- [Delete media uploaded](#delete-media-uploaded-by-a-user) by user (included avatar images)
+- Delete sent and received messages
+- Remove the user's creation (registration) timestamp
+- [Remove rate limit overrides](#override-ratelimiting-for-users)
+- Remove from monthly active users
+- Remove user's consent information (consent version and timestamp)
+
+## Reset password
+
+Changes the password of another user. This will automatically log the user out of all their devices.
+
+The api is:
+
+```
+POST /_synapse/admin/v1/reset_password/<user_id>
+```
+
+with a body of:
+
+```json
+{
+ "new_password": "<secret>",
+ "logout_devices": true
+}
+```
+
+The parameter `new_password` is required.
+The parameter `logout_devices` is optional and defaults to `true`.
+
+
+## Get whether a user is a server administrator or not
+
+The api is:
+
+```
+GET /_synapse/admin/v1/users/<user_id>/admin
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "admin": true
+}
+```
+
+
+## Change whether a user is a server administrator or not
+
+Note that you cannot demote yourself.
+
+The api is:
+
+```
+PUT /_synapse/admin/v1/users/<user_id>/admin
+```
+
+with a body of:
+
+```json
+{
+ "admin": true
+}
+```
+
+## List room memberships of a user
+
+Gets a list of all `room_id` that a specific `user_id` is member.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/users/<user_id>/joined_rooms
+```
+
+A response body like the following is returned:
+
+```json
+ {
+ "joined_rooms": [
+ "!DuGcnbhHGaSZQoNQR:matrix.org",
+ "!ZtSaPCawyWtxfWiIy:matrix.org"
+ ],
+ "total": 2
+ }
+```
+
+The server returns the list of rooms of which the user and the server
+are member. If the user is local, all the rooms of which the user is
+member are returned.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - fully qualified: for example, `@user:server.com`.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `joined_rooms` - An array of `room_id`.
+- `total` - Number of rooms.
+
+## Account Data
+Gets information about account data for a specific `user_id`.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/users/<user_id>/accountdata
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "account_data": {
+ "global": {
+ "m.secret_storage.key.LmIGHTg5W": {
+ "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
+ "iv": "fwjNZatxg==",
+ "mac": "eWh9kNnLWZUNOgnc="
+ },
+ "im.vector.hide_profile": {
+ "hide_profile": true
+ },
+ "org.matrix.preview_urls": {
+ "disable": false
+ },
+ "im.vector.riot.breadcrumb_rooms": {
+ "rooms": [
+ "!LxcBDAsDUVAfJDEo:matrix.org",
+ "!MAhRxqasbItjOqxu:matrix.org"
+ ]
+ },
+ "m.accepted_terms": {
+ "accepted": [
+ "https://example.org/somewhere/privacy-1.2-en.html",
+ "https://example.org/somewhere/terms-2.0-en.html"
+ ]
+ },
+ "im.vector.setting.breadcrumbs": {
+ "recent_rooms": [
+ "!MAhRxqasbItqxuEt:matrix.org",
+ "!ZtSaPCawyWtxiImy:matrix.org"
+ ]
+ }
+ },
+ "rooms": {
+ "!GUdfZSHUJibpiVqHYd:matrix.org": {
+ "m.fully_read": {
+ "event_id": "$156334540fYIhZ:matrix.org"
+ }
+ },
+ "!tOZwOOiqwCYQkLhV:matrix.org": {
+ "m.fully_read": {
+ "event_id": "$xjsIyp4_NaVl2yPvIZs_k1Jl8tsC_Sp23wjqXPno"
+ }
+ }
+ }
+ }
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - fully qualified: for example, `@user:server.com`.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `account_data` - A map containing the account data for the user
+ - `global` - A map containing the global account data for the user
+ - `rooms` - A map containing the account data per room for the user
+
+## User media
+
+### List media uploaded by a user
+Gets a list of all local media that a specific `user_id` has created.
+These are media that the user has uploaded themselves
+([local media](../../development/internal_documentation/media_repository.md#local-media)), as well as
+[URL preview images](../../development/internal_documentation/media_repository.md#url-previews) requested by the user if the
+[feature is enabled](../../usage/configuration/config_documentation.md#url_preview_enabled).
+
+By default, the response is ordered by descending creation date and ascending media ID.
+The newest media is on top. You can change the order with parameters
+`order_by` and `dir`.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/users/<user_id>/media
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "media": [
+ {
+ "created_ts": 100400,
+ "last_access_ts": null,
+ "media_id": "qXhyRzulkwLsNHTbpHreuEgo",
+ "media_length": 67,
+ "media_type": "image/png",
+ "quarantined_by": null,
+ "safe_from_quarantine": false,
+ "upload_name": "test1.png"
+ },
+ {
+ "created_ts": 200400,
+ "last_access_ts": null,
+ "media_id": "FHfiSnzoINDatrXHQIXBtahw",
+ "media_length": 67,
+ "media_type": "image/png",
+ "quarantined_by": null,
+ "safe_from_quarantine": false,
+ "upload_name": "test2.png"
+ }
+ ],
+ "next_token": 3,
+ "total": 2
+}
+```
+
+To paginate, check for `next_token` and if present, call the endpoint again
+with `from` set to the value of `next_token`. This will return a new page.
+
+If the endpoint does not return a `next_token` then there are no more
+reports to paginate through.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - string - fully qualified: for example, `@user:server.com`.
+- `limit`: string representing a positive integer - Is optional but is used for pagination,
+ denoting the maximum number of items to return in this call. Defaults to `100`.
+- `from`: string representing a positive integer - Is optional but used for pagination,
+ denoting the offset in the returned results. This should be treated as an opaque value and
+ not explicitly set to anything other than the return value of `next_token` from a previous call.
+ Defaults to `0`.
+- `order_by` - The method by which to sort the returned list of media.
+ If the ordered field has duplicates, the second order is always by ascending `media_id`,
+ which guarantees a stable ordering. Valid values are:
+
+ - `media_id` - Media are ordered alphabetically by `media_id`.
+ - `upload_name` - Media are ordered alphabetically by name the media was uploaded with.
+ - `created_ts` - Media are ordered by when the content was uploaded in ms.
+ Smallest to largest. This is the default.
+ - `last_access_ts` - Media are ordered by when the content was last accessed in ms.
+ Smallest to largest.
+ - `media_length` - Media are ordered by length of the media in bytes.
+ Smallest to largest.
+ - `media_type` - Media are ordered alphabetically by MIME-type.
+ - `quarantined_by` - Media are ordered alphabetically by the user ID that
+ initiated the quarantine request for this media.
+ - `safe_from_quarantine` - Media are ordered by the status if this media is safe
+ from quarantining.
+
+- `dir` - Direction of media order. Either `f` for forwards or `b` for backwards.
+ Setting this value to `b` will reverse the above sort order. Defaults to `f`.
+
+If neither `order_by` nor `dir` is set, the default order is newest media on top
+(corresponds to `order_by` = `created_ts` and `dir` = `b`).
+
+Caution. The database only has indexes on the columns `media_id`,
+`user_id` and `created_ts`. This means that if a different sort order is used
+(`upload_name`, `last_access_ts`, `media_length`, `media_type`,
+`quarantined_by` or `safe_from_quarantine`), this can cause a large load on the
+database, especially for large environments.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `media` - An array of objects, each containing information about a media.
+ Media objects contain the following fields:
+ - `created_ts` - integer - Timestamp when the content was uploaded in ms.
+ - `last_access_ts` - integer - Timestamp when the content was last accessed in ms.
+ - `media_id` - string - The id used to refer to the media. Details about the format
+ are documented under
+ [media repository](../../development/internal_documentation/media_repository.md).
+ - `media_length` - integer - Length of the media in bytes.
+ - `media_type` - string - The MIME-type of the media.
+ - `quarantined_by` - string - The user ID that initiated the quarantine request
+ for this media.
+ - `safe_from_quarantine` - bool - Status if this media is safe from quarantining.
+ - `upload_name` - string - The name the media was uploaded with.
+- `next_token`: integer - Indication for pagination. See above.
+- `total` - integer - Total number of media.
+
+### Delete media uploaded by a user
+
+This API deletes the *local* media from the disk of your own server
+that a specific `user_id` has created. This includes any local thumbnails.
+
+This API will not affect media that has been uploaded to external
+media repositories (e.g https://github.com/turt2live/matrix-media-repo/).
+
+By default, the API deletes media ordered by descending creation date and ascending media ID.
+The newest media is deleted first. You can change the order with parameters
+`order_by` and `dir`. If no `limit` is set the API deletes `100` files per request.
+
+The API is:
+
+```
+DELETE /_synapse/admin/v1/users/<user_id>/media
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "deleted_media": [
+ "abcdefghijklmnopqrstuvwx"
+ ],
+ "total": 1
+}
+```
+
+The following fields are returned in the JSON response body:
+
+* `deleted_media`: an array of strings - List of deleted `media_id`
+* `total`: integer - Total number of deleted `media_id`
+
+**Note**: There is no `next_token`. This is not useful for deleting media, because
+after deleting media the remaining media have a new order.
+
+**Parameters**
+
+This API has the same parameters as
+[List media uploaded by a user](#list-media-uploaded-by-a-user).
+With the parameters you can for example limit the number of files to delete at once or
+delete largest/smallest or newest/oldest files first.
+
+## Login as a user
+
+Get an access token that can be used to authenticate as that user. Useful for
+when admins wish to do actions on behalf of a user.
+
+The API is:
+
+```
+POST /_synapse/admin/v1/users/<user_id>/login
+{}
+```
+
+An optional `valid_until_ms` field can be specified in the request body as an
+integer timestamp that specifies when the token should expire. By default tokens
+do not expire.
+
+A response body like the following is returned:
+
+```json
+{
+ "access_token": "<opaque_access_token_string>"
+}
+```
+
+This API does *not* generate a new device for the user, and so will not appear
+their `/devices` list, and in general the target user should not be able to
+tell they have been logged in as.
+
+To expire the token call the standard `/logout` API with the token.
+
+Note: The token will expire if the *admin* user calls `/logout/all` from any
+of their devices, but the token will *not* expire if the target user does the
+same.
+
+
+## User devices
+
+### List all devices
+Gets information about all devices for a specific `user_id`.
+
+The API is:
+
+```
+GET /_synapse/admin/v2/users/<user_id>/devices
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "devices": [
+ {
+ "device_id": "QBUAZIFURK",
+ "display_name": "android",
+ "last_seen_ip": "1.2.3.4",
+ "last_seen_user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0",
+ "last_seen_ts": 1474491775024,
+ "user_id": "<user_id>"
+ },
+ {
+ "device_id": "AUIECTSRND",
+ "display_name": "ios",
+ "last_seen_ip": "1.2.3.5",
+ "last_seen_user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0",
+ "last_seen_ts": 1474491775025,
+ "user_id": "<user_id>"
+ }
+ ],
+ "total": 2
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - fully qualified: for example, `@user:server.com`.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `devices` - An array of objects, each containing information about a device.
+ Device objects contain the following fields:
+
+ - `device_id` - Identifier of device.
+ - `display_name` - Display name set by the user for this device.
+ Absent if no name has been set.
+ - `last_seen_ip` - The IP address where this device was last seen.
+ (May be a few minutes out of date, for efficiency reasons).
+ - `last_seen_user_agent` - The user agent of the device when it was last seen.
+ (May be a few minutes out of date, for efficiency reasons).
+ - `last_seen_ts` - The timestamp (in milliseconds since the unix epoch) when this
+ devices was last seen. (May be a few minutes out of date, for efficiency reasons).
+ - `user_id` - Owner of device.
+
+- `total` - Total number of user's devices.
+
+### Delete multiple devices
+Deletes the given devices for a specific `user_id`, and invalidates
+any access token associated with them.
+
+The API is:
+
+```
+POST /_synapse/admin/v2/users/<user_id>/delete_devices
+
+{
+ "devices": [
+ "QBUAZIFURK",
+ "AUIECTSRND"
+ ]
+}
+```
+
+An empty JSON dict is returned.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - fully qualified: for example, `@user:server.com`.
+
+The following fields are required in the JSON request body:
+
+- `devices` - The list of device IDs to delete.
+
+### Show a device
+Gets information on a single device, by `device_id` for a specific `user_id`.
+
+The API is:
+
+```
+GET /_synapse/admin/v2/users/<user_id>/devices/<device_id>
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "device_id": "<device_id>",
+ "display_name": "android",
+ "last_seen_ip": "1.2.3.4",
+ "last_seen_user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0",
+ "last_seen_ts": 1474491775024,
+ "user_id": "<user_id>"
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - fully qualified: for example, `@user:server.com`.
+- `device_id` - The device to retrieve.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `device_id` - Identifier of device.
+- `display_name` - Display name set by the user for this device.
+ Absent if no name has been set.
+- `last_seen_ip` - The IP address where this device was last seen.
+ (May be a few minutes out of date, for efficiency reasons).
+ - `last_seen_user_agent` - The user agent of the device when it was last seen.
+ (May be a few minutes out of date, for efficiency reasons).
+- `last_seen_ts` - The timestamp (in milliseconds since the unix epoch) when this
+ devices was last seen. (May be a few minutes out of date, for efficiency reasons).
+- `user_id` - Owner of device.
+
+### Update a device
+Updates the metadata on the given `device_id` for a specific `user_id`.
+
+The API is:
+
+```
+PUT /_synapse/admin/v2/users/<user_id>/devices/<device_id>
+
+{
+ "display_name": "My other phone"
+}
+```
+
+An empty JSON dict is returned.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - fully qualified: for example, `@user:server.com`.
+- `device_id` - The device to update.
+
+The following fields are required in the JSON request body:
+
+- `display_name` - The new display name for this device. If not given,
+ the display name is unchanged.
+
+### Delete a device
+Deletes the given `device_id` for a specific `user_id`,
+and invalidates any access token associated with it.
+
+The API is:
+
+```
+DELETE /_synapse/admin/v2/users/<user_id>/devices/<device_id>
+
+{}
+```
+
+An empty JSON dict is returned.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - fully qualified: for example, `@user:server.com`.
+- `device_id` - The device to delete.
+
+## List all pushers
+Gets information about all pushers for a specific `user_id`.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/users/<user_id>/pushers
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "pushers": [
+ {
+ "app_display_name":"HTTP Push Notifications",
+ "app_id":"m.http",
+ "data": {
+ "url":"example.com"
+ },
+ "device_display_name":"pushy push",
+ "kind":"http",
+ "lang":"None",
+ "profile_tag":"",
+ "pushkey":"a@example.com"
+ }
+ ],
+ "total": 1
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - fully qualified: for example, `@user:server.com`.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `pushers` - An array containing the current pushers for the user
+
+ - `app_display_name` - string - A string that will allow the user to identify
+ what application owns this pusher.
+
+ - `app_id` - string - This is a reverse-DNS style identifier for the application.
+ Max length, 64 chars.
+
+ - `data` - A dictionary of information for the pusher implementation itself.
+
+ - `url` - string - Required if `kind` is `http`. The URL to use to send
+ notifications to.
+
+ - `format` - string - The format to use when sending notifications to the
+ Push Gateway.
+
+ - `device_display_name` - string - A string that will allow the user to identify
+ what device owns this pusher.
+
+ - `profile_tag` - string - This string determines which set of device specific rules
+ this pusher executes.
+
+ - `kind` - string - The kind of pusher. "http" is a pusher that sends HTTP pokes.
+ - `lang` - string - The preferred language for receiving notifications
+ (e.g. 'en' or 'en-US')
+
+ - `profile_tag` - string - This string determines which set of device specific rules
+ this pusher executes.
+
+ - `pushkey` - string - This is a unique identifier for this pusher.
+ Max length, 512 bytes.
+
+- `total` - integer - Number of pushers.
+
+See also the
+[Client-Server API Spec on pushers](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushers).
+
+## Controlling whether a user is shadow-banned
+
+Shadow-banning is a useful tool for moderating malicious or egregiously abusive users.
+A shadow-banned users receives successful responses to their client-server API requests,
+but the events are not propagated into rooms. This can be an effective tool as it
+(hopefully) takes longer for the user to realise they are being moderated before
+pivoting to another account.
+
+Shadow-banning a user should be used as a tool of last resort and may lead to confusing
+or broken behaviour for the client. A shadow-banned user will not receive any
+notification and it is generally more appropriate to ban or kick abusive users.
+A shadow-banned user will be unable to contact anyone on the server.
+
+To shadow-ban a user the API is:
+
+```
+POST /_synapse/admin/v1/users/<user_id>/shadow_ban
+```
+
+To un-shadow-ban a user the API is:
+
+```
+DELETE /_synapse/admin/v1/users/<user_id>/shadow_ban
+```
+
+An empty JSON dict is returned in both cases.
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - The fully qualified MXID: for example, `@user:server.com`. The user must
+ be local.
+
+## Override ratelimiting for users
+
+This API allows to override or disable ratelimiting for a specific user.
+There are specific APIs to set, get and delete a ratelimit.
+
+### Get status of ratelimit
+
+The API is:
+
+```
+GET /_synapse/admin/v1/users/<user_id>/override_ratelimit
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "messages_per_second": 0,
+ "burst_count": 0
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - The fully qualified MXID: for example, `@user:server.com`. The user must
+ be local.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `messages_per_second` - integer - The number of actions that can
+ be performed in a second. `0` mean that ratelimiting is disabled for this user.
+- `burst_count` - integer - How many actions that can be performed before
+ being limited.
+
+If **no** custom ratelimit is set, an empty JSON dict is returned.
+
+```json
+{}
+```
+
+### Set ratelimit
+
+The API is:
+
+```
+POST /_synapse/admin/v1/users/<user_id>/override_ratelimit
+```
+
+A response body like the following is returned:
+
+```json
+{
+ "messages_per_second": 0,
+ "burst_count": 0
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - The fully qualified MXID: for example, `@user:server.com`. The user must
+ be local.
+
+Body parameters:
+
+- `messages_per_second` - positive integer, optional. The number of actions that can
+ be performed in a second. Defaults to `0`.
+- `burst_count` - positive integer, optional. How many actions that can be performed
+ before being limited. Defaults to `0`.
+
+To disable users' ratelimit set both values to `0`.
+
+**Response**
+
+The following fields are returned in the JSON response body:
+
+- `messages_per_second` - integer - The number of actions that can
+ be performed in a second.
+- `burst_count` - integer - How many actions that can be performed before
+ being limited.
+
+### Delete ratelimit
+
+The API is:
+
+```
+DELETE /_synapse/admin/v1/users/<user_id>/override_ratelimit
+```
+
+An empty JSON dict is returned.
+
+```json
+{}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `user_id` - The fully qualified MXID: for example, `@user:server.com`. The user must
+ be local.
+
+### Check username availability
+
+Checks to see if a username is available, and valid, for the server. See [the client-server
+API](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available)
+for more information.
+
+This endpoint will work even if registration is disabled on the server, unlike
+`/_matrix/client/r0/register/available`.
+
+The API is:
+
+```
+GET /_synapse/admin/v1/username_available?username=$localpart
+```
+
+The request and response format is the same as the
+[/_matrix/client/r0/register/available](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available) API.
+
+### Find a user based on their ID in an auth provider
+
+The API is:
+
+```
+GET /_synapse/admin/v1/auth_providers/$provider/users/$external_id
+```
+
+When a user matched the given ID for the given provider, an HTTP code `200` with a response body like the following is returned:
+
+```json
+{
+ "user_id": "@hello:example.org"
+}
+```
+
+**Parameters**
+
+The following parameters should be set in the URL:
+
+- `provider` - The ID of the authentication provider, as advertised by the [`GET /_matrix/client/v3/login`](https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3login) API in the `m.login.sso` authentication method.
+- `external_id` - The user ID from the authentication provider. Usually corresponds to the `sub` claim for OIDC providers, or to the `uid` attestation for SAML2 providers.
+
+The `external_id` may have characters that are not URL-safe (typically `/`, `:` or `@`), so it is advised to URL-encode those parameters.
+
+**Errors**
+
+Returns a `404` HTTP status code if no user was found, with a response body like this:
+
+```json
+{
+ "errcode":"M_NOT_FOUND",
+ "error":"User not found"
+}
+```
+
+_Added in Synapse 1.68.0._
diff --git a/docs/administration/admin_api/version_api.md b/docs/administration/admin_api/version_api.md
new file mode 100644
index 0000000000..27977de0d3
--- /dev/null
+++ b/docs/administration/admin_api/version_api.md
@@ -0,0 +1,21 @@
+# Version API
+
+This API returns the running Synapse version and the Python version
+on which Synapse is being run. This is useful when a Synapse instance
+is behind a proxy that does not forward the 'Server' header (which also
+contains Synapse version information).
+
+The api is:
+
+```
+GET /_synapse/admin/v1/server_version
+```
+
+It returns a JSON body like the following:
+
+```json
+{
+ "server_version": "0.99.2rc1 (b=develop, abcdef123)",
+ "python_version": "3.7.8"
+}
+```
|