OtisDocs

Reference

Identity

User and group identity, properties, and reading identity in the browser.

The SDK captures who did what: user identity and group membership. Identity flows through to every span and event automatically once set. All functions below work in both server and browser runtimes.

For session and chat IDs (sessionId, chatId) and auth-provider pass-through, see Sessions.

identifyUser

Link the current session to a user and optionally record group memberships:

import { identifyUser } from "@runotis/sdk";

identifyUser("user-123", { company: "acme", team: "eng" });

A user can belong to at most one group per group type, and at most 10 group types per call. The SDK throws if you pass more than 10.

In the browser, identifyUser also auto-captures session properties (user agent, language, UTM parameters). See Browser & consent for the exact keys.

setUserProperties

Set persistent user-level properties for cohort segmentation:

import { setUserProperties } from "@runotis/sdk";

setUserProperties("user-123", { plan: "pro", role: "developer" });

Feature flag exposures

Use the flag. prefix to record flag assignments:

setUserProperties("user-123", {
  "flag.dark_mode": "variant_b",
  "flag.new_onboarding": "control",
});

If you use a feature-flag platform like LaunchDarkly, Statsig, PostHog, GrowthBook, Unleash, or Hypertune, the Feature flags guide shows how to wire them through OpenFeature so every flag evaluation auto-records the assignment — no manual setUserProperties calls at each evaluation site.

Flag-property capture binds to the identified user. Anonymous sessions (no identifyUser or targetingKey set) are skipped: the assignment has no user to attach to.

setGroupProperties

Set persistent properties on a group (company, team, workspace):

import { setGroupProperties } from "@runotis/sdk";

setGroupProperties("company", "acme", { plan: "enterprise", size: 500 });

Property keys and values

The rules below apply to all property calls: setUserProperties, setGroupProperties, and the group types passed to identifyUser.

Key format

Property keys, group types, and group IDs are trimmed and lowercased before use, and must match [a-z0-9_.-]+. The SDK throws on invalid input.

ExampleResult
"plan"plan
"Plan"✅ normalized to plan
"first-name"✅ hyphens allowed
"flag.dark_mode"✅ dots allowed (used for flag. prefix)
"company name"❌ throws; spaces not allowed
"role!"❌ throws; ! not in allowed set

Because keys are lowercased, "Plan" and "plan" are the same key. Keep key spelling stable across call sites. Inconsistent casing or underscore/hyphen mixing won't produce separate keys; it'll just churn the value on every write.

Per-key merge semantics

Each call sends only the keys you pass; it does not replace the full property set for the entity. Setting properties incrementally works as you'd expect:

setUserProperties("user-123", { plan: "pro" });
// ...later, somewhere else...
setUserProperties("user-123", { role: "admin" });
// Both `plan` and `role` are now set on user-123.

Each key is stored independently. Within a single key, the most recent write wins (unless the key is immutable; see below).

Immutable (first-writer-wins) properties

Some property keys represent initial state that shouldn't change after first set: signup_source, first_plan, referral_code, initial_referrer, and similar. For these keys, Otis treats the first value written as authoritative and silently discards later writes to the same key.

You don't mark keys as immutable from the SDK

There's no client-side API to opt a property into first-writer-wins. Otis identifies immutable candidates automatically on the server based on property semantics. Keys named after initial state (signup_source, first_*, initial_*, *_at_signup) are typical candidates.

Two practical implications:

  • Name keys after what they represent. Use present-tense names (plan, role, team_size) for things that change over time. Reserve initial-state names (first_plan, signup_source, original_referrer) for things that genuinely shouldn't change.
  • If a write appears not to take effect, the key may be immutable. Attempting to overwrite an immutable key from application code is a silent no-op. If you genuinely need to reset one, do it from the Otis dashboard's property configuration rather than from application code.

Reading identity in the browser

In a Next.js client component, the useOtis() hook exposes the current userId, sessionId, and isAnonymous, plus identifyUser and sendFeedbackSignal. Values are reactive — components re-render when identifyUser() is called or the session rotates. See Next.js for the full hook reference.

On this page