Skip to content

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

{
  "application": null,
  "eligible": true,
  "min_member_count": 1
}

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.

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.

[
  { "id": 0, "name": "Gaming" },
  { "id": 1, "name": "Music" }
]

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).