Using Private Attributes
Use private attributes in Statsig SDKs to evaluate feature gates and experiments without sending sensitive user data to Statsig servers.
Evaluating feature gates, dynamic configs, segments, and experiments without logging user data
Private attributes let you evaluate feature gates, dynamic configs, segments, and experiments in Statsig without sending sensitive user data to Statsig's servers. You mark any field used for evaluation as private, and Statsig strips it from the user object before logging. Use them when legal requirements or internal policy prevent you from sending PII to a third party.
How private attributes work
You can make any field used for evaluation private.
Making the userID field private breaks your experience in the Statsig console: the Users tab, metrics charts, event logs, and Pulse metrics won't populate accurately. To hide the user ID, use a stable, one-way hash to preserve the ability to analyze user behavior.
For example, suppose you want to use an email condition:
- Create a feature gate with a condition that passes on certain emails or domains
- Pass
email: email@domain.cominprivateAttributesrather than in the top levelemailfield - Test it out in the
Test Gateconsole after saving changes

When evaluating rules and conditions, Statsig checks for fields at the top level, then in custom attributes, and finally in privateAttributes. Not passing email at the top level causes the evaluator to check custom and then privateAttributes before evaluating the condition.
To keep ip and userAgent private while still using browser or IP checks in the console, place them in the privateAttributes dictionary instead of at the top level of the user object. The same applies to country, locale, custom fields, and so on.
The statsig-node SDK illustrates how this works (this is also how Statsig servers evaluate gates): https://github.com/statsig-io/node-js-server-sdk/blob/d1cb9431fb68b40f840254fce70363de1dc51aa5/src/Evaluator.js#L374
Don't provide a privateAttribute key anywhere else in the user object. Statsig drops the entire privateAttributes dictionary, but still logs any duplicate fields at the top level or in the custom object (and evaluates against them).
Client vs server SDKs
Client and single-user environment SDKs send the user object with the initialize call to evaluate the user against every gate in your Statsig project. The SDK sends privateAttributes with this call, but Statsig servers don't store or log them. Statsig servers require the privateAttributes field to evaluate gate, config, and experiment conditions, then strip it from the user object before any logging occurs. Client SDKs remove privateAttributes before logging any events; the SDK uses the field only for gate evaluation.
Example with the @statsig/js-client SDK: https://github.com/statsig-io/js-client-monorepo/blob/17fb70f1bea00d07e156cf6ff03b8024bbc1b197/packages/client-core/src/EventLogger.ts#L325
If client SDK behavior doesn't meet your needs, Statsig server SDKs provide a stronger guarantee: privateAttributes never leave your server. Server SDKs download the definition of each gate, config, and experiment and evaluate them locally.
privateAttributes stripped from event logs with the statsig-node SDK: https://github.com/statsig-io/node-js-server-sdk/blob/d1cb9431fb68b40f840254fce70363de1dc51aa5/src/LogEvent.js#L21
Evaluation happening locally to the server on privateAttributes in the statsig-node SDK: https://github.com/statsig-io/node-js-server-sdk/blob/d1cb9431fb68b40f840254fce70363de1dc51aa5/src/Evaluator.js#L374
privateAttributes in the SDK you're using, or reach out on Slack for guidance.privateAttributes as part of the user object on the server, in the getClientInitializeResponse(<user object>) call. The getClientInitializeResponse call generates all assignments locally on your server. You then pass these assignments as initializeValues to the client SDK, which eliminates the need to send any user attributes from the client device to Statsig.Event logging
The SDK strips the privateAttributes field from the user object for any logEvent calls on both client and server SDKs. On server SDKs, you can omit the field from logEvent calls because no evaluation occurs at log time. If you use the same user object throughout, the SDK handles dropping privateAttributes for you.
Was this helpful?