Skip to content

User Context ​

This page documents the Web SDK user-context API: fetching the current viewer with web.Context.User() and looking up community members with the web.User.* methods. All run as the visiting user, authorized by the community session cookie.

Where the examples run

Every example imports web from @gainsight-hub/sdk and runs in a widget or in a sitewide script registered with "type": "module". See SDK. web forwards to the community's Web SDK and throws on pages without it.

TIP

Prefer these SDK methods over a Connector for public member data — they use the browser session and need no server-side secrets. Private account data, such as email addresses, is not available through the SDK.

Context: Current Viewer ​

Context.User() ​

Fetches the current user for the active session. This is the direct replacement for reading inSidedData.user.

Returns: Promise<User>

  • Resolves to a User object for an authenticated visitor.
  • For an unauthenticated (guest) visitor, resolves to the guest projection — a User object with userId: null and username: "guest". It is never null; branch on userId to tell guest from member.
  • Throws if called outside a browser environment or if the network request fails.

Takes no parameters. For a signed-in visitor, badges and profileFields are always present (an empty array when there is nothing to show) and rank is present when the user has one. The guest result has badges: [] and no profileFields or rank; see Guest Handling.

Private profile fields are included

Context.User() returns the signed-in member's own profile fields, including the ones set to Private (visibility: 2). Private keeps a field from other members. It does not keep it from scripts running on the community page (analytics, tag managers, chat tools, other widgets), which can read it through the SDK. Do not send profileFields to external services or logs, and filter on visibility === 1 when you need only public values. User.getById() called with the viewer's own ID returns public fields only.

Private account data is never included, even for the signed-in viewer: email address, private-message counts, subscription count, login/registration source, email-campaign opt-in, and custom role IDs.

Checking authenticated vs. guest: branch on userId, not on null.

javascript
import { web } from "@gainsight-hub/sdk";

const me = await web.Context.User()

if (me.userId === null) {
  // Guest — show a login prompt or public-only content
} else {
  // Authenticated — personalize the UI
  console.log(me.username)        // 'janedoe'
  console.log(me.userId)          // 101
  console.log(me.rank?.name)      // 'Regular'
  console.log(me.badges)          // [{ id: 100, title: 'First post', url: 'https://assets.example.com/…_thumb.png' }]
  console.log(me.profileFields)   // [{ id: 3, title: 'Seniority', value: 'senior', ... }]
}

User: Lookup and Listing Methods ​

All methods below are browser-only and operate on the community user directory. They return the public User shape — no private account data (email, private-message counts, login source) and no private-visibility profile fields are exposed by any of them.

User.getById(id) ​

Fetch a single user by numeric ID.

Returns: Promise<User | null> — resolves to null if the ID does not resolve.

ParameterTypeDescription
idnumber | stringUser ID. Numeric strings are accepted and coerced.
javascript
import { web } from "@gainsight-hub/sdk";

const user = await web.User.getById(101)
if (user) {
  console.log(user.username, user.rank?.name)
}

User.getUsersById(ids) ​

Fetch multiple users by an array of IDs in one request (batch lookup). Unresolved IDs are silently omitted — the result array may be shorter than the input.

Up to 100 IDs per request. IDs beyond the hundredth are dropped, not refused.

Returns: Promise<User[]> — array of resolved users. Order is not guaranteed to match the input; match results by userId (e.g. with .find()), not by position. Each item is a User object.

ParameterTypeDescription
idsArray<number | string>User IDs to fetch. Numeric strings are accepted.
javascript
import { web } from "@gainsight-hub/sdk";

const users = await web.User.getUsersById([101, 202, 303])
// users.length may be less than 3 if any ID did not resolve
users.forEach((u) => console.log(u.userId, u.username))

User.list(options?) ​

List and filter community members with pagination, sorting, and filtering. Returns a paginated result.

