> ## 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 and update user profiles, check usernames, and act on a specific user's follow and connection graph

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 reads and updates user profiles and lets the logged-in user act on the social graph of an **arbitrary target user**, addressed by `userId`.

In the follow and connection helpers below, `userId` is always the **target** — the user being followed, the other party in a connection, or the user whose followers you're reading. The **actor** is always the logged-in token holder. To act on your own graph (your own followers, the users you follow, your own connections and pending requests), use the [`follows`](/v8/js-sdk/follows) and [`connections`](/v8/js-sdk/connections) modules instead.

***

### fetchUserById

Fetches a user 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 by your application's own user identifier.

```typescript theme={null}
const user = await sublay.users.fetchUserByForeignId({
  foreignId: "external-user-42",
});
```

<ParamField body="foreignId" type="string" required>
  Your application's user identifier.
</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 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>`

***

### fetchUserSuggestions

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

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

```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 }>`

***

### updateUser

Updates a user's profile, optionally uploading a new avatar and/or banner image. A user token may only update its own profile.

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

<ParamField body="userId" type="string" required>
  The Sublay user ID to update. A user token may only update itself.
</ParamField>

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

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

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

<ParamField body="bio" type="string">
  New bio text.
</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 }`, or `null` to clear it.
</ParamField>

<ParamField body="metadata" type="object">
  Updated public metadata (`Record<string, any>`).
</ParamField>

<ParamField body="secureMetadata" type="object">
  Updated secure metadata (`Record<string, any>`, not exposed in public responses).
</ParamField>

<ParamField body="avatarFile" type="object">
  A new avatar image to upload: `{ file: Blob | File; options: ImageOptions }`.
</ParamField>

<ParamField body="bannerFile" type="object">
  A new banner image to upload: `{ file: Blob | File; options: ImageOptions }`.
</ParamField>

<Note>
  When `avatarFile` or `bannerFile` is supplied the request is sent as
  `multipart/form-data` carrying the browser `File`/`Blob`. The image `options`
  (an `ImageOptions` object whose `mode` selects a processing strategy) are sent
  alongside the file.
</Note>

```typescript theme={null}
// Uploading an avatar from an <input type="file"> element
const input = document.querySelector<HTMLInputElement>("#avatar-input");
const file = input.files[0];

const updatedUser = await sublay.users.updateUser({
  userId: "usr_abc123",
  avatarFile: {
    file,
    options: {
      mode: "exact-dimensions",
      dimensions: { default: { width: 256, height: 256 } },
      format: "webp",
    },
  },
});
```

**Returns** — `Promise<AuthUser>`

***

### 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) for which comments the list contains.

```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="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 the viewer is [block-edged](/v8/sdk/moderation/overview#blocking) with are treated. `"exclude"` (default) leaves them out; `"include-outbound-blocked"` re-includes only the viewer's own outbound-blocked authors, marked `isAuthorBlocked: true`. No effect without the `moderation` bundle.
</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 target 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 whose followers to fetch.
</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<FollowListItem>>`

***

### fetchFollowersCountByUserId

Returns the total number of followers for the target 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 target 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 whose following list to fetch.
</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<FollowListItem>>`

***

### fetchFollowingCountByUserId

Returns the number of users the target 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 the target user's established connections.

```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 whose connections to fetch.
</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 the target 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 }>`

***

### createFollow

The logged-in user follows the target user.

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

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

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

***

### deleteFollow

The logged-in user unfollows the target user.

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

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

<Note>
  This unfollows by the target's `userId`. To delete one of your own follow
  relationships by its record ID, use
  [`follows.deleteFollow`](/v8/js-sdk/follows), which takes a `followId`.
</Note>

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

***

### fetchFollowStatus

Returns whether the logged-in user follows the target user.

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

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

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

***

### requestConnection

Sends a connection request from the logged-in user to the target user.

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

<ParamField body="userId" type="string" required>
  The user the connection is requested with (the target).
</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 logged-in user and the target user.

```typescript theme={null}
const status = await sublay.users.fetchConnectionStatus({
  userId: "usr_target",
});
```

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

**Returns** — `Promise<ConnectionStatusResponse>`

***

### removeConnectionByUserId

Removes the connection with 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",
});
```

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

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

## Blocking

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`. The blocker is the **token holder** — there is no `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 a user. On a new block, existing follows and connections between the pair are torn down (both directions). Idempotent — re-blocking succeeds. Self-block is rejected server-side.

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

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

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

***

### deleteBlock

Unblocks a user. Idempotent; restores nothing.

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

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

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

***

### fetchBlockStatus

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

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

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

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

***

### fetchBlockedUsers

Returns a paginated list of the users the token holder 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({
  page: 1,
  limit: 20,
});
```

<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<BlockedUserListItem>>`, where each `BlockedUserListItem` is `{ id, blockedUser, createdAt }`.


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