docs(discovery): document the channel preview route (#3165)

This commit is contained in:
Hampus
2026-10-03 14:48:09 +02:00
committed by GitHub
parent 7eebfca20b
commit a3cf960660
@@ -8,14 +8,14 @@ import RouteHeader from '@/components/RouteHeader.astro';
Discovery is the public directory of guilds any account can browse and join. A guild manager applies to have a guild listed, and an operator reviews that application through the [Admin Discovery API](/admin-api/discovery/).
[Join discovery guild](#join-discovery-guild) is user-only and rejects a bot token with 403 `ACCESS_DENIED`. Every other route accepts a user session token or a bot token. Besides that rejection, a bearer credential is the only credential that produces `ACCESS_DENIED` on any route here. Where a response table below pairs a revoked account with `ACCESS_DENIED`, a session, bot, or bearer token that no longer resolves to an account returns 401 `UNAUTHORIZED` instead.
[Preview discovery channel](#preview-discovery-channel) and [Join discovery guild](#join-discovery-guild) are user-only and reject a bot token with 403 `ACCESS_DENIED`. Every other route accepts a user session token or a bot token. Besides that rejection, a bearer credential is the only credential that produces `ACCESS_DENIED` on any route here. Where a response table below pairs a revoked account with `ACCESS_DENIED`, a session, bot, or bearer token that no longer resolves to an account returns 401 `UNAUTHORIZED` instead.
On a guild an operator has marked unavailable, Fluxer refuses [Apply for discovery](#apply-for-discovery), [Edit discovery application](#edit-discovery-application), [Withdraw discovery application](#withdraw-discovery-application), and [Get discovery status](#get-discovery-status) with 403 `MISSING_ACCESS` before the route runs. The gate does not cover [Join discovery guild](#join-discovery-guild).
On a guild an operator has marked unavailable, Fluxer refuses [Apply for discovery](#apply-for-discovery), [Edit discovery application](#edit-discovery-application), [Withdraw discovery application](#withdraw-discovery-application), and [Get discovery status](#get-discovery-status) with 403 `MISSING_ACCESS` before the route runs. The gate does not cover [Preview discovery channel](#preview-discovery-channel) or [Join discovery guild](#join-discovery-guild).
The routes under `/v1/guilds/{guild_id}/discovery` require [MANAGE_GUILD](/http-api/permissions/), which is an [elevated permission](/http-api/permissions/#elevated-permissions). While the guild [MFA level](/http-api/guilds/#mfa-levels) is elevated, a caller other than the owner also needs an enrolled authenticator, and receives 400 `TWO_FACTOR_REQUIRED` without one. A guild that does not exist returns 404 `UNKNOWN_GUILD`. Both a non-member and a member without the permission return 403 `MISSING_PERMISSIONS`. The routes answer 503 `SERVICE_UNAVAILABLE` when the check cannot be admitted, and 504 `GATEWAY_TIMEOUT` when it does not answer in time.
:::note[Discovery is an optional instance capability]
Searching, applying, editing, withdrawing, and joining fail with 400 `DISCOVERY_DISABLED` while an operator has it disabled. [List discovery categories](#list-discovery-categories) and [Get discovery status](#get-discovery-status) keep answering.
Searching, previewing a channel, applying, editing, withdrawing, and joining fail with 400 `DISCOVERY_DISABLED` while an operator has it disabled. [List discovery categories](#list-discovery-categories) and [Get discovery status](#get-discovery-status) keep answering.
:::
## Discovery guild object
@@ -183,6 +183,54 @@ One entry of the fixed category registry, as [List discovery categories](#list-d
| id | integer | The [discovery category](#discovery-categories) value |
| name | string | The display name of the category |
## Discovery channel preview object
The guild and channel behind a channel or message link, as [Preview discovery channel](#preview-discovery-channel) returns them.
### Structure
| Field | Type | Description |
| --- | --- | --- |
| guild | [discovery preview guild](#discovery-preview-guild-object) object | The discoverable guild the channel belongs to |
| channel | [discovery preview channel](#discovery-preview-channel-object) object | The channel new members can read |
### Example
```json
{
"guild": {
"id": "1489002177550843904",
"name": "Example guild",
"icon": "a_9f1c2d3e4b5a60718293a4b5c6d7e8f9"
},
"channel": {
"id": "1489002177550843907",
"name": "general",
"type": 0
}
}
```
## Discovery preview guild object
### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The ID of the guild |
| name | string | The current name of the guild |
| icon | ?string | The icon hash of the guild, or null when it stores none |
## Discovery preview channel object
### Structure
| Field | Type | Description |
| --- | --- | --- |
| id | snowflake | The ID of the channel |
| name | ?string | The name of the channel, or null when it stores none |
| type | integer | The [channel type](/http-api/channels/#channel-types) of the channel |
## Discovery categories
The release fixes the set, so an operator cannot add, rename, or remove a category.
@@ -324,6 +372,43 @@ A client that wants a translated label supplies its own translation keyed on `id
60 requests per 10 seconds for each authenticated user, on the `discovery:categories` bucket.
## Preview discovery channel
<RouteHeader method="GET" path="/v1/discovery/guilds/{guild_id}/channels/{channel_id}" />
Returns the [discovery channel preview object](#discovery-channel-preview-object) for a channel or message link that points into a discoverable guild, so a client can show the guild and channel before the account joins. The caller needs no permission and no membership.
The route answers only when the guild holds an approved discovery application, the guild does not have the invites-disabled feature, the channel belongs to that guild, and the `@everyone` role can read the channel. Reading means `@everyone` holds both [VIEW_CHANNEL](/http-api/permissions/) and [READ_MESSAGE_HISTORY](/http-api/permissions/) after the channel's `@everyone` overwrite is applied. A guild whose `@everyone` role has [ADMINISTRATOR](/http-api/permissions/) passes regardless of overwrites. Role and member overwrites are not considered, and any channel type can pass.
:::note[Every refusal returns the same error]
A guild that does not exist, a guild without an approved listing, a channel that does not exist, a channel in another guild, and a channel `@everyone` cannot read all return 400 `DISCOVERY_NOT_DISCOVERABLE`. The response does not reveal which condition failed.
:::
### Path parameters
| Field | Type | Description |
| --- | --- | --- |
| guild_id | snowflake | The ID of the discoverable guild |
| channel_id | snowflake | The ID of the channel to preview |
### Response
| Status | Body | Condition |
| --- | --- | --- |
| 200 | [discovery channel preview](#discovery-channel-preview-object) object | Guild is listed in discovery and new members can read the channel |
| 400 | [error response](/http-api/#error-response) | A path parameter is not a valid snowflake and the request returns `INVALID_FORM_BODY` |
| 400 | [error response](/http-api/#error-response) | Discovery is disabled for the instance and the request returns `DISCOVERY_DISABLED` |
| 400 | [error response](/http-api/#error-response) | The guild is not discoverable or `@everyone` cannot read the channel, and the request returns `DISCOVERY_NOT_DISCOVERABLE` |
| 403 | [error response](/http-api/#error-response) | Caller is a bot, presents a bearer credential, or holds a revoked account and the request returns `ACCESS_DENIED` |
### Side effects
None. The route reads stored records only and emits no Gateway Dispatch.
### Rate limit
60 requests per 10 seconds for each authenticated user, on the `discovery:channel_preview` bucket, which is not partitioned by guild or channel.
## Join discovery guild
<RouteHeader method="POST" path="/v1/discovery/guilds/{guild_id}/join" />