Returns: Promise<{ totalItems: number, items: User[] }>

  • totalItems: total number of users matching the filters (before pagination).
  • items: users on the requested page. Defaults to 25 per page (max 100) when size is omitted.
  • Every row is a fully hydrated User (rank, badges, profileFields included), so a full page of 100 is a heavier payload — request only the size you need.
OptionTypeDefaultDescription
searchstring—Substring match on username. An authenticated caller may also pass an email address here; a guest who does gets a 403 (guests can still search by username).
rolenumber[]—Filter by main role ID(s). OR semantics.
joinDateDateFilter—Filter on registration date.
lastActivityDateFilter—Filter on last activity date.
lastVisitDateFilter—Filter on last visit (login) date.
topicsNumberFilter—Filter on topics created.
repliesNumberFilter—Filter on replies created.
pointsNumberFilter—Filter on points.
pagenumber11-indexed page number.
sizenumber25Page size (1–100).
sortUserSortField'userId'Sort field.
order'asc' | 'desc''desc'Sort direction.

Role, badge, and profile-field IDs used in the filters and examples below are specific to each community. Find your community's values in its admin settings (the Roles, Badges, and Profile Fields sections) instead of reusing the sample IDs.

DateFilter — ISO-8601 datetime, ISO-8601 duration relative to now (e.g. 'P3D' = last 72 h), or { from?, to? } range:

javascript
import { web } from "@gainsight-hub/sdk";

// Users who joined in the last 7 days
const { items } = await web.User.list({ joinDate: 'P7D' })

// Users who joined in a specific range
const { items } = await web.User.list({
  joinDate: { from: '2024-01-01T00:00:00Z', to: '2024-12-31T23:59:59Z' }
})

NumberFilter — exact integer or { eq?, gt?, gte?, lt?, lte? } range:

javascript
import { web } from "@gainsight-hub/sdk";

// Users with 10 or more topics
const { items } = await web.User.list({ topics: { gte: 10 } })

UserSortField values: 'userId' | 'username' | 'replies' | 'topics' | 'points' | 'lastVisit' | 'joinDate' | 'lastActivity'

There is no sort by total post count. topics and replies sort separately, and any other sort value returns HTTP 400.

Example — list moderators sorted by last visit:

javascript
import { web } from "@gainsight-hub/sdk";

const { totalItems, items } = await web.User.list({
  role: [7],
  sort: 'lastVisit',
  order: 'desc',
  size: 20
})
console.log(`${items.length} of ${totalItems} moderators`)
items.forEach((u) => console.log(u.username, u.lastVisit))

Example — paginate through all users:

javascript
import { web } from "@gainsight-hub/sdk";

const size = 100
let page = 1
let total = Infinity

while ((page - 1) * size < total) {
  const result = await web.User.list({ page, size })
  total = result.totalItems
  result.items.forEach((u) => console.log(u.userId, u.username))
  page++
}

User.getRecentlyActive(limit?) ​

Convenience wrapper around list(). Returns users sorted by lastVisit descending.

Returns: Promise<User[]>

ParameterTypeDefaultDescription
limitnumber10Maximum users to return.
javascript
import { web } from "@gainsight-hub/sdk";

const recentUsers = await web.User.getRecentlyActive(5)
recentUsers.forEach((u) => console.log(u.username, u.lastVisit))

User.getByRole(role, options?) ​

List users whose main role is one of the specified roles. Custom roles are not matched.

Returns: Promise<{ totalItems: number, items: User[] }> — defaults to 25 per page (max 100), same as list().

ParameterTypeDescription
rolenumber | number[]Role ID or array of IDs. OR semantics.
optionsobjectSame pagination and sort options as list() (minus role).
javascript
import { web } from "@gainsight-hub/sdk";

// Users with role id 7, first page
const { totalItems, items } = await web.User.getByRole(7, { size: 50 })

// Users with any of these roles
const { items } = await web.User.getByRole([7, 9])

User.search(query) ​

Search users by a query string. Returns a reduced field set optimized for mention/autocomplete UIs — use User.list() when you need the full user object.

