> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sublay.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Users

> Fetch, update and delete user profiles, check usernames, and query follow and connection graphs

export const SpaceReputationParams = ({variant = "context", field = "path"}) => {
  const isUser = variant === "user";
  return <>
      <ParamField {...{
    [field]: "spaceReputation",
    type: "object"
  }}>
        Opt-in. Adds a <code>spaceReputation</code> number to each returned user,
        scoped to a single space. Requires the <code>reputation</code> bundle.
        The <code>reputation</code> total is always present regardless;{" "}
        <code>spaceReputation</code> is purely additive and only appears when you
        pass this object. <strong>This object is the primary form</strong> — it
        replaces the deprecated flat props below. See the{" "}
        <a href="/v8/data-models/reputation">Reputation data model</a> for the
        full contract.

        <Expandable title="spaceReputation">
          {isUser ? <ParamField name="spaceId" type="string | &quot;none&quot;" required>
              Which bucket to read. A space <strong><code>&lt;uuid&gt;</code></strong> reads that
              space's bucket; <strong><code>"none"</code></strong> reads the
              project-general (space-less) bucket.{" "}
              <strong><code>"context"</code> is rejected (400)</strong> on this
              endpoint — there is no space context to derive. The empty string
              and the legacy <code>general</code> / <code>null</code> aliases are
              also rejected. Missing buckets read as <code>0</code>.
            </ParamField> : <ParamField name="spaceId" type="string | &quot;none&quot; | &quot;context&quot;" required>
              Which bucket to read. One of: a space <strong><code>&lt;uuid&gt;</code></strong>{" "}
              (that space's bucket), <strong><code>"none"</code></strong> (the
              project-general, space-less bucket), or{" "}
              <strong><code>"context"</code></strong> (the space derived from the
              request context — resolved per-row on lists, or the single space on
              member/team/conversation surfaces). The empty string and the legacy{" "}
              <code>general</code> / <code>null</code> aliases are rejected (400).
              Missing buckets read as <code>0</code>.
            </ParamField>}
          <ParamField name="includeDescendants" type="boolean">
            Only honored when <code>spaceId</code> is an explicit space <code>&lt;uuid&gt;</code>.
            When <code>true</code>, <code>spaceReputation</code> is the{" "}
            <strong>subtree sum</strong> — the named space plus all of its
            descendant spaces (the root space's own bucket included). Ignored for{" "}
            <code>"none"</code>; not allowed with <code>"context"</code>.
          </ParamField>
        </Expandable>
      </ParamField>

      {isUser ? <ParamField {...{
    [field]: "spaceReputationId",
    type: "string",
    deprecated: true
  }}>
          <strong>Deprecated</strong> — use the <code>spaceReputation</code>{" "}
          object instead. The flat form of <code>spaceReputation.spaceId</code>.
          Still accepted (the object wins if both are passed); removal is a later
          major version. Accepts a space <code>&lt;uuid&gt;</code> or <code>"none"</code> only —{" "}
          <code>"context"</code> is rejected (400) on this endpoint.
        </ParamField> : <ParamField {...{
    [field]: "spaceReputationId",
    type: "string",
    deprecated: true
  }}>
          <strong>Deprecated</strong> — use the <code>spaceReputation</code>{" "}
          object instead. The flat form of <code>spaceReputation.spaceId</code>.
          Still accepted (the object wins if both are passed); removal is a later
          major version. Accepts a space <code>&lt;uuid&gt;</code>, <code>"none"</code>, or{" "}
          <code>"context"</code>.
        </ParamField>}

      <ParamField {...{
    [field]: "spaceReputationDescendants",
    type: "boolean",
    deprecated: true
  }}>
        <strong>Deprecated</strong> — use{" "}
        <code>spaceReputation.includeDescendants</code> instead. The flat form,
        only honored alongside an explicit <code>spaceReputationId</code> <code>&lt;uuid&gt;</code>.
      </ParamField>
    </>;
};

The `users` module provides server-side access to user profiles, social graph data (follows and connections), and username availability. All operations use your project API key and do not require a user access token.

***

### fetchUserById

Fetches a user profile by their internal Sublay user ID.

```typescript theme={null}
const user = await sublay.users.fetchUserById({ userId: "usr_abc123" });
```

<ParamField body="userId" type="string" required>
  The Sublay user ID.
</ParamField>

<ParamField body="include" type="string">
  Comma-separated list of associations to populate.
</ParamField>

<SpaceReputationParams variant="user" field="body" />

**Returns** — `Promise<User>`

***

### fetchUserByForeignId

Fetches a user profile by your application's own user identifier. Optionally creates the user if not found.

```typescript theme={null}
const user = await sublay.users.fetchUserByForeignId({
  foreignId: "external-user-42",
  createIfNotFound: true,
  name: "Bob Smith",
  username: "bob",
});
```

<ParamField body="foreignId" type="string" required>
  Your application's user identifier.
</ParamField>

<ParamField body="createIfNotFound" type="boolean">
  When `true`, creates the user with the provided profile fields if no match is found. Defaults to `false`.
</ParamField>

<ParamField body="name" type="string">
  Display name — used only when creating a new user.
</ParamField>

<ParamField body="username" type="string">
  Username — used only when creating a new user.
</ParamField>

<ParamField body="avatar" type="string">
  Avatar URL — used only when creating a new user.
</ParamField>

<ParamField body="bio" type="string">
  Bio text — used only when creating a new user.
</ParamField>

<ParamField body="metadata" type="object">
  Public metadata — used only when creating a new user.
</ParamField>

<ParamField body="secureMetadata" type="object">
  Secure metadata (not exposed to the client) — used only when creating a new user.
</ParamField>

<ParamField body="include" type="string">
  Comma-separated list of associations to populate.
</ParamField>

<SpaceReputationParams variant="user" field="body" />

**Returns** — `Promise<User>`

***

### fetchUserByUsername

Fetches a user profile by their username.

```typescript theme={null}
const user = await sublay.users.fetchUserByUsername({ username: "alice" });
```

<ParamField body="username" type="string" required>
  The user's unique username.
</ParamField>

<ParamField body="include" type="string">
  Comma-separated list of associations to populate.
</ParamField>

<SpaceReputationParams variant="user" field="body" />

**Returns** — `Promise<User>`

***

### updateUser

Updates a user's profile fields.

```typescript theme={null}
const updatedUser = await sublay.users.updateUser({
  userId: "usr_abc123",
  name: "Alice Updated",
  bio: "New bio",
  avatar: "https://cdn.example.com/avatar.jpg",
  location: { latitude: 37.7749, longitude: -122.4194 },
});
```

<ParamField body="userId" type="string" required>
  The Sublay user ID to update.
</ParamField>

<ParamField body="name" type="string">
  New display name.
</ParamField>

<ParamField body="username" type="string">
  New username. Must be available.
</ParamField>

<ParamField body="bio" type="string">
  New bio text.
</ParamField>

<ParamField body="avatar" type="string">
  New avatar URL.
</ParamField>

<ParamField body="metadata" type="object">
  Updated public metadata. Merged with existing metadata.
</ParamField>

<ParamField body="secureMetadata" type="object">
  Updated secure metadata (not exposed in public responses). Merged with existing values.
</ParamField>

<ParamField body="birthdate" type="string">
  ISO 8601 date string for the user's birthdate.
</ParamField>

<ParamField body="location" type="object">
  Geographic location `{ latitude: number; longitude: number }`.
</ParamField>

**Returns** — `Promise<UserFull>`

***

### deleteUser

Permanently deletes a user and cascades cleanup across all of their related data — reactions, files, follows, connections, notifications, collections, reports, and mentions are removed, and their entities and comments are stripped of authored content. This mirrors the full-cascade delete performed from the dashboard.

<Warning>
  This is a hard, irreversible delete — the user and their related data cannot be recovered.
</Warning>

```typescript theme={null}
await sublay.users.deleteUser({ userId: "usr_abc123" });
```

<ParamField body="userId" type="string" required>
  The Sublay user ID to delete.
</ParamField>

**Returns** — `Promise<void>`

***

### fetchUserSuggestions

Returns users whose name or username matches a partial query string. Useful for mention autocomplete on the server.

```typescript theme={null}
const users = await sublay.users.fetchUserSuggestions({ query: "ali" });
```

<ParamField body="query" type="string" required>
  Partial name or username to search for.
</ParamField>

<SpaceReputationParams variant="user" field="body" />

**Returns** — `Promise<User[]>`

***

### checkUsernameAvailability

Checks whether a username is available for registration.

```typescript theme={null}
const { available } = await sublay.users.checkUsernameAvailability({
  username: "alice",
});
```

<ParamField body="username" type="string" required>
  The username to check.
</ParamField>

**Returns** — `Promise<{ available: boolean }>`

***

### fetchCommentsByUserId

Returns a user's comments — top-level comments and replies alike, across entities — cursor-paginated. See [Fetch Comments by User](/v8/api-reference/users/fetch-comments-by-user-id).

<Note>
  Pass `actingUserId` to read the history as that user: their own moderator-removed comments are included in full, the block rules and `blockedFilter` apply from their perspective (comments on an entity whose author blocked them are left out, except [their own](/v8/api-reference/users/fetch-comments-by-user-id#your-own-comments-on-an-unavailable-entity)), the `isAuthorBlocked` / `isUnavailable` flags are set and `userReaction` is populated. Without it the read is anonymous: deleted and moderator-removed comments are left out, no block rule applies, `isAuthorBlocked` and `isUnavailable` are always `false`, and `userReaction` isn't populated.
</Note>

```typescript theme={null}
const { data, pagination } = await sublay.users.fetchCommentsByUserId({
  userId: "usr_abc123",
  limit: 20,
  include: ["entity"],
});

const next = await sublay.users.fetchCommentsByUserId({
  userId: "usr_abc123",
  limit: 20,
  include: ["entity"],
  cursor: pagination.nextCursor,
});
```

<ParamField body="userId" type="string" required>
  The Sublay user ID whose comments to fetch.
</ParamField>

<ParamField body="actingUserId" type="string">
  The user to read the history as. Omit for the anonymous view.
</ParamField>

<ParamField body="entityId" type="string">
  Only comments on this entity.
</ParamField>

<ParamField body="sourceId" type="string">
  Only comments on entities with this `sourceId`.
</ParamField>

<ParamField body="blockedFilter" type="&#x22;exclude&#x22; | &#x22;include-outbound-blocked&#x22;">
  How comments by users `actingUserId` is block-edged with are treated: `"exclude"` (default) leaves them out; `"include-outbound-blocked"` re-includes comments by users they blocked, marked `isAuthorBlocked: true`. No effect without `actingUserId`.
</ParamField>

<ParamField body="limit" type="number">
  Comments per page. Server default `10`, max `100`.
</ParamField>

<ParamField body="cursor" type="string | null">
  `pagination.nextCursor` from the previous response, passed as returned. Omit it, or pass `null`, for the first page. Send the same sort as the request that returned it.
</ParamField>

<ParamField body="sortBy" type="&#x22;createdAt&#x22; | &#x22;top&#x22; | &#x22;controversial&#x22;">
  Server default `"createdAt"`. `"top"` ranks by the raw count of `sortByReaction`.
</ParamField>

<ParamField body="sortDir" type="&#x22;asc&#x22; | &#x22;desc&#x22;">
  Applies to every sort. Server default `"desc"`.
</ParamField>

<ParamField body="sortByReaction" type="ReactionType">
  With `sortBy: "top"` only. Server default `"upvote"`.
</ParamField>

<ParamField body="include" type="CommentIncludeParam">
  Related data to populate: `"user"`, `"entity"`, `"space"`, `"parent"`, `"grants"`.
</ParamField>

<SpaceReputationParams variant="context" field="body" />

**Returns** — `Promise<CursorPaginatedResponse<Comment>>` — `{ data, pagination: { nextCursor, hasMore } }`

***

### fetchFollowersByUserId

Returns a paginated list of users following the specified user.

```typescript theme={null}
const { data, pagination } = await sublay.users.fetchFollowersByUserId({
  userId: "usr_abc123",
  page: 1,
  limit: 20,
});
```

<ParamField body="userId" type="string" required>
  The Sublay user ID.
</ParamField>

<ParamField body="page" type="number">
  Page number (1-indexed). Defaults to `1`.
</ParamField>

<ParamField body="limit" type="number">
  Results per page. Defaults to `20`.
</ParamField>

<ParamField body="query" type="string">
  Optional search term. Filters the list to users whose username or name contains this text (case-insensitive). When omitted, no filtering is applied.
</ParamField>

<ParamField body="searchFields" type="string">
  Restricts which field `query` matches against — `username` or `name`. When omitted, `query` matches either field.
</ParamField>

<SpaceReputationParams variant="user" field="body" />

**Returns** — `Promise<PaginatedResponse<User>>`

***

### fetchFollowersCountByUserId

Returns the total number of followers for a user.

```typescript theme={null}
const { count } = await sublay.users.fetchFollowersCountByUserId({
  userId: "usr_abc123",
});
```

<ParamField body="userId" type="string" required>
  The Sublay user ID.
</ParamField>

**Returns** — `Promise<{ count: number }>`

***

### fetchFollowingByUserId

Returns a paginated list of users that the specified user is following.

```typescript theme={null}
const { data, pagination } = await sublay.users.fetchFollowingByUserId({
  userId: "usr_abc123",
  page: 1,
  limit: 20,
});
```

<ParamField body="userId" type="string" required>
  The Sublay user ID.
</ParamField>

<ParamField body="page" type="number">
  Page number (1-indexed). Defaults to `1`.
</ParamField>

<ParamField body="limit" type="number">
  Results per page. Defaults to `20`.
</ParamField>

<ParamField body="query" type="string">
  Optional search term. Filters the list to users whose username or name contains this text (case-insensitive). When omitted, no filtering is applied.
</ParamField>

<ParamField body="searchFields" type="string">
  Restricts which field `query` matches against — `username` or `name`. When omitted, `query` matches either field.
</ParamField>

<SpaceReputationParams variant="user" field="body" />

**Returns** — `Promise<PaginatedResponse<User>>`

***

### fetchFollowingCountByUserId

Returns the number of users the specified user is following.

```typescript theme={null}
const { count } = await sublay.users.fetchFollowingCountByUserId({
  userId: "usr_abc123",
});
```

<ParamField body="userId" type="string" required>
  The Sublay user ID.
</ParamField>

**Returns** — `Promise<{ count: number }>`

***

### fetchConnectionsByUserId

Returns a paginated list of established mutual connections for a user.

```typescript theme={null}
const { data, pagination } = await sublay.users.fetchConnectionsByUserId({
  userId: "usr_abc123",
  page: 1,
  limit: 20,
});
```

<ParamField body="userId" type="string" required>
  The Sublay user ID.
</ParamField>

<ParamField body="page" type="number">
  Page number (1-indexed). Defaults to `1`.
</ParamField>

<ParamField body="limit" type="number">
  Results per page. Defaults to `20`.
</ParamField>

<ParamField body="query" type="string">
  Optional search term. Filters the list to connections whose connected user's username or name contains this text (case-insensitive). When omitted, no filtering is applied.
</ParamField>

<ParamField body="searchFields" type="string">
  Restricts which field `query` matches against — `username` or `name`. When omitted, `query` matches either field.
</ParamField>

<SpaceReputationParams variant="user" field="body" />

**Returns** — `Promise<PaginatedResponse<EstablishedConnection>>`

***

### fetchConnectionsCountByUserId

Returns the number of established connections for a user.

```typescript theme={null}
const { count } = await sublay.users.fetchConnectionsCountByUserId({
  userId: "usr_abc123",
});
```

<ParamField body="userId" type="string" required>
  The Sublay user ID.
</ParamField>

**Returns** — `Promise<{ count: number }>`

***

## Acting on a user's follows and connections

The functions above are read-only queries about a target user. The functions below **perform an action** on behalf of one user toward another. Because a service key isn't tied to a single user, these routes take both:

* `userId` — the **target** of the action (the user being followed, connected with, etc.), taken from the request path.
* `actingUserId` — the **actor** performing it (the follower, the requester, the user whose perspective a status is from).

<Note>
  Listing and managing the acting user's own follow/connection graph by record ID
  lives on the dedicated [`follows`](/v8/node-sdk/follows) and
  [`connections`](/v8/node-sdk/connections) modules.
</Note>

***

### createFollow

Makes one user follow another.

```typescript theme={null}
const follow = await sublay.users.createFollow({
  userId: "usr_target",
  actingUserId: "usr_follower",
});
```

<ParamField body="userId" type="string" required>
  The user being followed (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user performing the follow (the follower).
</ParamField>

**Returns** — `Promise<Follow>`

***

### deleteFollow

Makes one user unfollow another.

```typescript theme={null}
await sublay.users.deleteFollow({
  userId: "usr_target",
  actingUserId: "usr_follower",
});
```

<ParamField body="userId" type="string" required>
  The user being unfollowed (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user performing the unfollow (the follower).
</ParamField>

**Returns** — `Promise<void>`

***

### fetchFollowStatus

Checks whether the acting user follows the target user.

```typescript theme={null}
const { isFollowing, followId } = await sublay.users.fetchFollowStatus({
  userId: "usr_target",
  actingUserId: "usr_viewer",
});
```

<ParamField body="userId" type="string" required>
  The user whose follow relationship is being checked (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user whose perspective the status is from.
</ParamField>

**Returns** — `Promise<{ isFollowing: boolean; followId?: string }>`

***

### requestConnection

Sends a connection request from the acting user to the target user.

```typescript theme={null}
const connection = await sublay.users.requestConnection({
  userId: "usr_target",
  actingUserId: "usr_requester",
  message: "Let's connect!",
});
```

<ParamField body="userId" type="string" required>
  The user the connection is requested with (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user sending the request (the requester).
</ParamField>

<ParamField body="message" type="string">
  Optional message to include with the request.
</ParamField>

**Returns** — `Promise<ConnectionRequestResponse>` — the created/pending connection.

***

### fetchConnectionStatus

Returns the connection status between the acting user and the target user.

```typescript theme={null}
const status = await sublay.users.fetchConnectionStatus({
  userId: "usr_target",
  actingUserId: "usr_viewer",
});
// status.status is "none" | "pending" | "connected" | "declined"
```

<ParamField body="userId" type="string" required>
  The other user in the connection (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user whose perspective the status is from.
</ParamField>

**Returns** — `Promise<ConnectionStatusResponse>` — a discriminated union on `status`: `"none"`, `"pending"` (with `type` and `connectionId`), `"connected"`, or `"declined"`.

***

### removeConnectionByUserId

Removes the connection between the acting user and the target user — withdrawing a sent request, declining a received one, or disconnecting an established connection, depending on the current state.

```typescript theme={null}
const result = await sublay.users.removeConnectionByUserId({
  userId: "usr_target",
  actingUserId: "usr_viewer",
});
// result.action is "withdraw" | "disconnect" | "decline"
```

<ParamField body="userId" type="string" required>
  The other user in the connection (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user withdrawing, declining, or disconnecting.
</ParamField>

**Returns** — `Promise<RemoveConnectionByUserIdResponse>`

## Acting on a user's blocks

User-to-user **blocking** is part of the trust & safety [moderation bundle](/v8/sdk/moderation/overview#blocking). These functions require that bundle; a project without it returns `403 database/tables-not-available`. Each acts on behalf of one user (the blocker) — pass that user's ID as `actingUserId`.

<Note>
  Blocking severs interaction between two users across every Sublay surface and is **non-disclosing** — the blocked user is never notified. See [Moderation — Blocking](/v8/sdk/moderation/overview#blocking) for the full enforcement behavior.
</Note>

***

### createBlock

Blocks one user on behalf of another. On a new block, existing follows and connections between the pair are torn down (both directions) in one transaction. Idempotent — re-blocking succeeds. Self-block is rejected.

```typescript theme={null}
const block = await sublay.users.createBlock({
  userId: "usr_target",
  actingUserId: "usr_blocker",
});
```

<ParamField body="userId" type="string" required>
  The user being blocked (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user performing the block (the blocker).
</ParamField>

**Returns** — `Promise<Block>` — `{ id, blockerId, blockedId, createdAt }`.

***

### deleteBlock

Removes a block on behalf of the blocker. Idempotent; restores nothing.

```typescript theme={null}
await sublay.users.deleteBlock({
  userId: "usr_target",
  actingUserId: "usr_blocker",
});
```

<ParamField body="userId" type="string" required>
  The user being unblocked (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user performing the unblock (the blocker).
</ParamField>

**Returns** — `Promise<void>`

***

### fetchBlockStatus

Checks whether the acting user blocks the target user. **Outbound-only** — it never reveals whether the target has blocked the acting user.

```typescript theme={null}
const { blocked, blockId } = await sublay.users.fetchBlockStatus({
  userId: "usr_target",
  actingUserId: "usr_blocker",
});
```

<ParamField body="userId" type="string" required>
  The user whose block relationship is being checked (the target).
</ParamField>

<ParamField body="actingUserId" type="string" required>
  The user whose outbound perspective the status is from (the blocker).
</ParamField>

**Returns** — `Promise<{ blocked: boolean; blockId?: string; createdAt?: string }>`

***

### fetchBlockedUsers

Returns a paginated list of the users the acting user has blocked (their outbound blocks), each row carrying a public profile summary. This block list is **private** to the blocker.

```typescript theme={null}
const { data, pagination } = await sublay.users.fetchBlockedUsers({
  actingUserId: "usr_blocker",
  page: 1,
  limit: 20,
});
```

<ParamField body="actingUserId" type="string" required>
  The user whose own block list to read (the blocker).
</ParamField>

<ParamField body="page" type="number">
  Page number (1-indexed). Defaults to `1`.
</ParamField>

<ParamField body="limit" type="number">
  Results per page. Defaults to `20`. Max `100`.
</ParamField>

**Returns** — `Promise<PaginatedResponse<BlockedUser>>`, where each `BlockedUser` is `{ id, blockedUser, createdAt }`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.