Relationships¶
A relationship describes a friendship, block, or pending friend request from the caller's perspective. Private notes belong to User notes.
All routes on this page are user-only. They require a user session token (Authorization: Bearer <token>). A valid Bot <token> credential is refused with 403 ACCESS_DENIED, and an invalid one with 401 UNAUTHORIZED.
Relationship records are directional
A friendship is a FRIEND record on both accounts. A pending request is OUTGOING_REQUEST on the sender and INCOMING_REQUEST on the recipient. A block exists only on the account that created it, so the blocked account holds no record for it.
Relationship object¶
A relationship record belongs to one account and names one other account. Every field describes the owning account's side, and every route on this page returns the caller's own record.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the related account. A relationship has no identifier of its own |
| type | integer | The relationship type |
| relation_type | integer | The same value as type, kept for older clients |
| user | partial user object | The related account, with two extra members described below |
| since | ?ISO8601 timestamp | The time the friendship was established. Only a FRIEND record carries a value; every other type has null |
| nickname | ?string | The caller-private nickname for the related account, or null when none is set |
| share_voice_activity | boolean | Whether the caller shares voice activity with the related account |
The user object is the ordinary partial user with two additional members:
| Field | Type | Description |
|---|---|---|
| name | string | The display name: the account's global_name when set, otherwise its username |
| image_url | string | The stored avatar URL, or an empty string when the account has none |
share_voice_activity is false on every new record. It becomes true on the caller's FRIEND records only when the caller turns voice activity sharing on through Modify voice activity sharing, which rewrites all of the caller's friendships at once.
Not supported on TEBHOS
The record carries no friend_shares_voice_activity field. Read the other side's preference from their own presence instead.
Example¶
{
"id": "1501314428688998182",
"type": 1,
"relation_type": 1,
"user": {
"id": "1501314428688998182",
"username": "aria",
"discriminator": "0000",
"global_name": "Aria",
"avatar": null,
"avatar_color": null,
"flags": 0,
"name": "Aria",
"image_url": ""
},
"since": "2026-01-14T09:31:00+00:00",
"nickname": "climbing partner",
"share_voice_activity": false
}
Relationship types¶
| Value | Name | Description |
|---|---|---|
| 1 | FRIEND | The two accounts are friends |
| 2 | BLOCKED | The caller has blocked the related account |
| 3 | INCOMING_REQUEST | The related account has sent the caller a friend request |
| 4 | OUTGOING_REQUEST | The caller has sent the related account a friend request |
Bulk ignore result object¶
A bulk ignore result reports how many incoming friend requests one call removed.
Structure¶
| Field | Type | Description |
|---|---|---|
| ignored_count | integer | The number of incoming friend requests that were removed |
Bulk ignore filters¶
| Value | Name | Description |
|---|---|---|
| all | All | Matches every incoming friend request |
| new_accounts | New accounts | Matches an incoming friend request whose sender account is younger than max_account_age_seconds |
new_accounts narrows the match only when max_account_age_seconds is also supplied. Without it, and for any value other than new_accounts, the call behaves as all.
Relationship limit¶
One account can hold at most 1000 relationship records in total, counting friendships, blocks, and pending requests in both directions. The limit is fixed and is not instance-configurable.
TEBHOS checks the caller and the target independently when a new friend request is created and when a pending request is accepted. A request fails when either account has reached the limit, with 400 MAX_FRIENDS. Blocking is not subject to the limit.
List relationships¶
GET /users/@me/relationships
Returns every relationship object the caller holds, covering friendships, blocks, and pending requests in both directions.
The operation is not paged, and the whole set comes back in one response. The order is not dependable. A connected client also receives the same set in the Gateway Ready payload.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | array of relationship objects | Relationships were returned, possibly as an empty array |
Rate limit¶
40 requests per 10 seconds for each authenticated user, on the user:relationships:list bucket.
Send friend request by username¶
POST /users/@me/relationships
Sends a friend request to the account identified by its username. Returns a relationship object on success.
TEBHOS usernames are unique, so the target is found by username alone and there is no discriminator field. The lookup is case-insensitive and ignores leading and trailing whitespace. Every rule after the lookup is the one described under Send friend request by user ID.
JSON body¶
| Field | Type | Description |
|---|---|---|
| username | string | The username of the target account |
Response¶
| Status | Body | Condition |
|---|---|---|
| 201 | relationship object | A pending request was created or already existed, or a pending incoming request was accepted |
| 400 | error response | username is empty (BAD_REQUEST), or a rule listed under Send friend request by user ID rejects the request |
| 404 | error response | No account has that username and the request returns NO_USERS_WITH_TEBHOSTAG_EXIST |
Side effects¶
The side effects are those of Send friend request by user ID.
Rate limit¶
10 requests per 60 seconds for each authenticated user, on the user:friend_request:send bucket, shared with Send friend request by user ID.
Send friend request by user ID¶
POST /users/@me/relationships/{user_id}
Sends a friend request to the selected account. Returns a relationship object on success. The route reads no request body.
TEBHOS evaluates the request in this order:
- A
user_idthat is not an integer returns 400BAD_REQUEST, and an ID that names no account returns 404NOT_FOUND. - The caller's own ID returns 400
CANNOT_SEND_FRIEND_REQUEST_TO_SELF. - The caller's current record with the target decides the next step:
INCOMING_REQUEST: the pending request is accepted, exactly as Accept friend request or block user would, and the resultingFRIENDrecord is returned.OUTGOING_REQUEST: the existing request is returned unchanged.FRIEND: 400ALREADY_FRIENDS.BLOCKED(the caller blocked the target): 400CANNOT_SEND_FRIEND_REQUEST_TO_BLOCKED_USER.
- A target that has blocked the caller returns 400
FRIEND_REQUEST_BLOCKED. The code is the same as the privacy refusal below, so the caller cannot tell that it is blocked. - The target's friend request privacy setting (the friend source flags in user settings) is checked. A target that accepts requests only from friends of friends or from members of a shared guild rejects an unrelated caller with 400
FRIEND_REQUEST_BLOCKED. - The relationship limit is checked for both accounts.
Steps 4 to 6 apply only to a genuinely new request. Accepting a pending incoming request (step 3) still checks the relationship limit, but not the privacy setting.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the target account |
Response¶
| Status | Body | Condition |
|---|---|---|
| 201 | relationship object | A pending request was created or already existed, or a pending incoming request was accepted |
| 400 | error response | The ID is malformed, the target is the caller, a relationship or privacy rule rejects the request, or the relationship limit is reached |
| 404 | error response | The target account does not exist (NOT_FOUND), or a pending incoming request was withdrawn during acceptance (UNKNOWN_USER) |
Side effects¶
A new request writes an OUTGOING_REQUEST record for the caller and an INCOMING_REQUEST record for the target, and emits Relationship Add to both accounts, each with its own record.
When the target had already sent the caller a request, both pending records are replaced with FRIEND records and Relationship Update reaches both accounts.
Returning an existing outgoing request writes nothing and emits nothing.
No path on this route opens a direct message channel.
Rate limit¶
10 requests per 60 seconds for each authenticated user, on the user:friend_request:send bucket, shared with Send friend request by username.
Accept friend request or block user¶
PUT /users/@me/relationships/{user_id}
Accepts a pending incoming friend request, or blocks the selected account when the body selects BLOCKED. Returns the caller's resulting relationship object.
The type member selects the operation
An omitted body, an omitted type, and type: 1 all select acceptance of a pending incoming request. type: 2 selects blocking. Any other value returns 400 BAD_REQUEST. Sending 1 does not create a friendship where no request exists.
Both operations return 400 BAD_REQUEST when user_id is malformed or is the caller's own ID, and 404 NOT_FOUND when it names no account.
Acceptance returns 404 UNKNOWN_USER when the caller holds no INCOMING_REQUEST record for the target, then applies the relationship limit to both accounts.
Blocking has no further admission rules. It is not subject to the relationship limit or to the target's privacy settings.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the target account |
JSON body¶
The body can be omitted, in which case acceptance is selected.
| Field | Type | Description |
|---|---|---|
| type? | integer | 1 to accept (default) or 2 to block. relation_type is accepted as an alias |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | relationship object | The friendship or the block was returned |
| 400 | error response | The ID is malformed or is the caller, type is not 1 or 2, or the relationship limit is reached |
| 404 | error response | The target account does not exist, or no pending incoming request exists |
Side effects¶
Acceptance replaces the pending records on both accounts with FRIEND records, sets since to the current time, and emits Relationship Update to both accounts. It does not open a direct message channel; either side can open one with Create private channel.
Blocking deletes any existing record on both accounts (friendship or pending request in either direction) and writes a BLOCKED record for the caller only. Relationship Add reaches the caller with the block, and Relationship Remove reaches the target with the caller's ID. The Remove is sent even when the target held no record. Blocking an account that is already blocked rewrites the same record. Blocking never closes or deletes the pair's direct message channel.
Rate limit¶
20 requests per 10 seconds for each authenticated user, on the user:friend_request:accept bucket.
Remove relationship¶
DELETE /users/@me/relationships/{user_id}
Removes the caller's friendship, pending friend request in either direction, or block involving the selected account. Returns 204 with an empty body on success.
The route reads no request body. The caller's current record determines what is removed, so the same route unfriends, cancels or declines a request, and unblocks.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the related account |
Response¶
| Status | Body | Condition |
|---|---|---|
| 204 | empty | The relationship was removed |
| 400 | error response | user_id is malformed |
| 404 | error response | The account does not exist, or the caller holds no relationship of any type with it (NOT_FOUND) |
Side effects¶
Removing a friendship or a pending request in either direction removes both accounts' records and emits Relationship Remove to both, each carrying the other account's ID in id. Declining an incoming request therefore also removes the sender's OUTGOING_REQUEST record, and the sender is notified.
A block is stored on one account only, so removing one deletes the caller's record and emits Relationship Remove to the caller alone.
Rate limit¶
30 requests per 10 seconds for each authenticated user, on the user:relationship:delete bucket.
Modify relationship nickname¶
PATCH /users/@me/relationships/{user_id}
Sets or clears the caller's private nickname for a friend. Returns the updated relationship object. Emits a Relationship Update Gateway event to the caller.
Only a FRIEND record accepts a nickname. Every other relationship state returns 404 NOT_FOUND.
The nickname is private to the caller
TEBHOS writes it only to the caller's record. The friend never reads it in a response and never receives it in a Dispatch.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the friend |
JSON body¶
| Field | Type | Description |
|---|---|---|
| nickname? | ?string | The nickname to store, or null (or an omitted member) to clear it |
The value is stored as sent. The server does not trim it, and a literal empty string is stored as an empty string rather than cleared.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | relationship object | The updated relationship was returned |
| 400 | error response | user_id is malformed or the body is not a JSON object |
| 404 | error response | The account does not exist or no FRIEND record exists with it |
Side effects¶
The record changes only for the caller, and share_voice_activity keeps its stored value. A stored since value is preserved, and a record that has none is stamped with the current time. Relationship Update reaches the caller for every accepted request, including one that submits the value already stored.
Rate limit¶
30 requests per 10 seconds for each authenticated user, on the user:relationship:update bucket.
Ignore pending requests in bulk¶
POST /users/@me/relationships/bulk-ignore
Removes matching incoming friend requests. Returns a bulk ignore result object on success.
Matching applies only to the caller's INCOMING_REQUEST records. TEBHOS derives a sender's account age from the timestamp embedded in its snowflake and compares it against max_account_age_seconds as an exclusive upper bound, so a sender exactly that old is not matched. One call removes every matching request.
An unbounded new_accounts filter removes everything
The age comparison applies only when max_account_age_seconds is supplied. Selecting new_accounts without it removes every incoming friend request, exactly as all does.
JSON body¶
The body can be omitted, in which case the all filter applies.
| Field | Type | Description |
|---|---|---|
| filter? | string | The bulk ignore filter to apply (default all) |
| max_account_age_seconds? | integer | The exclusive maximum sender account age in seconds, read only by the new_accounts filter. A value of 0 or below matches no sender |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | bulk ignore result object | Matching requests were removed, possibly none |
Side effects¶
Each matched request removes the caller's INCOMING_REQUEST record and the sender's OUTGOING_REQUEST record, and emits Relationship Remove to the caller and to the sender. A call that matches nothing changes nothing.
A call that fails partway can remove some incoming friend requests and return no count. Call List relationships again to read what remains.
Rate limit¶
5 requests per 60 seconds for each authenticated user, on the relationship:bulk_ignore bucket.
Gateway events¶
Every relationship Dispatch goes only to the sessions of the account it names:
| Event | Payload |
|---|---|
RELATIONSHIP_ADD |
The recipient's relationship object |
RELATIONSHIP_UPDATE |
The recipient's updated relationship object |
RELATIONSHIP_REMOVE |
An object whose id is the related account's ID |
See Gateway events for the envelope.