Skip to content

Friends API

Endpoints for friend links, requests, moderation, and live social snapshots.

Permissions

  • social:friends.read for listing friends, requests, blocks, and live snapshots
  • social:friends.write for creating requests and reports
  • social:friends.edit for accepting, declining, canceling, removing, blocking, unblocking, and metadata updates

If a permission node is missing in the database, the route currently allows access by default.

Endpoints

GET /v1/social/friends

Returns the authenticated user's friend list plus summary counts.

GET /v1/social/friends/requests

Returns incoming and outgoing requests for the authenticated user.

POST /v1/social/friends/requests

Creates a new friend request. Requests are rejected when the recipient has disabled allowFriendRequests in GET /v1/profile/settings.

Body:

json
{
  "targetId": "uuid",
  "targetName": "playerName",
  "note": "optional"
}

Either targetId or targetName is required.

POST /v1/social/friends/requests/{id}/accept

Accepts an incoming request and creates the friend link in both directions.

POST /v1/social/friends/requests/{id}/decline

Declines an incoming request.

DELETE /v1/social/friends/requests/{id}

Cancels an outgoing request or removes a pending request visible to the caller.

PATCH /v1/social/friends/{friendId}

Updates caller-owned metadata for one friend link.

Body:

json
{
  "favorite": true,
  "visibility": "DEFAULT"
}

Supported visibility values:

  • DEFAULT
  • LIMITED
  • HIDDEN

Unknown values currently normalize back to DEFAULT.

DELETE /v1/social/friends/{friendId}

Removes the friendship in both directions.

GET /v1/social/friends/blocks

Lists blocked users for the authenticated caller.

POST /v1/social/friends/blocks

Creates or updates a block entry and removes any existing friend links or pending requests between the caller and the blocked user.

Body:

json
{
  "targetId": "uuid",
  "reason": "optional"
}

DELETE /v1/social/friends/blocks/{targetId}

Removes an existing block entry.

POST /v1/social/friends/reports

Creates a profile moderation report for a target user.

Body:

json
{
  "targetId": "uuid",
  "reason": "optional"
}

GET /v1/social/friends/live

WebSocket endpoint that emits an initial snapshot and then only sends a new payload when the friend/request snapshot changes.

Auth:

  • Authorization: Bearer <token> header, or
  • Sec-WebSocket-Protocol: uebliche-bearer.<base64url-token> for WebSocket clients that cannot set custom headers.

Optional query parameters:

  • interval: polling interval in seconds, clamped server-side

Server friends endpoints

Server friends endpoints require Authorization: Bearer <CONNECT_SERVER_SECRET> and X-Server-Id. They use the same Public API friends service as the user endpoints, including blocks, allowFriendRequests, permission-based friend limits, request TTLs, notifications, flame-streak enrichment, and party enrichment.

  • POST /v1/social/friends/server/overview with { "actorId": "uuid" }
  • POST /v1/social/friends/server/requests with { "actorId": "uuid", "targetId": "uuid", "targetName": "optional fallback", "note": "optional" }
  • POST /v1/social/friends/server/requests/accept with { "actorId": "uuid", "targetId": "uuid", "targetName": "optional fallback" }
  • POST /v1/social/friends/server/requests/decline with { "actorId": "uuid", "targetId": "uuid", "targetName": "optional fallback" }
  • POST /v1/social/friends/server/requests/cancel with { "actorId": "uuid", "targetId": "uuid", "targetName": "optional fallback" }
  • DELETE /v1/social/friends/server with { "actorId": "uuid", "targetId": "uuid", "targetName": "optional fallback" }

Request mutations may identify the other player by targetId or targetName, so server-side clients do not need to duplicate Public API user lookup behavior. Sending a request returns the created request entry. Accept, decline, cancel, and remove return 204 No Content.

Server overview response:

json
{
  "friends": [],
  "counts": {
    "total": 0,
    "favorites": 0,
    "online": 0,
    "max": 250
  },
  "self": null,
  "incoming": [],
  "outgoing": [],
  "requests": {
    "incoming": [],
    "outgoing": []
  }
}

GET /v1/social/party

Returns the caller's current party and pending invites.

POST /v1/social/party

Creates a party for the caller when none exists, or returns the existing party snapshot. This is a breaking change from the old leave behavior.

POST /v1/social/party/leave

Leaves the caller's current party. If the last member leaves, the party is removed.

PUT /v1/social/party/lobby/selection

Updates the current party lobby selection. The caller must be the party leader or be included in party.lobby.selectorIds. Updating the selection increments party.lobby.revision and clears all ready states.

json
{
  "targetKind": "minigame",
  "targetId": "ffa-ragemode-round",
  "displayName": "FFA RageMode Round",
  "serverAddress": "uebliche.games",
  "minigame": {
    "mode": "ffa",
    "variant": "ragemode-round",
    "roundBased": true
  },
  "profileRequirement": {
    "minecraftVersion": "1.20.4",
    "loader": "fabric",
    "recommendedProfileId": "uebliche-games",
    "recommendedProfileName": "Uebliche.games",
    "recommendedProfileShareCode": "438Z2V",
    "requiredMods": []
  }
}