Requires an authenticated session — a guest calling search() receives a 403 (this is a different endpoint from list(), and it is not open to guests at all).

Returns: Promise<UserSearchResult[]> — up to 25 users. This limit is fixed; search() takes no size parameter.

ParameterTypeDescription
querystringNon-empty search query

UserSearchResult object shape:

FieldTypeNotes
userIdnumberCanonical user ID.
usernamestringDisplay username. "guest" on the guest projection.
profileUrlstring | nullAbsolute URL to the profile page.
avatarstringAvatar URL, or "" when none.
userTitlestringDisplay title.
reputationnumber | nullReputation level, not the raw points. Omitted when no level is available for the user.
isBannedbooleantrue if the user holds the banned role.
badgesBadge[]Hydrated badges, without id.
rankRank | nullHydrated rank, without id.
javascript
import { web } from "@gainsight-hub/sdk";

const results = await web.User.search('jane')
results.forEach((u) => console.log(u.userId, u.username, u.avatar))

User Object Shape ​

Every method above except search() returns the unified User model. search() returns the reduced UserSearchResult shape described under User.search(query).

A field with no value is omitted from the User object rather than returned as null. Fields marked (optional) in the tables below can be absent: read them with optional chaining (user.rank?.name) or a presence check, not === null. userId and its mirror id are the exception: they are always present, and null on the guest result. Inside nested objects the rule differs: the Rank object returns its empty fields (such as color or iconUrl) as null, and a ProfileField value can be null.

Not returned today: firstName, lastName, signature, customRoles, solved, likesReceived, likesGiven, followers and following. The generated TypeScript User type still declares some of these as optional, but the backend does not populate them, so reading them always gives undefined.

Core identity fields ​

FieldTypeNotes
userIdnumber | nullCanonical user ID. null only on the guest projection. Use this in new code.
idnumber | nullDeprecated mirror of userId. Kept for backward compatibility; migrate to userId.
usernamestringDisplay username. "guest" on the guest projection.
namestringDeprecated alias of username.
avatarstringAvatar URL, or empty string "" when none.
profileUrlstring (optional)Absolute URL to the user's profile page (e.g. "https://community.example.com/members/janedoe-101", where 101 is the user ID). The path holds the username in a URL-safe form (lowercase, symbols become hyphens: mod_99 becomes mod-99), so use this value and do not build the link from username. Omitted on the guest result.
urlstring (optional)Deprecated mirror of profileUrl. Kept for backward compatibility; migrate to profileUrl.
userTitlestring (optional)Display title (custom title or rank name; empty string if hidden). Omitted on the guest result.
companyIdstring (optional)The external ID of the company the user is linked to. Omitted when the user is not linked to a company. It is not the community's "Company" profile field, which is an ordinary text field in profileFields.

Role and moderation fields ​

FieldTypeNotes
isBannedbooleantrue if the user holds the banned role.
rolenumber (optional)Main role ID as an integer. Community-specific. Omitted on the guest result.
isModeratorbooleanGuest result only (false). A signed-in user's object never has it.
mainRolestringGuest result only ("roles.guest"). A signed-in user's object never has it.

A signed-in user's object has no moderator flag. role is the main role only: custom roles are not returned, and getByRole() and list({ role }) match the main role only. To show something to moderators, compare role with your community's moderator, community manager and administrator role IDs (they are specific to each community, so look them up in the admin Roles settings and never assume the sample ID 7). A role granted only as a custom role is not visible to the SDK.

Rank fields ​

FieldTypeNotes
rankIdnumber (optional)Rank ID. Omitted when the user has no rank.
rankRank (optional)Hydrated rank with display styling. Omitted when the user has no rank; see below.

A user has no rank until the community's rank conditions assign one (a new member starts without one), and loses it when no rank condition matches. rank is also omitted when the community has turned the rank feature off or the rank no longer exists; in those two cases rankId can still be present, so test rank, not rankId, before rendering a rank.

Rank object shape:

