Applications¶
An application is an OAuth2 client owned by one user account, and every bot account belongs to one application. Creating an application always creates its bot account too. A user owns at most 25 applications. The authorisation code grant, the scopes, and the tokens issued to third-party clients belong to the OAuth2 resource. For a walkthrough, see Creating a bot.
Access rules¶
The management routes are user-only: they require a user session token, and a valid bot token is rejected with 403 ACCESS_DENIED (an invalid one with 401). Get public application needs no credential. Get bot application accepts only a bot token, and Get current application accepts either.
Only the owner can read or change an application. Another account receives 403 FORBIDDEN.
The credential-rotation routes and Delete application require sudo mode. Put the proof in the JSON body (password, mfa_code, or sudo_proof), or call them from a session that holds a sudo grant from Grant sudo mode. A missing proof returns 403 SUDO_MODE_REQUIRED.
Credentials are shown once
The client secret is returned only by Create application and Reset client secret, and the bot token only by Create application and Reset bot token. TEBHOS stores only a hash of each and cannot show them again.
Bot tokens¶
A bot token is the application ID, a full stop, and a secret of 32 random bytes encoded as base64url:
Send it as Authorization: Bot <token>. The bot user's ID is always equal to the application ID. See Authentication.
Application object¶
The application as its owner sees it.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
| name | string | The application name (1-100 characters) |
| redirect_uris | array[string] | The registered OAuth2 redirect URIs, in no defined order; an empty array when there are none |
| bot_public | boolean | Whether anyone can install the bot, not only the owner |
| bot_require_code_grant | boolean | Whether installing the bot requires a full code grant with a redirect URI |
| client_secret? | string | The client secret, present only in the responses of Create application and Reset client secret |
| bot? | application bot object | The bot account; absent if the bot account can no longer be read |
Example¶
{
"id": "1501314428688998182",
"name": "Party Parrot",
"redirect_uris": ["https://example.com/callback"],
"bot_public": true,
"bot_require_code_grant": false,
"bot": {
"id": "1501314428688998182",
"username": "Party_Parrot",
"discriminator": "0001",
"avatar": null,
"banner": null,
"bio": null,
"mfa_enabled": false,
"authenticator_types": [],
"flags": 0
}
}
Application bot object¶
The bot account an application owns.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The bot user ID, equal to the application ID |
| username | string | The bot's username |
| discriminator | string | The four-digit discriminator, zero-padded |
| avatar | ?string | The avatar hash, or null |
| banner | ?string | The banner hash, or null |
| bio | ?string | The profile biography, or null |
| mfa_enabled | boolean | Whether authenticator_types is non-empty |
| authenticator_types | array[integer] | The authenticator types copied from the owner's account when the bot was created |
| flags | integer | The bot's user flags, including the bot flags |
| token? | string | The bot token, present only in the response of Create application |
Bot flags¶
| Value | Name | Description |
|---|---|---|
| 1 << 4 | FRIENDLY_BOT | The bot accepts friend requests |
| 1 << 5 | FRIENDLY_BOT_MANUAL_APPROVAL | Friend requests to the bot need manual approval |
These bits live in the bot's user flags. Update bot profile can change only these two bits. A client MUST ignore bits it does not recognise.
Public application object¶
The application as any caller sees it, including a caller that sends no credential.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
| name | string | The application name |
| icon | ?string | The bot's avatar hash, used as the application icon, or null |
| description | null | Always null |
| redirect_uris | array[string] | The registered redirect URIs |
| scopes | array[string] | ["bot"] when the application has a bot account, otherwise empty |
| bot_public | boolean | Whether the caller can install the bot. Always true for the owner, even when the bot is private |
| bot | ?object | {id, username, discriminator, global_name, avatar, bot, flags} of the bot account, or null |
| current_user | ?object | {id, username, discriminator, global_name, avatar} of the caller, or null when the request has no valid user session |
Public data
This object exposes the redirect URI list and the bot profile to anyone. It never contains the client secret, the bot token, or the owner's identity.
Current bot application object¶
The application behind a bot token, as returned to that token.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
| name | string | The application name |
| icon | ?string | The bot's avatar hash, or null |
| description | ?string | The bot's biography, or null |
| bot_public | boolean | Whether anyone can install the bot |
| bot_require_code_grant | boolean | Whether installing the bot requires a full code grant |
| verify_key | string | A compatibility placeholder: always 64 0 characters |
| owner | object | {id, username, discriminator, global_name, avatar} of the owning account |
| redirect_uris | array[string] | The registered redirect URIs |
| bot? | application bot object | The bot account, without token |
Bot profile object¶
Returned by Update bot profile.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The bot user ID |
| username | string | The bot's username |
| discriminator | string | The four-digit discriminator, zero-padded |
| avatar | ?string | The avatar hash, or null |
| banner | ?string | The banner hash, or null |
| bio | ?string | The biography, or null |
| flags | integer | The bot's user flags |
Bot token reset object¶
Returned by Reset bot token.
Structure¶
| Field | Type | Description |
|---|---|---|
| token | string | The new bot token |
| bot | application bot object | The bot account, without token |
List owned applications¶
GET /users/@me/applications
Returns an array of every application object the current user owns. Requires a user session token; a request with a Bot credential returns 403 FORBIDDEN. No entry has a client secret or bot token. The list is not paginated.
Rate limit¶
60 requests per minute for each account, on the oauth_dev:clients:list bucket.
Get current application¶
GET /oauth2/applications/@me
Returns the caller's applications, selected by credential type:
- A user session returns the same array as List owned applications.
- A bot token returns the current bot application object.
The response shape depends on the credential
The body is an array for a user and a single object for a bot. A client that accepts both MUST branch on the shape.
Rate limit¶
60 requests per minute for each user or bot, on the oauth_dev:clients:list bucket.
Get bot application¶
GET /applications/@me
Returns the current bot application object for the presented bot token. Only Authorization: Bot <token> is accepted.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | current bot application object | The application was returned |
| 401 | error response | The credential is missing, is not a bot token, does not verify, or its application or owner no longer exists (UNAUTHORIZED, message invalid_token) |
Every rejection is the same, so a caller cannot tell a malformed token from one whose application was deleted. A bot can use this route to validate its token on start-up.
Get public application¶
GET /oauth2/applications/{id}/public
Returns the public application object. Authentication is not required. A valid user session fills in current_user, and the owner sees bot_public: true.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | public application object | The application was returned |
| 400 | error response | The ID is not a snowflake |
| 404 | error response | The application does not exist |
Rate limit¶
60 requests per minute for each client, on the oauth_dev:clients:list bucket.
Create application¶
POST /oauth2/applications
Creates an application together with its bot account. Returns the application object with the initial client_secret and, in bot.token, the initial bot token.
The route is CAPTCHA-gated: when the instance has a CAPTCHA provider, a missing X-Captcha-Token returns 400 CAPTCHA_REQUIRED and a rejected one returns 400 INVALID_CAPTCHA. See CAPTCHA handling.
Request headers¶
| Field | Type | Description |
|---|---|---|
| X-Captcha-Token? | string | The CAPTCHA solution |
| X-Captcha-Type? | string | hcaptcha or turnstile; omit to use the instance provider |
JSON body¶
| Field | Type | Description |
|---|---|---|
| name | string | The application name (1-100 characters after trimming) |
| redirect_uris? | ?array[string] | The OAuth2 redirect URIs to register |
| bot_public? | boolean | Whether anyone can install the bot (default true) |
| bot_require_code_grant? | boolean | Whether installing the bot requires a full code grant (default false) |
Redirect URIs are stored exactly as given. Authorise application and the token exchange compare them byte for byte, so register the exact string your callback uses, preferably with https.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | application object | The application and its bot were created |
| 400 | error response | The name is invalid, or the CAPTCHA is missing or rejected |
| 401 | error response | No valid user session |
| 400 | error response | MAX_APPLICATIONS: the caller already owns 25 applications |
| 429 | error response | The route bucket denied the request |
Side effects¶
The bot's username is derived from the application name: ASCII letters, digits, and _ are kept, spaces, -, and . become _, and the result is cut to 32 characters. When that leaves fewer than 2 characters, or the name has no free discriminator, a random name of the form Bot plus 8 letters and digits is used. The discriminator is the lowest free number for that username. The bot starts with no avatar, banner, or biography.
Rate limit¶
10 requests per hour for each account, on the oauth_dev:clients:create bucket.
Example¶
curl -X POST https://tebhos.com/api/v1/oauth2/applications \
-H "Authorization: Bearer <session token>" \
-H "Content-Type: application/json" \
-d '{"name": "Party Parrot", "redirect_uris": ["https://example.com/callback"]}'
Get application¶
GET /oauth2/applications/{id}
Returns one application object. Only the owner can read it.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | application object | The application was returned |
| 400 | error response | The ID is not a snowflake |
| 403 | error response | The caller does not own the application |
| 404 | error response | The application does not exist |
Rate limit¶
60 requests per minute for each account, on the oauth_dev:clients:list bucket.
Update application¶
PATCH /oauth2/applications/{id}
Updates an application and returns the resulting application object. Only the owner can update it. An omitted field is left unchanged.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
JSON body¶
| Field | Type | Description |
|---|---|---|
| name? | string | The application name (1-100 characters after trimming) |
| redirect_uris? | array[string] | The complete new set of redirect URIs. [] removes them all; null or omission leaves the set unchanged |
| bot_public? | boolean | Whether anyone can install the bot |
| bot_require_code_grant? | boolean | Whether installing the bot requires a full code grant |
Removing a redirect URI affects new authorisation requests only. A code already issued for that URI still exchanges with it, and existing tokens are unaffected. Neither the client secret nor the bot token changes.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | application object | The application was updated |
| 400 | error response | The ID or the name is invalid |
| 403 | error response | The caller does not own the application |
| 404 | error response | The application does not exist |
Rate limit¶
30 requests per minute for each application, on the oauth_dev:clients:update bucket, shared with Update bot profile.
Update bot profile¶
PATCH /oauth2/applications/{id}/bot
Updates the application's bot account and returns the bot profile object. Only the owner can update it.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
JSON body¶
| Field | Type | Description |
|---|---|---|
| username? | string | The new username (2-32 characters after trimming) |
| discriminator? | integer | Accepted only when it equals the current discriminator; the discriminator cannot be chosen |
| avatar? | ?string | A new avatar as a data URI (data:image/png;base64,...), or null to remove it |
| banner? | ?string | A new banner as a data URI, or null to remove it |
| bio? | ?string | The biography (at most 1024 characters), or null to remove it |
| bot_flags? | integer | The desired bot flags. Only those two bits are read; every other bit is ignored |
An image must be PNG, JPEG, GIF, or WebP and between 1 byte and 8 MiB after decoding. An omitted avatar, banner, or bio leaves the stored value unchanged; only null removes it.
When the username changes, the bot keeps its discriminator if it is free for the new name, and otherwise gets the lowest free one. A name with no free discriminator returns 400.
The update always clears the bot's display name, so a bot is always shown by its username.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | bot profile object | The profile was updated |
| 400 | error response | A field, an image, or the discriminator is invalid, or no discriminator is free |
| 403 | error response | The caller does not own the application |
| 404 | error response | The application or its bot account does not exist |
Rate limit¶
30 requests per minute for each application, on the oauth_dev:clients:update bucket, shared with Update application.
Reset bot token¶
POST /oauth2/applications/{id}/bot/reset-token
Rotates the bot token and returns the bot token reset object. The caller must own the application and prove sudo mode.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
JSON body¶
Optional sudo verification fields: password, mfa_code, or sudo_proof. Send an empty body when the session holds a sudo grant.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | bot token reset object | The token was rotated |
| 401 | error response | The password or code is wrong |
| 403 | error response | The caller does not own the application, or sudo mode was not proven (SUDO_MODE_REQUIRED) |
| 404 | error response | The application or its bot account does not exist |
Rotation disconnects the bot
The previous token stops working immediately, and every Gateway session the bot holds is closed. The bot reconnects only after its operator installs the new token.
The bot's guild and group DM memberships are unchanged.
Rate limit¶
10 requests per hour for each application, on the oauth_dev:clients:rotate_secret bucket, shared with Reset client secret.
Reset client secret¶
POST /oauth2/applications/{id}/client-secret/reset
Rotates the OAuth2 client secret and returns the application object with the new client_secret. The caller must own the application and prove sudo mode.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
JSON body¶
Optional sudo verification fields, as for Reset bot token.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | application object | The secret was rotated |
| 401 | error response | The password or code is wrong |
| 403 | error response | The caller does not own the application, or sudo mode was not proven |
| 404 | error response | The application does not exist |
Token exchange, introspection, and revocation stop accepting the previous secret immediately. Existing access and refresh tokens stay valid.
Rate limit¶
10 requests per hour for each application, on the oauth_dev:clients:rotate_secret bucket, shared with Reset bot token.
Delete application¶
DELETE /oauth2/applications/{id}
Deletes an application and retires its bot account. Returns 204. The caller must own the application and prove sudo mode.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The application ID |
JSON body¶
Optional sudo verification fields, as for Reset bot token.
Response¶
| Status | Body | Condition |
|---|---|---|
| 204 | empty | The application was deleted |
| 401 | error response | The password or code is wrong |
| 403 | error response | The caller does not own the application, or sudo mode was not proven |
| 404 | error response | The application does not exist |
Deletion is permanent
The bot is removed from every guild, its Gateway sessions are closed, and its profile is anonymised (username silinmis_bot_<id>, display name Silinmiş Bot, no avatar, banner, or biography). The application, its client secret, and its bot token are destroyed.
After deletion, the token, introspection, and revocation routes reject the client, and Get current OAuth2 authorisation returns 401 for the application's access tokens. The bot's messages remain, attributed to the anonymised account.
Rate limit¶
10 requests per hour for each application, on the oauth_dev:clients:delete bucket.