---
source_url: https://www.pubnub.com/docs/chat/sdks/users/presence
title: Tracking user presence (deprecated)
updated_at: 2026-06-22T14:37:26.954Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Tracking user presence (deprecated)

:::warning Use Chat SDKs
This documentation is deprecated. Use any of our dedicated [Chat SDKs](https://www.pubnub.com/docs/chat/overview) to quickly implement chat functionality in your application.
:::

Presence lets you track the online and offline status of users and devices in real time and store custom state information. When you have *Presence* enabled in the [Admin Portal](https://admin.pubnub.com), PubNub automatically creates equivalents of all channels that monitor and collect all presence events.

For more information, refer to [User Presence](https://www.pubnub.com/docs/general/presence/overview).

:::note User ID / UUID
User ID is also referred to as **UUID/uuid** in some APIs and server responses but **holds the value** of the **userId** parameter you [set during initialization](https://www.pubnub.com/docs/general/setup/users-and-devices#set-the-user-id).
:::

## Subscribe with Presence

Presence events (such as `join`, `leave`, or `timeout`) are not published on the channel itself. Instead, PubNub publishes them on a dedicated Presence channel that uses the base channel name with a `-pnpres` suffix (for example, `my_channel-pnpres`). By default, subscribing to a channel does **not** subscribe to its `-pnpres` counterpart, so you won't receive any presence events.

To start receiving presence events, you have two options:

* **Use the receivePresenceEvents subscription option** (or its SDK equivalent, such as `withPresence`). This automatically subscribes to the `-pnpres` channel behind the scenes.
* **Subscribe to the -pnpres channel directly** (for example, `my_channel-pnpres`). This is useful when you want to receive presence events without subscribing to the main channel itself, which avoids increasing its occupancy count.

In both cases, you also need to add a [Presence listener](https://www.pubnub.com/docs/chat/sdks/users/setup#add-event-listeners) to handle the incoming events. Make sure you add the listener before you subscribe. PubNub fires a `join` event shortly after the subscription is established, and having the listener already in place ensures you don't miss it.

:::note Enable Presence
Before subscribing with Presence, make sure that [Presence is enabled](https://www.pubnub.com/docs/general/presence/overview#configuration) on your keyset in the [Admin Portal](https://admin.pubnub.com/) and that Presence tracking is configured for [all or selected channels](https://www.pubnub.com/docs/bizops-workspace/presence-management).
:::

For code examples showing how to subscribe with Presence and add Presence listeners, refer to [Subscribe to Presence channel](https://www.pubnub.com/docs/general/presence/presence-events#subscribe-to-presence-channel) in the General documentation.

## Get user occupancy

Use the [hereNow](https://www.pubnub.com/docs/sdks/javascript/api-reference/presence#here-now) method to get information about the current state of users in a channel. The call returns a list of unique users currently subscribed to the channel.

### JavaScript

```js
pubnub.hereNow({
   channels: ['ch-1'],
  includeUUIDs: true,
  includeState: true,
}, (status, response) => {
  // handle status, response
});
```

### Java

```java
pubnub.hereNow()
    .channels(Arrays.asList("ch-1"))
    .includeUUIDs(true)
    .includeState(true)
    .async(result -> {
        result.onSuccess(res -> {
            Map<String, PNHereNowChannelData> channelsMap = res.getChannels();
            for (String channel : channelsMap.keySet()) {
                PNHereNowChannelData channelData = channelsMap.get(channel);
                System.out.println("Channel: " + channelData.getChannelName());
                System.out.println("Occupants: " + channelData.getOccupancy());
                for (PNHereNowOccupantData occupant : channelData.getOccupants()) {
                    System.out.println("\tUserID: " + occupant.getUuid());
                    System.out.println("\tState: " + occupant.getState());
                }
            }
        }).onFailure(exception -> {
            exception.printStackTrace();
        });
    });
```

### Swift

```swift
pubnub.hereNow(on: ["ch-1"], also: true) { result in
  switch result {
  case let .success(response):
    print("Successful HereNow Response: \(response)")
  case let .failure(error):
    print("Failed HereNow Response: \(error.localizedDescription)")
  }
}
```

### Objective-C

```objectivec
[self.client hereNowForChannel: @"ch-1" withVerbosity:PNHereNowUUID
                    completion:^(PNPresenceChannelHereNowResult *result,
                                 PNErrorStatus *status) {
    if (!status) {
        /**
         Handle downloaded presence information using:
            result.data.uuids - list of uuids.
            result.data.occupancy - total number of active subscribers.
         */
    }
    else {
    }
}];
```

### Unity

```csharp
pubnub.HereNow()
      .Channels(new List<string>(){
        "ch-1"
      })
      .IncludeState(true)
      .IncludeUUIDs(true)
      .Async((result, status) => {
        if (status.Error) {
          Debug.Log(string.Format("HereNow Error: {0} {1} {2}", status.StatusCode, status.ErrorData, status.Category));
        } else {
          Debug.Log(string.Format("Channels: {0} {1}", result.TotalChannels, result.TotalOccupancy));
        }
        Debug.Log(status.Error);
      });
```

## User state

Users can set dynamic custom state on one or more channels. This custom state persists on a channel as long as the user stays subscribed to that channel. Some examples of custom states are to add your score, game state, or location in an application if it changes frequently.

Use the [setState](https://www.pubnub.com/docs/sdks/javascript/api-reference/presence#user-state) method to set dynamic state for a user on channels. A state is an object of key/value pairs with any arbitrary data, as long as the data is of type `int`, `float`, or `string`. When you set or update the state, PubNub fires a `state-change` event to other users on the channel.

### JavaScript

```js
pubnub.setState({
  state: {
    mood: 'grumpy',
  },
  channels: ['main'],
}, (status, response) => {
  // handle state setting response
});
```

### Java

```java
JsonObject state = new JsonObject();
state.addProperty("mood", "grumpy");

pubNub.setPresenceState()
        .state(state)
        .channels(Arrays.asList("main"))
        .async(result -> { /* check result */ });
```

### Swift

```swift
pubnub.setPresence(
  state: ["New": "State"],
  on: ["main"]
) { result in
  switch result {
  case let .success(response):
    print("Successful Set State Response: \(response)")
  case let .failure(error):
    print("Failed Set State Response: \(error.localizedDescription)")
  }
}
```

### Objective-C

```objectivec
[self.client setState: @{@"Key": @"Value"} forUUID:self.client.uuid onChannel: @"main"
       withCompletion:^(PNClientStateUpdateStatus *status) {

    if (!status.isError) {

        // Client state successfully modified on specified channel.
    }
    else {

        /**
         Handle client state modification error. Check 'category' property
         to find out possible reason because of which request did fail.
         Review 'errorData' property (which has PNErrorData data type) of status
         object to get additional information about issue.

         Request can be resent using: [status retry];
         */
    }
}];
```

### Unity

```csharp
Dictionary<string, object> state = new Dictionary<string, object>();
state.Add("mood", "grumpy");

pubnub.SetPresenceState()
    .Channels(new List<string> (){"main"})
    .State(state)
    .Async((result, status) => {
        if(status.Error){
            Debug.Log (string.Format("In Example, SetPresenceState Error: {0} {1} {2}", status.StatusCode, status.ErrorData, status.Category));
        } else {
            Debug.Log (string.Format("DateTime {0}, In Example SetPresenceState, result:", DateTime.UtcNow));
            if(result != null){
                if(result.StateByChannels!= null){
                    foreach (KeyValuePair<string, object> key in dict){
                        Debug.Log(string.Format("Channel:{0}, State:{1}", key.Key, pubnub.JsonLibrary.SerializeToJsonString(key.Value)));
                    }
                }
            }
        }
    });
```

You can get state data for any client on a given channel(s) or channel group(s) user the *Get State* API. This is useful to get the state of other clients based on their User ID.

#### JavaScripts

```javascript
pubnub.getState(
  {
    uuid: "john-doe",
    channels: ["main"],
    channelGroups: ["my_channel_group"]
  },
  function (status, response) {
    // handle status, response
  }
);
```

#### Java

```java
pubnub.getPresenceState()
  .channels(Arrays.asList("main"))
  .channelGroups(Arrays.asList("my_channel_group"))
  .uuid("anotherClientUserID")
  .async(result -> { /* check result */ });
```

#### Swift

```swift
pubnub.getPresenceState(
  for: "john-doe",
  on: ["main"],
  and: ["my_channel_group"]
) { result in
  switch result {
  case let .success(response):
    print("Successful Get State Response: \(response)")

  case let .failure(error):
    print("Failed Get State Response: \(error.localizedDescription)")
  }
}
```

#### Objective-C

```objectivec
 [self.pubnub stateForUUID:@"john-doe" onChannel:@"chats.room1"
      withCompletion:^(PNChannelClientStateResult *result, PNErrorStatus *status) {

  if (!status) {

  }
  else {

  }
}];
```

#### Unity

```csharp
pubnub.GetPresenceState()
    .UUID("john-doe")
    .Channels(new List<string> (){"main"})
    .ChannelGroups(new List<string> (){"my_channel_group"})
    .Async((result, status) => {

        if(status.Error){
            Debug.Log (string.Format(" GetPresenceState Error: {0} {1} {2}", status.StatusCode, status.ErrorData, status.Category));
        } else {
            Debug.Log (string.Format("DateTime {0}, In Example GetPresenceState, result:", DateTime.UtcNow));
            if(result != null){
                if(result.StateByChannels!= null){
                    foreach (KeyValuePair<string, object> key in dict){
                        Debug.Log(string.Format("Channel:{0}, State:{1}", key.Key, pubnub.JsonLibrary.SerializeToJsonString(key.Value)));
                    }
                }
            }
        }
    });
```

For more details on working with user state, refer to [Setting Custom Presence State](https://www.pubnub.com/docs/general/presence/presence-state).

## Presence events

PubNub triggers presence events as users come online or go offline from the application. Clients can receive these events directly, or you can use webhooks on a server to keep a user's online/offline status up to date. Use these events to keep track of users coming and going and the total occupancy of the channel.

| Events | Description |
| --- | --- |
| `join` | Fires when a user subscribes to a channel. |
| `leave` | Fires when a user unsubscribes from a channel. |
| `timeout` | Fires when a connection to a channel is lost and the subscriber hasn't been seen in 320 seconds (just over 5 minutes). |
| `state-change` | Fires anytime the user's state is changed using the state API (function signature varies by SDK). |
| `interval` | Fires to provide an occupancy count. The default setting for the Presence Interval property is 10 seconds, which is configurable on your Admin Portal's Presence add-on panel. |

Join event:

```json
{
    "channel": "room-1",
    "subscription": null,
    "actualChannel": null,
    "subscribedChannel": "room-1-pnpres",
    "action": "join",
    "timetoken": "15119466699655811",
    "occupancy": 2,
    "uuid": "user1",
    "timestamp": 1511946669
}
```

Leave event:

```json
{
    "channel": "room-1",
    "subscription": null,
    "actualChannel": null,
    "subscribedChannel": "room-1-pnpres",
    "action": "leave",
    "timetoken": "15119446002445794",
    "occupancy": 1,
    "uuid": "user1",
    "timestamp": 1511944600
}
```

Timeout event:

```json
{
   "channel": "room-1",
   "subscription": null,
   "actualChannel": null,
   "subscribedChannel": "room-1-pnpres",
   "action": "timeout",
   "timetoken": "15119519897494311",
   "occupancy": 3,
   "uuid": "user1",
   "timestamp": 1511951989
}
```

User state change event:

```json
{
    "channel": "room-1",
    "subscription": null,
    "actualChannel": null,
    "subscribedChannel": "room-1-pnpres",
    "action": "state-change",
    "state": {
        "mood": "grumpy"
    },
    "timetoken": "15119477895378127",
    "occupancy": 5,
    "uuid": "user1",
    "timestamp": 1511947789
}
```

Interval event:

```json
{
    "channel": "room-1",
    "subscription": null,
    "actualChannel": null,
    "subscribedChannel": "room-1-pnpres",
    "action": "interval",
    "timetoken": "15119477895378127",
    "occupancy": 2,
    "timestamp": 1511947739
}
```