FieldTypeNotes
idnumberRank ID
namestringRank display name (e.g. "Regular")
colorstring | nullHex color for the rank name (e.g. "#3366ff")
isBoldbooleanWhether the rank name should be bold
isItalicbooleanWhether the rank name should be italic
isUnderlinebooleanWhether the rank name should be underlined
iconstring | nullIcon identifier
iconUrlstring | nullURL to the rank icon image
avatarIconstring | nullAvatar icon identifier
avatarIconUrlstring | nullURL to the avatar icon image

Badge fields ​

FieldTypeNotes
badgesBadge[]Hydrated badges. Always present. Unresolvable badge IDs are dropped — the array only contains objects.

Badge object shape:

FieldTypeNotes
idnumberBadge ID
titlestringBadge display name (e.g. "First post")
urlstringAbsolute URL of the badge's thumbnail image (e.g. "https://assets.example.com/attachment/3f2a_thumb.png"). Empty string "" when the badge has no image.

Group and profile fields ​

FieldTypeNotes
groupsnumber[]Group IDs the user belongs to.
profileFieldsProfileField[]Custom profile fields stored for this user that the viewer is allowed to see, in the community's display order; see below.

profileFields is not the community's full field list: one user can have 1 entry and another 17, so look a field up by id, never by position. To find a field's id, log your own web.Context.User() and read profileFields. A field titled "Email" is an ordinary profile field, not the account email address, which is never returned.

ProfileField object shape:

