---
updatedAt: 2026-07-10T07:53:21.000Z
agentTools:
  projectIndex: https://docs.zetaglobal.com/llms.txt
---

# Contact management

Identify users, update user properties and contacts, and track events.

Once the SDK is initialized, you can begin tracking user identity and behavior. This guide will walk you through updating user profiles, managing email and phone contacts, handling session identity, tracking locations, and sending custom events.

## On this page

* [Update user properties](#update-user-properties)
* [Understand emailId vs email](#understand-emailid-vs-email)
* [Add contact additional info](#add-contact-additional-info)
* [Manage the user identity session](#manage-the-user-identity-session)
* [Listen for identity updates](#listen-for-identity-updates)
* [Set advertising and vendor identifiers](#set-advertising-and-vendor-identifiers)
* [Track location](#track-location)
* [Configure unique client IDs](#configure-unique-client-ids)
* [Track custom and screen events](#track-custom-and-screen-events)

***

## Update user properties

You can update user profile data in two ways: using the builder pattern or by passing a `ZTUser` object directly to `updateUser()`.

### Using the Builder Pattern (Recommended)

The builder pattern provides a clean, fluent API to set multiple properties at once:

```javascript
ZetaClient.user.build((user) => {
  user.uid = 'user-unique-id';
  user.firstName = 'John';
  user.lastName = 'Doe';
  user.name = 'John Doe';
  user.signedUpAt = '2026-07-01T10:30:00Z'; // ISO 8601 format
  user.source = 'organic';
  user.emailId = 'john.doe@example.com';

  user.email = {
    email: 'john.doe@example.com',
    additionalInfo: {},
  };

  user.phone = {
    phone: '+1234567890',
    additionalInfo: {},
  };

  user.additionalProperties = {
    membership_level: 'gold',
    preferred_language: 'en',
    points_balance: 1500,
    is_subscribed: true,
  };

  user.location = {
    latitude: 40.7128,
    longitude: -74.006,
    isForeground: true,
  };
});
```

### Direct Object Update

Alternatively, you can pass a partial `ZTUser` object directly:

```javascript
const user = {
  name: 'Jane Smith',
  firstName: 'Jane',
  lastName: 'Smith',
};
ZetaClient.user.updateUser(user);
```

***

## Understand emailId vs email

When updating user profiles, it is important to distinguish between `emailId` and `email`:

* `user.emailId`: Use when the email address should be treated as the user's primary unique identifier. This populates the **Email** field under **ZMP Identifiers** (and also creates a Contact for this email).
* `user.email`: Use when you want to add the email as a contact channel with additional properties (such as subscription status or preferences). This populates the **Contact** section in ZMP.

> **Important:** If the user's `uid` is set to an email address in the same submission, the `emailId` value is ignored.

***

## Add contact additional info

For email and phone contacts, you can attach rich metadata using the `ZTContactAdditionalInfo` object:

```javascript
const contactAdditionalInfo = {
  subscriptionStatus: 'active',
  inactivityReason: 'none',
  signedUpAt: '2026-07-01T10:30:00Z',
  doubleOptInStatus: 'single',
  phoneType: 'mobile',
  lastSent: '2026-07-01T10:30:00Z',
  lastOpened: '2026-07-01T10:30:00Z',
  lastClicked: '2026-07-01T10:30:00Z',
  properties: { marketing_segment: 'newsletter' },
};

ZetaClient.user.build((user) => {
  user.email = {
    email: 'john.doe@example.com',
    additionalInfo: contactAdditionalInfo,
  };
});
```

### Contact Additional Info Properties

* `preference` (string\[]): Optional. Defaults to `["standard"]` if not provided. Other preferences can be customized in ZMP Settings; only values defined there may be passed here.
* `subscriptionStatus` (string): The contact's subscription state. Supported values are `new`, `active`, or `inactive`.
* `inactivityReason` (string): Optional. Reason for contact inactivity.
* `signedUpAt` (string): Optional. Date when the contact signed up (ISO 8601 format recommended).
* `doubleOptInStatus` (string): Optional. Default is `null`; accepts `single` or `double`.
* `phoneType` (string): Optional. Default is `null`; accepts `mobile` or `landline`.
* `lastSent`**&#x20;/&#x20;**`lastOpened`**&#x20;/&#x20;**`lastClicked` (string): Optional. Date-time values in ISO 8601 format, UTC.
* `properties` (object): Optional. Additional custom key-value pairs for the contact.

***

## Manage the user identity session

The SDK tracks user session-specific properties and events. Setting `uid` or `emailId` starts the user identity session:

```javascript
ZetaClient.user.build((user) => {
  user.uid = 'john123';
  user.emailId = 'john.doe@example.com';
});
```

### Clearing the user session

When a user logs out or switches accounts, clear the identity session on the device. This ensures subsequent events are not associated with the previous user:

```javascript
ZetaClient.user.clear();
```

***

## Listen for identity updates

Native identity callbacks are surfaced to JavaScript through an event listener. Register a listener for `ZetaCoreEvents.IdentityDelegate`, then call `ZetaClient.getCachedBSIN()` to request the current identity state. The event carries the device's `bsin` (Browser Session Identifier), which ZMP assigns shortly after initialization:

```javascript
import { Platform, NativeEventEmitter, DeviceEventEmitter } from 'react-native';
import ZetaClient, { ZetaCoreEvents } from 'zetakit_reactnative';

const eventEmitter = Platform.select({
  ios: new NativeEventEmitter(ZetaClient.bridge),
  android: DeviceEventEmitter,
});

const identityListener = eventEmitter?.addListener(
  ZetaCoreEvents.IdentityDelegate,
  (event) => {
    console.log('Identity updated. BSIN:', event.bsin);
  }
);

// Request the current BSIN; the listener above receives the result.
ZetaClient.getCachedBSIN();
```

Call `setIdentityDelegate()` once after SDK initialization, on both iOS and Android, to receive automatic updates whenever the BSIN changes:

```javascript
ZetaClient.user.setIdentityDelegate();
```

> **Important:** Register the listener **before** calling `getCachedBSIN()`, and call `identityListener?.remove()` when the owning component unmounts.

***

## Set advertising and vendor identifiers

Pass the device's advertising identifier (IDFA on iOS, GAID on Android) and the vendor/app-set identifier to enable cross-device identity resolution:

```javascript
ZetaClient.user.setIdentifierForAdvertiser(idfaString);

ZetaClient.user.setIdentifierForVendor(idfvString);
```

***

## Track location

Pass location coordinates along with a foreground/background flag. The provided location is cached in memory and automatically attached to subsequent events as a property:

```javascript
const location = {
  latitude: 40.7128,
  longitude: -74.006,
  isForeground: true, // Indicates if collected while app is in foreground
};
ZetaClient.user.updateLocation(location);
```

***

## Configure unique client IDs

The SDK supports providing a `unique_client_id` to ZMP for advanced profile-merge scenarios. Before using it, review these conditions:

* **Profile Merging**: No two profiles may have the same `unique_client_id` name:value pair. If a profile is updated with a `unique_client_id` matching another profile, the profiles are merged.
* **Value Constraints**: A `unique_client_id` pair is discarded if the same key already exists with a different value.
* **Coordination**: Enabling support for a unique client ID requires site-configuration updates. Coordinate with your Zeta account team to ensure the configuration is correct.

```javascript
ZetaClient.user.setUniqueClientId('custom_client_key', 'custom_client_value');
```

***

## Track custom and screen events

### Auto-tracked lifecycle events

The SDK automatically tracks the following core lifecycle events:

* `app_installed`: Fired on the very first launch.
* `app_opened`: Fired when the app transitions from the background to the foreground.
* `app_closed`: Fired when the app transitions from the foreground to the background.
* `app_terminated`: Fired when the app is fully closed (iOS only).

### Screen name tracking

Use `trackScreenName()` to track user screen navigation. The provided screen name is cached in memory and attached to subsequent events as a property:

```javascript
ZetaClient.events.trackScreenName('ProductDetailScreen', 'myapp://product/123', {
  category: 'electronics',
});
```

### Custom events

Send custom events by passing an event name and a dictionary of properties. Properties must be JSON-serializable:

```javascript
ZetaClient.events.send('purchase_completed', {
  item_id: 'sku_12345',
  price: 99.99,
  currency: 'USD',
  tags: ['promo', 'summer_sale'],
});
```

> **Important:** If any property value is not serializable to JSON, the entire event data is silently discarded.

***

## Next

Now that you can identify users and track events, continue to configure messaging channels:

* **[Push Notifications](https://docs.zetaglobal.com/docs/react-native-sdk-push-notifications)** — Register device tokens and configure native push setups.
* **[In-App Messaging](https://docs.zetaglobal.com/docs/react-native-sdk-in-app-messaging)** — Foreground messages and email collection.
* **[App Inbox](https://docs.zetaglobal.com/docs/react-native-sdk-app-inbox)** — A persistent, queryable message store.
* **[User Guide: User Properties](https://docs.zetaglobal.com/docs/zmp-user-properties)** — Standard and custom properties in ZMP.
* **[User Guide: Identity Resolution](https://docs.zetaglobal.com/docs/zmp-identity-resolution)** — How profiles merge in ZMP.

## See also

* [Platform support](https://docs.zetaglobal.com/docs/sdk-platform-support) -- feature availability by platform and SDK version.

<br />