---
source_url: https://www.pubnub.com/docs/general/data-sync/users-channels-memberships
title: Users, channels, and memberships in DataSync
updated_at: 2026-09-08T16:52:30.000Z
---

# Users, channels, and memberships in DataSync

## Documentation index

To discover more PubNub resources:

1. Fetch [PubNub's llms.txt](https://www.pubnub.com/llms-full.txt) for a list of available pages in Markdown format.
2. Identify relevant URLs from that index.
3. Fetch the target pages.

Do not assume a path exists, always check the index first.

Users, channels, and memberships are DataSync's built-in classes: a user and a channel are entities, and a membership is a relationship linking a channel to a user. Everything on the [Data model](https://www.pubnub.com/docs/general/data-sync/data-model.md) page applies to them too, including payloads, ETags, TTL, events, and access control.

## Users

A user is an entity of the built-in Global `User` [entity class](https://www.pubnub.com/docs/admin-api/get-all-entity-class-entries.md), which PubNub seeds at version 1. DataSync picks the class for you when you don't name one. Creating a user requires only `entityClassVersion`. The `id` is optional, and DataSync generates one if you omit it, as are `status` and `payload`.

The user endpoints are [Get users](https://www.pubnub.com/docs/sdks/rest-api/get-users.md), [Get user](https://www.pubnub.com/docs/sdks/rest-api/get-user.md), [Create user](https://www.pubnub.com/docs/sdks/rest-api/create-user.md), [Update user](https://www.pubnub.com/docs/sdks/rest-api/update-user.md), [Patch user](https://www.pubnub.com/docs/sdks/rest-api/patch-user.md), and [Delete user](https://www.pubnub.com/docs/sdks/rest-api/delete-user.md).

A user's fields:

| Field | Purpose |
| --- | --- |
| `id` | The user ID |
| `createdAt` | Creation timestamp |
| `updatedAt` | Last modification timestamp |
| `eTag` | Optimistic concurrency marker |
| `status` | A free-form application lifecycle string, 1 to 100 characters |
| `expiresAt` | Expiry timestamp. An instance of the Global class inherits its 30-day TTL |
| `entityClass` | The name of the entity class, `User` unless you've extended it |
| `entityClassVersion` | The version of the `User` class this instance pins to |
| `entityClassLevel` | Whether that class is `Global` or `SubKey` (keyset-level) |
| `payload` | Your application data |

##### How this differs from App Context user metadata

App Context user metadata has top-level `name`, `email`, `profileUrl`, `externalId`, and `custom` fields. A DataSync user keeps all application data inside `payload` instead.

The built-in `User` class declares two properties: `name` at `/payload/name` and `type` at `/payload/type`, both with `valueKind` `string`, `isNullable` `true`, filtering mode `full`, and membership of the `__default__` projection. Because they're `full`, both are filterable and sortable out of the box, and because both are nullable, neither is required. There are no other top-level fields reserved for user data, and any additional payload fields are stored as-is but aren't indexed. The Global `Channel` class declares the same two properties.

The Global `User` class itself can't be changed. To add your own fields, use [Create a new entity class](https://www.pubnub.com/docs/admin-api/create-a-new-entity-class.md) to define a class on your keyset that extends the Global `User` class as its parent, through an `extends` reference (see [Class inheritance](https://www.pubnub.com/docs/general/data-sync/schemas-and-validation.md#class-inheritance)).

The subclass name is up to you, it does not need to match the parent's. You create, read, and list instances of your extended class, the same as any other Global `User`, and they validate and project against your extended definition while still being users in every other sense (the same `users` resource type for Access Manager grants, the same fields table above).

Your subclass can't itself be extended. A class can only extend a class defined at a higher level, so a keyset-level class extends a Global class and the hierarchy stops there.

In [Bob's marketplace](https://www.pubnub.com/docs/general/data-sync/overview.md#running-example-bobs-live-marketplace), Bob defines a keyset-level `MarketplaceUser` class at version 1, extending the Global `User` class, adding `display_name`, `avatar_url`, `email`, and `phone`. `user-alice` is a user entity created against that version, with a payload of

```json
{ "name": "Alice", "type": "shopper", "display_name": "Alice", "avatar_url": "https://.../alice.png", "email": "alice@example.com", "phone": "+1-555-0100" }
```

You can't modify the Global class, so you extend it. A subclass inherits its parent's properties automatically: `MarketplaceUser` declares only Bob's four fields, and inherits `name` (`/payload/name`, `string`, nullable) and `type` (`/payload/type`, `string`, nullable) from the parent, for six effective properties in all. If you do redeclare an inherited property yourself, its path, value kind, and nullability must match the parent exactly, or the class is rejected, but you're free to pick a different filtering mode or a different set of projections for an inherited property.

## Channels

A channel is an entity of the built-in Global `Channel` [entity class](https://www.pubnub.com/docs/admin-api/get-all-entity-class-entries.md), with the same shape as a user. Refer to the [fields](#users) table above for the channel's fields, they are the same as a user's.

A channel entity stores data about a channel your app uses for messaging, for example a display name or a category.

The channel endpoints are [Get channels](https://www.pubnub.com/docs/sdks/rest-api/get-channels.md), [Get channel](https://www.pubnub.com/docs/sdks/rest-api/get-channel.md), [Create channel](https://www.pubnub.com/docs/sdks/rest-api/create-channel.md), [Update channel](https://www.pubnub.com/docs/sdks/rest-api/update-channel.md), [Patch channel](https://www.pubnub.com/docs/sdks/rest-api/patch-channel.md), and [Delete channel](https://www.pubnub.com/docs/sdks/rest-api/delete-channel.md).

:::warning A channel entity in DataSync isn't a pub/sub channel
Creating a channel entity doesn't create or configure the underlying Pub/Sub channel, and a channel does not need an entity for messaging to work.
The entity is where the channel's metadata lives.
:::

The channel entity holds metadata, while the Pub/Sub and Presence channel carries messages and subscribers. They share an ID but have separate lifecycles, and messaging works whether or not the entity exists.

###### These are the same channels Presence tracks

A channel entity's ID is the same channel your users join for messaging and Presence. See [Presence](https://www.pubnub.com/docs/general/presence/overview.md).

In Bob's marketplace, `channel-summer-sale` is a channel entity with a payload of

```json
{ "name": "Summer Sale Live", "type": "live-stream" }
```

## Memberships

A membership is a relationship of the built-in Global `Membership` [relationship class](https://www.pubnub.com/docs/admin-api/get-all-relationship-classes.md), linking a channel and a user. The `Membership` class is many-to-many, so a user can belong to many channels and a channel can have many members. Its two sides are pinned to the Global classes: entity A must be a `Channel` and entity B must be a `User`, or a subclass of either.

The membership endpoints are [Get memberships](https://www.pubnub.com/docs/sdks/rest-api/get-memberships.md), [Get membership](https://www.pubnub.com/docs/sdks/rest-api/get-membership.md), [Create membership](https://www.pubnub.com/docs/sdks/rest-api/create-membership.md), [Update membership](https://www.pubnub.com/docs/sdks/rest-api/update-membership.md), [Patch membership](https://www.pubnub.com/docs/sdks/rest-api/patch-membership.md), and [Delete membership](https://www.pubnub.com/docs/sdks/rest-api/delete-membership.md).

A membership's fields, in addition to the system fields shared with entities:

| Field | Purpose |
| --- | --- |
| `id` | The membership's own identifier, and the only handle for get, replace, partial update, and delete. There's no addressing a membership by its channel and user pair |
| `channelId` | The linked channel's ID, the relationship's entity A |
| `userId` | The linked user's ID, the relationship's entity B |
| `relationshipClass` | Always `Membership`, set by DataSync. Relationship classes can't be extended |
| `relationshipClassVersion` | The version of the `Membership` class this instance pins to |
| `payload` | Association data, for example a role |

A membership is a relationship, so it follows every relationship rule on the [Data model](https://www.pubnub.com/docs/general/data-sync/data-model.md#relationships) page, with a few specializations:

* The sides are renamed. Where a generic relationship exposes `entityAId` and `entityBId`, a membership exposes them as `channelId` and `userId`.
* `relationshipClass` is fixed to `Membership` and can't be supplied.
* The channel must be a `Channel`, or a subclass of it, and the user must be a `User`, or a subclass of it, or the request is rejected with a `400`.
* `expiresAt` is derived as the earlier of the channel's and the user's expiry, and can't be set.
* DataSync events keep the generic `entityAId` and `entityBId` names.

Creating a membership requires `channelId`, `userId`, and `relationshipClassVersion`. The `id` is optional, as are `status` and `payload`. Both the channel and the user must already exist, or the request returns a `404`. Only one membership can exist for a given channel and user, so a duplicate returns a `409`.

Memberships support both replace (PUT) and partial update (PATCH), the same as any other relationship. Refer to [Data operations](https://www.pubnub.com/docs/general/data-sync/data-operations.md#updating-objects) for more information.

In Bob's marketplace, Alice's membership in `channel-summer-sale` carries a payload of

```json
{ "role": "viewer" }
```

Because the `Membership` class declares no properties and can't be extended, membership payload fields are never indexed. You can't filter or sort memberships by `role`. The [built-in fields](https://www.pubnub.com/docs/general/data-sync/data-operations.md#built-in-fields) `id`, `createdAt`, `updatedAt`, and `status` still work, so you can order memberships by when they were created even though the payload isn't searchable.

Membership lists can be narrowed by `user_id`, by `channel_id`, or by both together, which covers the two common questions: which channels a user belongs to, and who is a member of a channel. Both parameters are optional, so omit them to list every membership. Refer to [Get memberships](https://www.pubnub.com/docs/sdks/rest-api/get-memberships.md).

## Real-time updates

Creating, updating, or deleting a user, channel, or membership can publish a DataSync event, the same way any other entity or relationship can. Events are off by default and are enabled per class version, per keyset, in the Admin Portal. A create event for `user-alice` is published on the `user-alice` channel. Update and delete events are also published on the channels of every entity `user-alice` is linked to. A membership event is published on both the linked channel's and the linked user's channels, never on the membership's own ID. Refer to [Events](https://www.pubnub.com/docs/general/data-sync/events.md) for more information.

Because a membership event lands on the linked user's and channel's ID channels, the way to watch Alice's memberships appear and disappear is to subscribe to `user-alice`, not to any membership id. SDKs that ship [DataSync SDK entities](https://www.pubnub.com/docs/general/entities.md#datasync-sdk-entities) expose that as a user handle and a channel handle. A membership handle exists for symmetry, but it receives nothing for the link itself.

###### Access Manager grants apply per user, per channel, and per membership

Access Manager tokens grant permissions on the same user and channel ids you use here. Memberships are a separate `datasync:memberships` resource type, so granting access to a user or channel doesn't authorize creating, updating, or deleting memberships between them, that requires its own grant. Refer to [Access control and permissions management](https://www.pubnub.com/docs/general/data-sync/access-control.md) for more information.

##### Comparison with App Context

| App Context | DataSync |
| --- | --- |
| User metadata (`name`, `email`, `custom`, ...) | User entity, data in `payload` |
| Channel metadata | Channel entity, data in `payload` |
| Membership with custom data | Membership relationship with `payload` |
| Optional Access Manager | Access Manager required |
| set/delete events (message type 2) | create/update/delete events (message type 5) |

App Context keeps working, and there is no automatic migration of existing data to DataSync.

Creating `user-alice` against Bob's `MarketplaceUser` class, then reading her back by id:

###### JavaScript

```javascript
const created = await pubnub.dataSync.createUser({
    id: 'user-alice',
    class: 'MarketplaceUser',
    data: {
        classVersion: 1,
        payload: {
            name: 'Alice',
            type: 'shopper',
            display_name: 'Alice',
            avatar_url: 'https://.../alice.png',
            email: 'alice@example.com',
            phone: '+1-555-0100',
        },
    },
})

const user = await pubnub.dataSync.getUser({ id: 'user-alice' })
```

###### C#

```csharp
PNResult<PNDataSyncUserResult> created = await pubnub.DataSync.CreateUser(new CreateUserParameters
{
    Id = "user-alice",
    EntityClass = "MarketplaceUser",
    EntityClassVersion = 1,
    Payload = new Dictionary<string, object>
    {
        { "name", "Alice" },
        { "type", "shopper" },
        { "display_name", "Alice" },
        { "avatar_url", "https://.../alice.png" },
        { "email", "alice@example.com" },
        { "phone", "+1-555-0100" },
    },
});

PNResult<PNDataSyncUserResult> user = await pubnub.DataSync.GetUser(new GetUserParameters
{
    Id = "user-alice",
});
```

Refer to [Create user (JavaScript)](https://www.pubnub.com/docs/sdks/javascript/api-reference/data-sync.md#create-user), [Create user (C#)](https://www.pubnub.com/docs/sdks/c-sharp/api-reference/data-sync.md#create-user), and [Create user (REST)](https://www.pubnub.com/docs/sdks/rest-api/create-user.md) for the full parameter reference, including the equivalent methods for channels and memberships.

## Terms in this document

* **Channel** - A pathway for sending and receiving messages between devices, created automatically when you first use it, that can handle any number of users and messages for different communication needs, like 1-1 text chats, group conversations, and other data streaming.
* **DataSync** - PubNub's real-time data layer for storing and synchronizing application state as entities and relationships. The successor to App Context.
* **DataSync event** - A real-time change notification (message type 5) published when a DataSync object is created, updated, or deleted. Delivered on the ID channels of the entities the change affects, with a separate channel for each named projection.
* **Membership** - A relationship in DataSync that links a channel to a user, using the built-in many-to-many Membership class.
* **User** - An individual or entity that interacts with a system, application, or service. In PubNub, a user typically refers to someone who sends or receives messages through the platform, identified by a unique user ID or username.

Last updated at: 2026-09-08T16:52:30.000Z