FieldTypeNotes
idnumberProfile field ID
titlestringProfile field label (e.g. "Seniority")
typestringField type: 'text', 'check', 'select', 'radio', 'birthday' or 'date'.
valuestring | nullThe value as stored. An empty text field is ""; an unset select, check or date field is null. Treat both as "no value". For select and radio fields it is the stored option value (for example "india"), not necessarily the label members see.
visibilitynumberVisibility level of the field: 1 public, 2 private. Only Context.User() returns private fields (the viewer's own); every other method returns public ones. Hidden fields are never returned.

Activity and engagement fields ​

FieldTypeNotes
topicsnumberTopics created.
repliesnumberReplies created.
pointsnumberPoints.
reputationnumber (optional)Reputation level, not the raw points (points is a separate field). Omitted when no level is available for the user.
userLevelnumber (optional)Deprecated alias of reputation, returned by getUsersById() only.

Date fields ​

All three are UTC timestamps as ISO-8601 strings, written with a +00:00 offset (for example "2024-03-18T09:30:00+00:00"). new Date(value) parses them.

FieldTypeNotes
joinDatestring (optional)Registration timestamp as ISO-8601 datetime (e.g. "2024-03-18T09:30:00+00:00"). Omitted on the guest result. Not a Unix timestamp — the legacy inSidedData.user.joindate was a Unix int; this is a string.
lastActivitystring (optional)Last activity timestamp (ISO-8601). Equals joinDate until the user has activity. Omitted on the guest result.
lastVisitstring (optional)Last visit/login timestamp (ISO-8601). Omitted when the user has never visited, and on the guest result.

No private account data

The User object never carries private account data — no email address, private-message counts, subscription count, login/registration source, email-campaign opt-in, or custom role IDs. These are stripped on the server for every method, including Context.User() for the authenticated viewer. Private profile fields are the one exception: only Context.User() returns them, and only the viewer's own. Obtain private account data server-side through a Connector if a widget genuinely needs it.


Guest Handling ​

When an unauthenticated visitor calls web.Context.User(), it resolves to the guest projection — a User object with userId: null, username: "guest", and zeroed counts. It is never null, so always branch on me.userId === null before treating the viewer as a member.

The guest result contains only these keys: userId and id (both null), username and name ("guest"), avatar (""), badges and groups (both []), isBanned (false), isModerator (false), mainRole ("roles.guest"), and topics, replies and points (all 0). It has no profileFields, rank, role, profileUrl or date fields, so guard reads such as me.profileFields and me.joinDate behind the userId === null check.

For web.User.list() and related methods, guests get the public field set. Email-based search requires an authenticated session (guests receive a 403).

Private communities

On a private community, unauthenticated requests are redirected to the login page instead of returning data, so the SDK call rejects rather than resolving. Wrap calls in try/catch and treat a rejection as "not signed in."


Field Availability by Method ​

User.search() returns a reduced set optimized for autocomplete. Every other method — Context.User(), getById(), getUsersById(), list(), getByRole(), getRecentlyActive() — returns the full public User shape. No method exposes private account data, and only Context.User() returns private-visibility profile fields (the viewer's own).

Field groupContext.User()Other User.* lookupssearch()
Base scalars (userId, username, avatar, …)✅✅Partial
rank✅✅✅ (without id)
badges✅✅✅ (without id)
profileFields (public visibility)✅✅❌
profileFields (private visibility, the viewer's own)✅❌❌
Private account data (email, PM counts, subscriptions, login/register source, email-campaign opt-in, custom role IDs)❌❌❌

Legacy Field Migration ​

If you are migrating code that reads inSidedData.user (or the getUsersById response, which carried url and userLevel), use this table to find the canonical field name:

Legacy fieldCanonical field
userid (int)userId
nameusername
urlprofileUrl
userLevelreputation
rankNamerank.name
rankIconrank.avatarIconUrl (rank.iconUrl is the separate username icon)
topicsCounttopics
repliesCountreplies
joindate (Unix int)joinDate (ISO-8601 string — different type)

Some legacy inSidedData.user fields have no canonical equivalent:

  • Private data — email, pmUnreadCount, pmTotalCount, subscriptions, loginSource, registerSource. No SDK method exposes them; read them server-side through a Connector if required.
  • Engagement counts — solvedCount, likes, likes_given. No SDK method returns them.
  • Moderation — isModerator, mainRole and the comma-separated role. The SDK returns isModerator and mainRole only for guests, and role is the main role ID alone, so a script that gated on isModerator must compare role with your moderator role IDs instead. Custom roles are not returned.

Profile fields are broader than before

inSidedData.user.profileFields held public fields only. Context.User().profileFields also holds the viewer's own private fields, so a script that was safe to log or forward with the legacy list is not safe to do so with this one.

joinDate type change

inSidedData.user.joindate was a Unix timestamp (integer). The canonical joinDate is an ISO-8601 datetime string. If you compare or format dates, update your parsing logic.


Error Handling ​

All methods throw on failure. Wrap calls in try/catch:

javascript
import { web } from "@gainsight-hub/sdk";

try {
  const me = await web.Context.User()
  if (me.userId === null) {
    // Guest — handle gracefully
  } else {
    // Use me.username, me.rank, etc.
  }
} catch (err) {
  console.error('Failed to fetch user context:', err.message)
}

Common error conditions:

ScenarioCause
"Context.User can only be called in a browser environment"Called outside a browser (e.g. during SSR or in Node).
HTTP 400Invalid filter operator, an unsupported sort field (e.g. sort=email), or an unsupported filter passed to list().
HTTP 403A guest called User.search() (authenticated-only), or passed an email-address term to list({ search }). Guest username search via list() is allowed.
HTTP 5xxA server error occurred. Retry after a brief delay.
Rejected call (private community)Unauthenticated requests are redirected to a login page instead of returning data; catch it and treat as "not signed in."

The SDK surfaces these as a rejected promise (a thrown Error), not as a structured status code you can switch on — the "Scenario" column describes the underlying cause, not a machine-readable field. Branch on whether the call rejected, and inspect err.message for detail; there is no reliable err.status to implement per-code handling.


Next Steps ​

  • SDK Examples — runnable, copy-paste code for Context.User() and the User.* methods
  • SDK Reference — full reference for all SDK namespaces
  • SDK — how the import resolves and project setup
  • Passing User Context — use server-side template variables when building Connectors that need the viewer's identity

Gainsight CC Developer Portal