PUT /v1/social/party/lobby/selectors

Updates which current party members may change the lobby selection. Only the party leader may call this endpoint. The leader is always kept in selectorIds.

json
{
  "selectorIds": ["uuid"]
}

PUT /v1/social/party/lobby/ready

Updates the caller's ready state for the selected party lobby target. ready=true requires profilePrepared=true. For round-based minigames, the API creates party.lobby.launchSignal once all current party members are ready.

json
{
  "ready": true,
  "profilePrepared": true,
  "profileId": "uebliche-games",
  "temporaryProfile": false,
  "requirementHash": "profile-requirement-hash"
}

DELETE /v1/social/party

Disbands the caller's current party. The caller must be the party leader.

POST /v1/social/party/invites

Sends a party invite and creates the caller's party when needed.

json
{
  "targetId": "uuid",
  "targetName": "optional fallback"
}

POST /v1/social/party/invites/{partyId}/accept

Accepts a pending invite for the caller.

POST /v1/social/party/invites/{partyId}/decline

Declines a pending invite for the caller.

GET /v1/social/party/live

WebSocket endpoint that emits an initial party snapshot and then only sends a new payload when party membership or pending party invites change.

Auth:

  • Authorization: Bearer <token> header, or
  • Sec-WebSocket-Protocol: uebliche-bearer.<base64url-token> for WebSocket clients that cannot set custom headers.

Optional query parameters:

  • interval: polling interval in seconds, clamped server-side

Server party endpoints

Server party endpoints require Authorization: Bearer <CONNECT_SERVER_SECRET> and X-Server-Id.

  • POST /v1/social/party/server/overview with { "actorId": "uuid" }
  • POST /v1/social/party/server with { "actorId": "uuid" }
  • POST /v1/social/party/server/leave with { "actorId": "uuid" }
  • DELETE /v1/social/party/server with { "actorId": "uuid" }
  • POST /v1/social/party/server/invites with { "actorId": "uuid", "targetId": "uuid" }
  • POST /v1/social/party/server/invites/{partyId}/accept with { "actorId": "uuid" }
  • POST /v1/social/party/server/invites/{partyId}/decline with { "actorId": "uuid" }
  • POST /v1/social/party/server/lookup with { "playerIds": ["uuid"] }

Lookup response:

json
{
  "memberships": [
    {
      "playerId": "uuid",
      "partyId": "uuid",
      "leader": true
    }
  ],
  "parties": []
}

GET /v1/users/{id}/friends

Returns the same overview shape as the server friends overview for a specific user id. Requires social:friends.read.

Payloads

Friend entry

json
{
  "friendId": "uuid",
  "favorite": false,
  "visibility": "DEFAULT",
  "note": "optional",
  "createdAt": "2026-01-07T12:00:00Z",
  "acceptedAt": "2026-01-07T12:00:00Z",
  "updatedAt": "2026-01-07T12:00:00Z",
  "online": true,
  "partyId": "uuid",
  "name": "Player",
  "status": {
    "state": "ONLINE",
    "timestamp": "2026-01-07T12:00:00Z"
  },
  "player": {
    "id": "uuid",
    "name": "Player",
    "online": true,
    "status": {
      "state": "ONLINE",
      "timestamp": "2026-01-07T12:00:00Z"
    },
    "partyId": "uuid"
  },
  "lastSeenAt": "2026-01-07T12:00:00Z"
}

Request entry

json
{
  "id": "objectId",
  "senderId": "uuid",
  "recipientId": "uuid",
  "direction": "incoming",
  "note": "optional",
  "createdAt": "2026-01-07T12:00:00Z",
  "expiresAt": "2026-01-14T12:00:00Z",
  "player": {
    "id": "uuid",
    "name": "Player",
    "online": false,
    "status": {
      "state": "OFFLINE",
      "timestamp": "2026-01-07T12:00:00Z"
    },
    "partyId": null
  }
}

Block entry

json
{
  "blockedUserId": "uuid",
  "reason": "optional",
  "createdAt": "2026-01-07T12:00:00Z",
  "updatedAt": "2026-01-07T12:00:00Z"
}

Friends snapshot

GET /v1/social/friends returns:

json
{
  "friends": [],
  "counts": {
    "total": 0,
    "favorites": 0,
    "online": 0,
    "max": 250
  },
  "self": null
}

GET /v1/users/{id}/friends returns:

json
{
  "friends": [],
  "counts": {
    "total": 0,
    "favorites": 0,
    "online": 0,
    "max": 250
  },
  "self": null,
  "incoming": [],
  "outgoing": [],
  "requests": {
    "incoming": [],
    "outgoing": []
  }
}

GET /v1/social/friends/live emits:

json
{
  "type": "friends_snapshot",
  "friends": [],
  "counts": {
    "total": 0,
    "favorites": 0,
    "online": 0
  },
  "requests": {
    "incoming": [],
    "outgoing": []
  }
}

GET /v1/social/party/live emits the same shape as GET /v1/social/party:

json
{
  "party": null,
  "invites": []
}