Discovery¶
Discovery is the public directory of guilds that any account can browse and join. A guild manager applies to have a guild listed, and TEBHOS staff review the application. Approved guilds are indexed in the TEBHOS search backend (Elasticsearch) and appear in Search discovery guilds.
Every route on this page requires a user session (Authorization: Bearer <token>, see Authentication). A valid bot token is rejected with 403 ACCESS_DENIED.
The application routes live under /guilds/{guild_id}/discovery. The same handlers are also mounted under /servers/{guild_id}/discovery; both paths behave identically. They require the MANAGE_GUILD permission (the guild owner always passes). A caller who is not a member of the guild, or who lacks the permission, receives 403 FORBIDDEN.
Listing requires boost level 2
An approved application (or the DISCOVERABLE guild feature) only makes a guild eligible for the directory. The guild is actually shown in search results only while its boost level is 2 or higher. A guild that drops below level 2 disappears from search automatically and reappears when it regains the level.
Discovery guild object¶
One public guild listing, as returned by Search discovery guilds.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the guild |
| name | string | The name of the guild at index time |
| icon | ?string | The icon hash of the guild |
| banner | ?string | The banner hash of the guild |
| description | ?string | The description from the approved application, falling back to the guild description |
| category_type | ?integer | The discovery category of the listing |
| primary_language | ?string | The primary language code of the listing |
| custom_tags | array[string] | The custom tags of the listing |
| member_count | integer | The member count of the guild |
| online_count | integer | The approximate number of online members |
| features | array[string] | The guild features the guild has |
| verification_level | integer | The verification level of the guild |
| boost_level | integer | The boost level of the guild at index time |
member_count and online_count are refreshed from the gateway for the returned page. When live counts are unavailable, member_count can be stale and online_count is approximated from the presence of the first 2000 members (or 0).
Listing updates can take time
Name, icon, banner, feature and boost changes are pushed to the search index when they happen, and a background job re-indexes every approved listing periodically.
Example¶
{
"id": "1489002177550843904",
"name": "Example guild",
"icon": "a_9f1c2d3e4b5a60718293a4b5c6d7e8f9",
"banner": null,
"description": "A place to talk about the example project.",
"category_type": 4,
"primary_language": "en-US",
"custom_tags": ["open source", "rust"],
"member_count": 4120,
"online_count": 318,
"features": ["DISCOVERABLE"],
"verification_level": 1,
"boost_level": 2
}
Discovery search result object¶
One page of matching listings.
Structure¶
| Field | Type | Description |
|---|---|---|
| guilds | array[discovery guild object] | The matching guilds on this page |
| total | integer | The total number of guilds matching the query, across all pages |
A client pages with offset until offset reaches total.
Discovery application object¶
One guild's discovery listing and review state. A guild has at most one application. A rejected or removed application remains readable with its outcome and reason until the guild applies again or withdraws it.
Structure¶
| Field | Type | Description |
|---|---|---|
| guild_id | snowflake | The ID of the guild |
| status | string | The application status |
| description | string | The description shown on the listing |
| category_type | ?integer | The discovery category of the listing |
| primary_language | ?string | The primary language code of the listing |
| custom_tags | array[string] | The custom tags of the listing |
| applied_at | ISO8601 timestamp | When the application was submitted |
| reviewed_at | ?ISO8601 timestamp | When the application was approved or rejected |
| review_reason | ?string | The reason recorded with the approval or rejection |
| removed_at | ?ISO8601 timestamp | When staff removed an approved listing |
| removal_reason | ?string | The reason recorded with the removal |
Example¶
{
"guild_id": "1489002177550843904",
"status": "pending",
"description": "A place to talk about the example project.",
"category_type": 4,
"primary_language": "en-US",
"custom_tags": ["open source", "rust"],
"applied_at": "2026-08-04T11:22:19.000000+00:00",
"reviewed_at": null,
"review_reason": null,
"removed_at": null,
"removal_reason": null
}
Discovery status object¶
The application state of one guild and its current eligibility to apply.
Structure¶
| Field | Type | Description |
|---|---|---|
| application | ?discovery application object | The current application, or null when the guild has never applied or has withdrawn |
| eligible | boolean | Whether the guild has at least min_member_count members |
| min_member_count | integer | The number of members required to apply (currently 1) |
Example¶
Discovery category object¶
| Field | Type | Description |
|---|---|---|
| id | integer | The discovery category value |
| name | string | The English display name of the category |
Discovery categories¶
The set is fixed.
| Value | Display name |
|---|---|
| 0 | Gaming |
| 1 | Music |
| 2 | Entertainment |
| 3 | Education |
| 4 | Science & Technology |
| 5 | Content Creator |
| 6 | Anime & Manga |
| 7 | Movies & TV |
| 8 | Other |
A category_type outside 0 through 8 is rejected with 400 BAD_REQUEST. In search queries an out-of-range category is ignored (no category filter is applied).
The display names are not localised; a client that wants translated labels supplies its own, keyed on id.
Discovery application statuses¶
| Value | Description |
|---|---|
| pending | Submitted and awaiting a staff decision |
| approved | Approved; the guild is eligible for the directory |
| rejected | Refused; the guild can submit a new application |
| removed | An approved listing was withdrawn by staff; the guild can submit a new application |
A guild holding a pending or approved application cannot submit another. A new submission after a rejection or removal replaces the previous record.
Primary language¶
Each listing stores one primary language code (for example en-US or tr). The value is trimmed; an omitted or empty value is stored as en-US. Search filters on the exact stored code.
Custom tags¶
A listing has up to ten custom tags. TEBHOS trims every tag and keeps only tags that are 2 through 30 characters long after trimming; shorter or longer tags are dropped silently rather than rejected. At most the first ten remaining tags are stored.
Search discovery guilds¶
GET /discovery/guilds
Searches the discovery index and returns a discovery search result object. The caller needs no permission and no relationship to the matched guilds.
Query parameters¶
| Field | Type | Description |
|---|---|---|
| query? | string | Free-text query matched against the guild name, custom tags, vanity code and description. Omit it to browse the whole directory |
| category? | integer | Restrict results to one discovery category (0-8) |
| language? | string | Restrict results to one primary language code |
| limit? | integer | Results per page (1-48, default 36; out-of-range values are clamped) |
| offset? | integer | Number of matches to skip (default 0) |
Without query, results are ordered by member count (descending). With a query, results are ordered newest guild first. The tag and sort_by parameters are accepted but ignored.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | discovery search result object | Search completed, possibly with no match |
| 500 | error response | No search backend is configured for the instance |
Rate limit¶
30 requests per 10 seconds per client, on the discovery:search bucket.
Quick guild search¶
GET /discovery/search
A lightweight search used by the client's search box. Returns an array of discoverable guild index documents (no paging envelope, no live online counts).
| Field | Type | Description |
|---|---|---|
| q? | string | The free-text query; empty matches everything |
| limit? | integer | Number of results (1-50, default 20) |
List discovery categories¶
GET /discovery/categories
Returns an array of every discovery category object in ascending id order.
Rate limit¶
60 requests per 10 seconds per client, on the discovery:categories bucket.
List discovery bots¶
GET /discovery/bots
Returns up to 50 bot applications whose owner marked the bot as public, for the "Bots" shelf of the discovery screen. Bots without a readable bot user are skipped.
Response¶
{
"bots": [
{
"id": "1489002177550843905",
"client_id": "1489002177550843905",
"bot_user_id": "1489002177550843906",
"name": "Example Bot",
"description": "Plays music in voice channels.",
"avatar": "9f1c2d3e4b5a60718293a4b5c6d7e8f9",
"banner": null,
"default_permissions": "12371"
}
]
}
| Field | Type | Description |
|---|---|---|
| id / client_id | snowflake | The application ID (use it as client_id in the OAuth2 bot authorization flow) |
| bot_user_id | snowflake | The user ID of the bot account |
| name | string | The bot's display name, falling back to its username or the application name |
| description | string | The bot user's bio, or a generic placeholder when it has none |
| avatar | ?string | The avatar hash of the bot user |
| banner | ?string | The banner hash of the bot user |
| default_permissions | string | Suggested permission bitfield for the invite link (12371 = View Channels, Send Messages, Connect, Speak) |
Join discovery guild¶
POST /discovery/guilds/{guild_id}/join
Adds the authenticated account to a discoverable guild and returns 204 with an empty body. No invite is needed and no invite use is consumed.
A guild can be joined this way when it has an approved application, has the DISCOVERABLE feature, or has the BUSINESS_CLASS feature (so that the Business badge's "Join" button works even for unlisted Business guilds).
Response¶
| Status | Body | Condition |
|---|---|---|
| 204 | empty | The account was added, or was already a member |
| 403 | error response | The guild has the INVITES_DISABLED feature (FORBIDDEN) |
| 403 | error response | The caller is banned from the guild (FORBIDDEN) |
| 404 | error response | The guild does not exist, or is not joinable through discovery (NOT_FOUND) |
Side effects¶
For a new member, the join dispatches Guild Member Add to the guild and connects the joining account's open sessions to the guild (they receive Guild Create). If the account has "restrict DMs from new servers" enabled, the guild is added to its restricted list. No join system message is posted.
Rate limit¶
10 requests per minute per user, on the discovery:join bucket (not partitioned by guild).
Get discovery status¶
GET /guilds/{guild_id}/discovery
Returns the discovery status object of one guild, including for a guild that has never applied. Requires MANAGE_GUILD.
Rate limit¶
30 requests per 10 seconds per user and guild, on the discovery:status bucket.
Apply for discovery¶
POST /guilds/{guild_id}/discovery
Submits the discovery application of a guild and returns the resulting discovery application object with status pending. Requires MANAGE_GUILD. Every application waits for a staff decision; there is no automatic approval.
JSON body¶
| Field | Type | Description |
|---|---|---|
| description | string | The listing description (10-300 characters after trimming) |
| category_type | integer | The discovery category (0-8) |
| primary_language? | string | The primary language code (default en-US) |
| custom_tags? | array[string] | The custom tags to store |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | discovery application object | The application was stored as pending |
| 400 | error response | The description length or category is invalid, the guild has fewer members than required, or the guild already holds a pending or approved application (BAD_REQUEST) |
| 403 | error response | The caller is not a member or lacks MANAGE_GUILD (FORBIDDEN) |
Side effects¶
The guild is re-indexed and a Guild Update is dispatched, because the guild's effective features can change.
Rate limit¶
5 requests per minute per user and guild, on the discovery:apply bucket, shared with Edit discovery application and Withdraw discovery application.
Example¶
curl -X POST "https://tebhos.com/api/v1/guilds/1489002177550843904/discovery" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"description":"A place to talk about the example project.","category_type":4,"primary_language":"en-US","custom_tags":["open source","rust"]}'
Edit discovery application¶
PATCH /guilds/{guild_id}/discovery
Updates the stored listing and returns the updated discovery application object. Requires MANAGE_GUILD. The status and review details are preserved.
JSON body¶
Every field is optional; an omitted field keeps the stored value.
| Field | Type | Description |
|---|---|---|
| description? | string | The listing description (10-300 characters) |
| category_type? | integer | The discovery category (0-8) |
| primary_language? | string | The primary language code; an empty string keeps the stored value |
| custom_tags? | array[string] | The complete new tag list; an empty array clears every tag |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | discovery application object | The listing was updated |
| 400 | error response | Invalid description or category, or the application is rejected or removed (BAD_REQUEST) |
| 403 | error response | The caller is not a member or lacks MANAGE_GUILD (FORBIDDEN) |
| 404 | error response | The guild has no application (NOT_FOUND) |
Changes to an approved listing are pushed to the search index and a Guild Update is dispatched.
Rate limit¶
Shares the discovery:apply bucket (5 requests per minute per user and guild).
Withdraw discovery application¶
DELETE /guilds/{guild_id}/discovery
Deletes the application record of a guild and returns 204 with an empty body. Requires MANAGE_GUILD.
Withdrawal destroys the review history
The review and removal details are deleted with the application. A guild that applies again is treated as a new applicant.
Response¶
| Status | Body | Condition |
|---|---|---|
| 204 | empty | The application was deleted |
| 403 | error response | The caller is not a member or lacks MANAGE_GUILD (FORBIDDEN) |
| 404 | error response | The guild has no application (NOT_FOUND) |
Side effects¶
Withdrawing an approved application also removes the DISCOVERABLE guild feature. The guild is re-indexed and a Guild Update is dispatched. Existing members are unaffected.
Rate limit¶
Shares the discovery:apply bucket (5 requests per minute per user and guild).