---
source_url: https://www.pubnub.com/docs/sdks/javascript/api-reference/datasync
title: DataSync API for JavaScript SDK
updated_at: 2026-11-09T13:29:01.000Z
sdk_name: PubNub JavaScript SDK
sdk_version: 13.1.3
description: "Reference for the DataSync API of the PubNub JavaScript SDK: create, get, list, set, update, and remove users, channels, memberships, entities, and relationships, and receive real-time events."
---

# DataSync API for JavaScript SDK

PubNub JavaScript SDK, use the latest version: 13.1.3

Install:

```bash
npm install pubnub@13.1.3
```

> For AI agents: documentation index at https://www.pubnub.com/llms-full.txt

The [DataSync](https://www.pubnub.com/docs/data-storage/structured-data/overview.md) API of the PubNub JavaScript SDK stores application state as users, channels, memberships, entities, and relationships, and keeps connected clients current through real-time events. Use it to model the objects your application works with and to react the moment they change.

DataSync is the successor to [App Context](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects.md).

:::note DataSync entities and SDK entities are different things
This page is about **DataSync entities**, the records the service stores and treats as the source of truth for your application state. They are created and read through the `pubnub.dataSync.*` methods documented here.
An **SDK entity** is a local client-side handle such as `pubnub.channel('room-1')`. It carries no stored state of its own and gives you a scoped handle for subscribing and calling APIs. See [SDK entities](https://www.pubnub.com/docs/sdks/javascript/api-reference/subscribe.md#sdk-entities) in Publish and subscribe.
The two meet in the [DataSync SDK entities](https://www.pubnub.com/docs/sdks/javascript/api-reference/subscribe.md#datasync-sdk-entities): SDK entities whose only job is to subscribe to a DataSync object's real-time updates.
:::

The classes that your objects conform to (their types and schemas) are defined through the Admin API or the [Admin Portal](https://admin.pubnub.com/), not through this SDK:

* Entity classes, which back users, channels, and custom entities: [list](https://www.pubnub.com/docs/admin-api/get-all-entity-class-entries.md), [read](https://www.pubnub.com/docs/admin-api/get-entity-class-by-id.md), [create](https://www.pubnub.com/docs/admin-api/create-a-new-entity-class.md), [replace](https://www.pubnub.com/docs/admin-api/update-entity-class-with-complete-resource-replacement.md), and [delete](https://www.pubnub.com/docs/admin-api/delete-entity-class-by-id.md). Partial updates aren't implemented. Replace the complete class version instead.
* Relationship classes, which back memberships and custom relationships: [list](https://www.pubnub.com/docs/admin-api/get-all-relationship-classes.md), [read](https://www.pubnub.com/docs/admin-api/get-relationship-class-by-name-and-version.md), [create](https://www.pubnub.com/docs/admin-api/create-a-new-relationship-class.md), [replace](https://www.pubnub.com/docs/admin-api/update-relationship-class-with-complete-resource-replacement.md), and [delete](https://www.pubnub.com/docs/admin-api/delete-relationship-class-by-name-and-version.md).

A class definition decides three things for every object of that class. The definition decides which `payload` fields are filterable and sortable, which [projection](https://www.pubnub.com/docs/sdks/javascript/api-reference/access-manager.md#grant-token) each field belongs to, and how long the object lives before it expires. See [managing classes](https://www.pubnub.com/docs/data-storage/structured-data/schemas.md#managing-classes) for details.

Every response includes the HTTP `status`. The `remove*` methods return the status on its own. Every other method adds a `data` key holding the object, or the array of objects for a list method. A response can also add a `links` object with the hypermedia URLs (HATEOAS) the service provides (`self`, and `next` where it applies). Every stored object carries its `expiresAt` auto-deletion timestamp (ISO 8601). A stored object also carries its free-form `status` when you set one.

The operations fall into six areas, and each operation has its own section:

* Users are entities of the built-in `User` class. Start with [Create user](#create-user).
* Channels are entities of the built-in `Channel` class. Start with [Create channel](#create-channel).
* Memberships link a user to a channel. Start with [Create membership](#create-membership).
* Entities are instances of the custom entity classes you define. Start with [Create entity](#create-entity).
* Relationships link two entities. Start with [Create relationship](#create-relationship).
* Real-time updates deliver change events to a `dataSync` listener. Start with [Where each event is delivered](#where-each-event-is-delivered).

## DataSync: Authorization

Every DataSync request must be [authorized](https://www.pubnub.com/docs/data-storage/structured-data/access-control.md#authorizing-requests) with a token or signature. For the status codes a failed request returns, see [error codes](https://www.pubnub.com/docs/data-storage/structured-data/error-codes.md#http-status-codes).

A signature means the client is configured with a `secretKey`, and the SDK then signs every request with it automatically. Never put a secret key in a client-side application, use it only on a server you control. Give client-side applications a token from [`grantToken()`](https://www.pubnub.com/docs/sdks/javascript/api-reference/access-manager.md#grant-an-authorized-client-access-to-datasync-resources) instead, set with `setToken()`.

## DataSync: Pagination

DataSync pagination is forward-only, so there is no previous-page cursor. To revisit an earlier page, page from the start again.

All list methods (`getUsers`, `getChannels`, `getMemberships`, `getEntities`, and `getRelationships`) accept `cursor` and `limit` (default 20, max 100) and can return a `meta` object with `has_next`, `next_cursor`, and `limit`. The `meta` object is optional, so guard the access when you read it, for example `response.meta?.next_cursor`.

To page forward, pass the returned `next_cursor` back as `cursor` on the next call, and stop when `has_next` is `false`. `next_cursor` is `null` on the last page. See [sorting and pagination](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#sorting-and-pagination).

## DataSync: Filtering and sorting

All list methods accept the two filter parameters `filter` and `filterFast`. The `filter` and `filterFast` parameters are mutually exclusive, and a request that carries both fails with a `400`. The SDK sends whatever you pass, so the call reaches the server and fails there.

| Parameter | Consistency | Properties it can read | Expression complexity |
| --- | --- | --- | --- |
| `filterFast` | Strongly consistent, reflects the latest writes | Filtering mode `simple` or `full` | Up to 10 conditions by default (configurable per keyset) |
| `filter` | Eventually consistent, results can briefly lag writes | Filtering mode `full` only | Full expression language |

Use `filterFast` when the query has to see an object you just wrote. Use `filter` when you need the full expression language over a property with filtering mode `full`.

:::warning danger
`filter` is not the strongly consistent one
The two names read the wrong way round if you assume `filter` is the basic option. `filterFast` is the strongly consistent, limited one. `filter` is the richer, eventually consistent one. Earlier builds of the SDK named these `filter` and `filterAdvanced` respectively, so a `filter` expression written against an older build now runs on the other backing store. Rename `filter` to `filterFast` and `filterAdvanced` to `filter` when you upgrade.
:::

Both parameters share one expression language of comparison, pattern (`LIKE`, `SLIKE`, `ILIKE`), and logical operators, described in [filter expression syntax](https://www.pubnub.com/docs/data-storage/structured-data/query-structured-data.md#filter-expression-syntax). Reference a property by its `name`, not its declared `path`. To filter on a nested `payload` property, declare it as a class property first, then filter by that property's `name`.

Some examples:

```javascript
filterFast: 'price < 100'                              // strongly consistent, single condition
filterFast: '(price < 100 && stock > 0) || price > 500' // strongly consistent, grouped conditions
filter: 'name LIKE "*sneaker*"'                        // eventually consistent, needs filtering mode "full"
filter: '!(status == "discontinued")'                  // eventually consistent, negated condition
```

Filter expressions only work over properties declared on the class. The built-in fields `id`, `createdAt`, and `updatedAt` are always filterable without a declaration. The `status` field is filterable too, unless the class redeclares `/status` scoped to projections that your token cannot fully reach.

When you don't pass `classVersion`, a list includes items of every version, but `filter`, `filterFast`, and `sort` can only use properties declared on the latest version of the class. The service rejects a property that exists only in an older version with a `400`. Pass `classVersion` to use properties declared on an older version. See [filtering](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#filtering) for the two filtering tiers, and [property definitions](https://www.pubnub.com/docs/data-storage/structured-data/schemas.md#property-definitions) for how properties are declared.

Use `sort` to order results. Pass an object mapping each field to a direction. A key's value is `'asc'`, `'desc'`, or `null` to apply the service default (ascending). Map multiple fields to sort by more than one, evaluated in the order the keys appear. The examples assume a class that declares `type` and `price` with a filtering mode other than `none`:

```javascript
sort: { price: 'desc' }                        // single field, descending
sort: { type: 'asc', price: 'desc' }           // type first, then price within each type
sort: { createdAt: null }                      // explicit service default (ascending)
```

`sort` also accepts a raw string, which the SDK passes through unchanged. List the fields separated by commas, each a property name optionally suffixed with `:desc`. A bare name sorts ascending:

```javascript
sort: 'price:desc'                             // single field, descending
sort: 'type,price:desc'                        // type ascending, then price descending
sort: 'createdAt'                              // bare name, ascending
```

The `+field` and `-field` prefixes are not accepted.

Sorting alone or alongside `filterFast` works only on properties declared on the class with a filtering mode other than `none`. Sorting alongside `filter` also requires the field to be declared `full`. The built-in fields `id`, `createdAt`, and `updatedAt` are always sortable without a declaration. The `status` field is sortable too, unless the class redeclares `/status` scoped to projections that your token cannot fully reach.

The service rejects a `sort` field that the class does not declare. The rejection currently arrives as `403 Forbidden`, not as a `400` that names the field.

## DataSync: Concurrency (ETag)

Every stored object carries an `eTag`. When the stored object has changed since you read it, a write that carries the earlier `eTag` fails with a `412`. Re-read the object and retry. See [optimistic concurrency with ETags](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#optimistic-concurrency-with-etags).

To guard against concurrent writes, pass the `eTag` you read earlier as `ifMatchesEtag` on `set*`, `update*`, and `remove*`. The SDK sends it as the `If-Match` request header.

## DataSync: Partial updates

The `update*` methods apply a partial update using JSON Patch (RFC 6902). They accept `add`, `replace`, and `test` (JSON Pointer key/value maps), `remove` (an array of JSON Pointers), and `move` and `copy` (arrays of `{ from, path }` JSON Pointer pairs).

Each path is a full JSON Pointer (RFC 6901) from the document root. To target a field inside `payload`, include the `/payload` prefix, for example `/payload/price`. To target a top-level field, use a pointer such as `/status`.

The SDK sends each path as you write it and does not add a `/payload` prefix.

See [partial update](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#partial-update) for details.

Correct patch forms:

```javascript
add: { '/payload/tags/0': 'featured' }                 // adds a value inside payload.tags
replace: { '/payload/price': 79.99 }                   // replaces payload.price
remove: ['/payload/tempFlag', '/payload/legacy/field'] // removes payload.tempFlag and payload.legacy.field
move: [{ from: '/payload/legacyName', path: '/payload/displayName' }]
copy: [{ from: '/payload/displayName', path: '/payload/previousName' }]
test: { '/status': 'active' }                          // fails the patch if status is not "active"
```

Paths address the object's stored property names, the same keys that come back in responses. The class version is `entityClassVersion` on users, channels, and entities, and `relationshipClassVersion` on memberships and relationships. The service cannot patch the fields set at creation: `entityClass` and `entityClassLevel` on users, channels, and entities, `relationshipClass`, `entityAId`, and `entityBId` on relationships, and `userId` and `channelId` on memberships.

At least one of `add`, `replace`, `remove`, `move`, `copy`, or `test` must be provided. An empty object or array does not count. The SDK checks this before it sends anything and throws a `PubNubError` if nothing is provided.

## DataSync: Expiry (TTL)

An object's `expiresAt` is set once when the object is created, from its class TTL, and updates never refresh it. See [data expiry](https://www.pubnub.com/docs/data-storage/structured-data/schemas.md#data-expiry-ttl) for the default TTLs, the day rounding, and how relationships expire.

:::note Supported and recommended asynchronous patterns
PubNub supports [Callbacks, Promises, and Async/Await](https://javascript.info/async) for asynchronous JS operations. The recommended pattern is Async/Await and all sample requests in this document are based on it. This pattern returns a status only on detecting an error. To receive the error status, you must add the [`try...catch`](https://javascript.info/try-catch) syntax to your code.
:::

## Create user

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.createUser()` creates a DataSync user through a PubNub client for JavaScript. The server generates the identifier unless the call supplies one.

Users have no top-level `name` or `email` fields. All application data lives in the free-form `payload`. See [users](https://www.pubnub.com/docs/data-storage/structured-data/built-in-types.md#users) for the concept.

A user is an entity of the built-in `User` [entity class](https://www.pubnub.com/docs/admin-api/get-all-entity-class-entries.md), which the service provides at the `Global` class level. To give your users their own declared properties, define a subclass of `User` with [Create a new entity class](https://www.pubnub.com/docs/admin-api/create-a-new-entity-class.md) and pass its name as `class`.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Optional | n/a | Identifier of the new DataSync user. Omit it to let the server generate one. The identifier has at most 255 characters. |
| class | string | Optional | `User` | Name of the entity class that this user belongs to. The class is set at creation and immutable afterward. Must be `User` or one of its subclasses. |
| classLevel | string | Optional | n/a | Class hierarchy level of `class`, either `Global` for a class the service provides or `SubKey` for one defined on your key set. The level is set at creation and immutable afterward. Omit it to let the service apply its default. |
| data | object | Yes | n/a | The mutable user data. |
| > classVersion | number | Yes | n/a | Version of the user class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |

### Returns

`pubnub.dataSync.createUser()` resolves with the HTTP status and the created user.

### Examples

#### Syntax

```javascript
pubnub.dataSync.createUser({
    id: string,
    class: string,
    classLevel: string,
    data: { classVersion: number, status: string, payload: object },
}): Promise<DataSync.CreateUserResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.createUser({
  id: 'user-alice',
  data: {
    classVersion: 1,
    payload: { name: 'Alice', type: 'shopper' },
  },
});
console.log('createUser response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "user-alice",
        "entityClassVersion": 1,
        "payload": {
            "name": "Alice",
            "type": "shopper"
        },
        "createdAt": "2026-07-13T09:00:00.000Z",
        "updatedAt": "2026-07-13T09:00:00.000Z",
        "eTag": "AbQdEfGhIjKlMn",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Get user

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getUser()` returns one DataSync user by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the user. |

### Returns

`pubnub.dataSync.getUser()` resolves with the HTTP status and the requested user.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getUser({
    id: string,
}): Promise<DataSync.GetUserResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getUser({ id: 'user-alice' });
console.log('getUser response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "user-alice",
        "entityClassVersion": 1,
        "payload": {
            "name": "Alice",
            "type": "shopper"
        },
        "createdAt": "2026-07-13T09:00:00.000Z",
        "updatedAt": "2026-07-13T09:00:00.000Z",
        "eTag": "AbQdEfGhIjKlMn",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Get all users

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getUsers()` returns a paginated list of DataSync users through a PubNub client for JavaScript.

All parameters are optional, so you can call `getUsers()` with no arguments. You can also pass a callback as the only argument. For pagination, filtering, and sorting, see [sorting and pagination](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#sorting-and-pagination).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| class | string | Optional | n/a | Name of the entity class to list. Omit it to list users of every user class. |
| classVersion | number | Optional | n/a | Class version to list. Omit it to list users of every version of the class. |
| classLevel | string | Optional | n/a | Class hierarchy level of `class`, either `Global` for a class the service provides or `SubKey` for one defined on your key set. Use it to disambiguate a class name defined at both levels. |
| cursor | string | Optional | n/a | Opaque pagination cursor from the previous page. Omit it to get the first page. |
| limit | number | Optional | n/a | Maximum number of users to return in one page, up to 100. |
| filter | string | Optional | n/a | Filter expression evaluated against eventually consistent storage, so results can briefly lag writes. The parameter supports the full expression language over properties declared with filtering mode `full`. A call cannot combine it with `filterFast`. |
| filterFast | string | Optional | n/a | Filter expression evaluated against strongly consistent storage, so it reflects the latest writes. The parameter accepts up to 10 conditions by default (configurable per keyset) over properties declared with filtering mode `simple` or `full`. A call cannot combine it with `filter`. |
| sort | object \| string | Optional | n/a | Sort order of the returned users. An object that maps each field to `'asc'`, `'desc'`, or `null` (service default), or a comma-separated string such as `'createdAt:desc'`. |

### Returns

`pubnub.dataSync.getUsers()` resolves with the HTTP status, an array of users, and paging metadata.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getUsers({
    class: string,
    classVersion: number,
    classLevel: string,
    cursor: string,
    limit: number,
    filter: string,
    filterFast: string,
    sort: object | string,
}): Promise<DataSync.GetUsersResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getUsers({
  limit: 20,
  sort: { createdAt: 'asc' },
});
console.log('getUsers response:', response.data);
console.log('getUsers meta:', response.meta);
```

#### Returns

```json
{
    "status": 200,
    "data": [
        {
            "id": "user-alice",
            "entityClassVersion": 1,
            "payload": {
                "name": "Alice",
                "type": "shopper"
            },
            "createdAt": "2026-07-13T09:00:00.000Z",
            "updatedAt": "2026-07-13T09:00:00.000Z",
            "eTag": "AbQdEfGhIjKlMn",
            "expiresAt": "2026-08-12T09:00:00.000Z"
        }
    ],
    "meta": {
        "next_cursor": "b2Zmc2V0PTIw",
        "has_next": true,
        "limit": 20
    }
}
```

#### Filter with filter

`filter` runs over properties declared with filtering mode `full` and is evaluated against eventually consistent storage, so a very recent write may not be matched yet. Reference a declared property by its `name`, not its `path`.

```javascript
// `filter` runs over properties declared with filtering mode `full` and is evaluated against
// eventually consistent storage.
const response = await pubnub.dataSync.getUsers({
  filter: 'name LIKE "*Alice*"',
});
console.log('getUsers (filter) response:', response.data);
```

#### Filter with filterFast

`filterFast` is evaluated against strongly consistent storage, so it matches an object you just wrote. The expression runs over properties declared with filtering mode `simple` or `full` and accepts up to 10 conditions by default (configurable per keyset). The two parameters share the expression language of `filter`, so a call can send only one of them.

```javascript
// `filterFast` is evaluated against strongly consistent storage, so it matches an object you just
// wrote.
const response = await pubnub.dataSync.getUsers({
  filterFast: 'type == "shopper"',
});
console.log('getUsers (filterFast) response:', response.data);
```

#### Page through results with cursor

Pass no `cursor` on the first call. Take `meta.next_cursor` from the response and pass it back as `cursor` on the next call. Stop when `meta.has_next` is `false`.

```javascript
// Pass no `cursor` on the first call. Take `meta.next_cursor` from the response and pass it back
// as `cursor` on the next call. Stop when `meta.has_next` is `false`.
let cursor: string | undefined;
let hasNext = true;
let page = 0;

while (hasNext) {
  const response = await pubnub.dataSync.getUsers({
    filterFast: 'type == "shopper"',
    limit: 20,
    cursor,
  });
  console.log(`Page ${++page}:`, response.data);
  cursor = response.meta?.next_cursor ?? undefined;
  hasNext = response.meta?.has_next ?? false;
}
```

## Set user

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.setUser()` replaces a DataSync user in full through a PubNub client for JavaScript. The call clears a stored `status` or `payload` that it omits.

To guard against concurrent writes, pass `ifMatchesEtag`. See [optimistic concurrency with ETags](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#optimistic-concurrency-with-etags).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the user. |
| data | object | Yes | n/a | The replacement user data. |
| > classVersion | number | Yes | n/a | Version of the user class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The update succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.setUser()` resolves with the HTTP status and the stored user.

### Examples

#### Syntax

```javascript
pubnub.dataSync.setUser({
    id: string,
    data: { classVersion: number, status: string, payload: object },
    ifMatchesEtag: string,
}): Promise<DataSync.SetUserResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.setUser({
  id: 'user-alice',
  data: {
    classVersion: 1,
    payload: { name: 'Alice B.', type: 'shopper' },
  },
  ifMatchesEtag: 'AbQdEfGhIjKlMn',
});
console.log('setUser response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "user-alice",
        "entityClassVersion": 1,
        "payload": {
            "name": "Alice B.",
            "type": "shopper"
        },
        "createdAt": "2026-07-13T09:00:00.000Z",
        "updatedAt": "2026-07-13T10:15:00.000Z",
        "eTag": "CdEfGhIjKlMnOp",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Update user

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.updateUser()` applies a JSON Patch (RFC 6902) partial update to a DataSync user through a PubNub client for JavaScript. The call needs at least one patch operation, and fields that no operation addresses stay unchanged.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the user. |
| add | object | Optional | n/a | Full JSON Pointers mapped to values to add. |
| replace | object | Optional | n/a | Full JSON Pointers mapped to replacement values. |
| remove | array | Optional | n/a | Full JSON Pointers to remove. |
| move | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is removed and re-added at `path`. |
| copy | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is duplicated to `path`. |
| test | object | Optional | n/a | Full JSON Pointers mapped to expected values. The patch fails if any value does not match. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The patch succeeds only if it still matches, otherwise the server returns `412`. |

Paths can target fields inside `payload` or top-level stored fields. A JSON Pointer that starts with `/payload/` targets a payload field, and a root pointer such as `/status` targets a top-level field. See [partial update](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#partial-update) for the JSON Pointer rules.

At least one of `add`, `replace`, `remove`, `move`, `copy`, or `test` must be provided.

A single `updateUser()` call can mix `add`, `replace`, `remove`, `move`, `copy`, and `test`. The order you write the keys in does not matter. The SDK always sends the operations in the same order, with `test` last.

### Returns

`pubnub.dataSync.updateUser()` resolves with the HTTP status and the updated user.

### Examples

#### Syntax

```javascript
pubnub.dataSync.updateUser({
    id: string,
    add: object,
    replace: object,
    remove: string[],
    move: { from: string, path: string }[],
    copy: { from: string, path: string }[],
    test: object,
    ifMatchesEtag: string,
}): Promise<DataSync.UpdateUserResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.updateUser({
  id: 'user-alice',
  replace: { '/payload/name': 'Alice B.', '/status': 'active' },
});
console.log('updateUser response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "user-alice",
        "entityClassVersion": 1,
        "status": "active",
        "payload": {
            "name": "Alice B.",
            "type": "shopper"
        },
        "createdAt": "2026-07-13T09:00:00.000Z",
        "updatedAt": "2026-07-13T10:20:00.000Z",
        "eTag": "EfGhIjKlMnOpQr",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Remove user

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.removeUser()` deletes a DataSync user by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the user. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The delete succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.removeUser()` resolves with only the HTTP status.

### Examples

#### Syntax

```javascript
pubnub.dataSync.removeUser({
    id: string,
    ifMatchesEtag: string,
}): Promise<DataSync.RemoveUserResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.removeUser({ id: 'user-alice' });
console.log('removeUser response status:', response.status);
```

#### Returns

```json
{
    "status": 200
}
```

## Create channel

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.createChannel()` creates a DataSync channel through a PubNub client for JavaScript. The server generates the identifier unless the call supplies one.

Channels work like users. They have no top-level `name` field, all application data lives in `payload`, and they support the same operations. See [channels](https://www.pubnub.com/docs/data-storage/structured-data/built-in-types.md#channels) for the concept.

A channel is an entity of the built-in `Channel` [entity class](https://www.pubnub.com/docs/admin-api/get-all-entity-class-entries.md), which the service provides at the `Global` class level. To give your channels their own declared properties, define a subclass of `Channel` with [Create a new entity class](https://www.pubnub.com/docs/admin-api/create-a-new-entity-class.md) and pass its name as `class`.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Optional | n/a | Identifier of the new DataSync channel. Omit it to let the server generate one. The identifier has at most 255 characters. |
| class | string | Optional | `Channel` | Name of the entity class that this channel belongs to. The class is set at creation and immutable afterward. Must be `Channel` or one of its subclasses. |
| classLevel | string | Optional | n/a | Class hierarchy level of `class`, either `Global` for a class the service provides or `SubKey` for one defined on your key set. The level is set at creation and immutable afterward. Omit it to let the service apply its default. |
| data | object | Yes | n/a | The mutable channel data. |
| > classVersion | number | Yes | n/a | Version of the channel class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |

### Returns

`pubnub.dataSync.createChannel()` resolves with the HTTP status and the created channel.

### Examples

#### Syntax

```javascript
pubnub.dataSync.createChannel({
    id: string,
    class: string,
    classLevel: string,
    data: { classVersion: number, status: string, payload: object },
}): Promise<DataSync.CreateChannelResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.createChannel({
  id: 'channel-summer-sale',
  data: {
    classVersion: 1,
    payload: { name: 'Summer Sale', type: 'promotion' },
  },
});
console.log('createChannel response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "channel-summer-sale",
        "entityClassVersion": 1,
        "payload": {
            "name": "Summer Sale",
            "type": "promotion"
        },
        "createdAt": "2026-07-13T09:05:00.000Z",
        "updatedAt": "2026-07-13T09:05:00.000Z",
        "eTag": "GhIjKlMnOpQrSt",
        "expiresAt": "2026-08-12T09:05:00.000Z"
    }
}
```

## Get channel

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getChannel()` returns one DataSync channel by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the channel. |

### Returns

`pubnub.dataSync.getChannel()` resolves with the HTTP status and the requested channel.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getChannel({
    id: string,
}): Promise<DataSync.GetChannelResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getChannel({ id: 'channel-summer-sale' });
console.log('getChannel response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "channel-summer-sale",
        "entityClassVersion": 1,
        "payload": {
            "name": "Summer Sale",
            "type": "promotion"
        },
        "createdAt": "2026-07-13T09:05:00.000Z",
        "updatedAt": "2026-07-13T09:05:00.000Z",
        "eTag": "GhIjKlMnOpQrSt",
        "expiresAt": "2026-08-12T09:05:00.000Z"
    }
}
```

## Get all channels

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getChannels()` returns a paginated list of DataSync channels through a PubNub client for JavaScript.

All parameters are optional, so you can call `getChannels()` with no arguments. You can also pass a callback as the only argument. For pagination, filtering, and sorting, see [sorting and pagination](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#sorting-and-pagination).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| class | string | Optional | n/a | Name of the entity class to list. Omit it to list channels of every channel class. |
| classVersion | number | Optional | n/a | Class version to list. Omit it to list channels of every version of the class. |
| classLevel | string | Optional | n/a | Class hierarchy level of `class`, either `Global` for a class the service provides or `SubKey` for one defined on your key set. Use it to disambiguate a class name defined at both levels. |
| cursor | string | Optional | n/a | Opaque pagination cursor from the previous page. Omit it to get the first page. |
| limit | number | Optional | n/a | Maximum number of channels to return in one page, up to 100. |
| filter | string | Optional | n/a | Filter expression evaluated against eventually consistent storage, so results can briefly lag writes. The parameter supports the full expression language over properties declared with filtering mode `full`. A call cannot combine it with `filterFast`. |
| filterFast | string | Optional | n/a | Filter expression evaluated against strongly consistent storage, so it reflects the latest writes. The parameter accepts up to 10 conditions by default (configurable per keyset) over properties declared with filtering mode `simple` or `full`. A call cannot combine it with `filter`. |
| sort | object \| string | Optional | n/a | Sort order of the returned channels. An object that maps each field to `'asc'`, `'desc'`, or `null` (service default), or a comma-separated string such as `'createdAt:desc'`. |

### Returns

`pubnub.dataSync.getChannels()` resolves with the HTTP status, an array of channels, and paging metadata.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getChannels({
    class: string,
    classVersion: number,
    classLevel: string,
    cursor: string,
    limit: number,
    filter: string,
    filterFast: string,
    sort: object | string,
}): Promise<DataSync.GetChannelsResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getChannels({ limit: 20 });
console.log('getChannels response:', response.data);
console.log('getChannels meta:', response.meta);
```

#### Returns

```json
{
    "status": 200,
    "data": [
        {
            "id": "channel-summer-sale",
            "entityClassVersion": 1,
            "payload": {
                "name": "Summer Sale",
                "type": "promotion"
            },
            "createdAt": "2026-07-13T09:05:00.000Z",
            "updatedAt": "2026-07-13T09:05:00.000Z",
            "eTag": "GhIjKlMnOpQrSt",
            "expiresAt": "2026-08-12T09:05:00.000Z"
        }
    ],
    "meta": {
        "next_cursor": null,
        "has_next": false,
        "limit": 20
    }
}
```

#### Filter with filter

`filter` runs over properties declared with filtering mode `full` and is evaluated against eventually consistent storage, so a very recent write may not be matched yet. Reference a declared property by its `name`, not its `path`.

```javascript
const response = await pubnub.dataSync.getChannels({
  filter: 'name LIKE "*Sale*"',
});
console.log('getChannels (filter) response:', response.data);
```

#### Filter with filterFast

`filterFast` is evaluated against strongly consistent storage, so it matches an object you just wrote. The expression runs over properties declared with filtering mode `simple` or `full` and accepts up to 10 conditions by default (configurable per keyset). The two parameters share the expression language of `filter`, so a call can send only one of them.

```javascript
const response = await pubnub.dataSync.getChannels({
  filterFast: 'type == "promotion"',
});
console.log('getChannels (filterFast) response:', response.data);
```

#### Page through results with cursor

Pass no `cursor` on the first call. Take `meta.next_cursor` from the response and pass it back as `cursor` on the next call. Stop when `meta.has_next` is `false`.

```javascript
let cursor: string | undefined;
let hasNext = true;
let page = 0;

while (hasNext) {
  const response = await pubnub.dataSync.getChannels({
    filterFast: 'type == "promotion"',
    limit: 20,
    cursor,
  });
  console.log(`Page ${++page}:`, response.data);
  cursor = response.meta?.next_cursor ?? undefined;
  hasNext = response.meta?.has_next ?? false;
}
```

## Set channel

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.setChannel()` replaces a DataSync channel in full through a PubNub client for JavaScript. The call clears a stored `status` or `payload` that it omits.

To guard against concurrent writes, pass `ifMatchesEtag`. See [optimistic concurrency with ETags](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#optimistic-concurrency-with-etags).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the channel. |
| data | object | Yes | n/a | The replacement channel data. |
| > classVersion | number | Yes | n/a | Version of the channel class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The update succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.setChannel()` resolves with the HTTP status and the stored channel.

### Examples

#### Syntax

```javascript
pubnub.dataSync.setChannel({
    id: string,
    data: { classVersion: number, status: string, payload: object },
    ifMatchesEtag: string,
}): Promise<DataSync.SetChannelResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.setChannel({
  id: 'channel-summer-sale',
  data: {
    classVersion: 1,
    payload: { name: 'Summer Sale 2026', type: 'promotion' },
  },
});
console.log('setChannel response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "channel-summer-sale",
        "entityClassVersion": 1,
        "payload": {
            "name": "Summer Sale 2026",
            "type": "promotion"
        },
        "createdAt": "2026-07-13T09:05:00.000Z",
        "updatedAt": "2026-07-13T11:00:00.000Z",
        "eTag": "IjKlMnOpQrStUv",
        "expiresAt": "2026-08-12T09:05:00.000Z"
    }
}
```

## Update channel

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.updateChannel()` applies a JSON Patch (RFC 6902) partial update to a DataSync channel through a PubNub client for JavaScript. The call needs at least one patch operation, and fields that no operation addresses stay unchanged.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the channel. |
| add | object | Optional | n/a | Full JSON Pointers mapped to values to add. |
| replace | object | Optional | n/a | Full JSON Pointers mapped to replacement values. |
| remove | array | Optional | n/a | Full JSON Pointers to remove. |
| move | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is removed and re-added at `path`. |
| copy | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is duplicated to `path`. |
| test | object | Optional | n/a | Full JSON Pointers mapped to expected values. The patch fails if any value does not match. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The patch succeeds only if it still matches, otherwise the server returns `412`. |

Paths can target fields inside `payload` or top-level stored fields. A JSON Pointer that starts with `/payload/` targets a payload field, and a root pointer such as `/status` targets a top-level field. See [partial update](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#partial-update) for the JSON Pointer rules.

At least one of `add`, `replace`, `remove`, `move`, `copy`, or `test` must be provided.

A single `updateChannel()` call can mix `add`, `replace`, `remove`, `move`, `copy`, and `test`. The order you write the keys in does not matter. The SDK always sends the operations in the same order, with `test` last.

### Returns

`pubnub.dataSync.updateChannel()` resolves with the HTTP status and the updated channel.

### Examples

#### Syntax

```javascript
pubnub.dataSync.updateChannel({
    id: string,
    add: object,
    replace: object,
    remove: string[],
    move: { from: string, path: string }[],
    copy: { from: string, path: string }[],
    test: object,
    ifMatchesEtag: string,
}): Promise<DataSync.UpdateChannelResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.updateChannel({
  id: 'channel-summer-sale',
  replace: { '/payload/name': 'Summer Sale 2026' },
});
console.log('updateChannel response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "channel-summer-sale",
        "entityClassVersion": 1,
        "payload": {
            "name": "Summer Sale 2026",
            "type": "promotion"
        },
        "createdAt": "2026-07-13T09:05:00.000Z",
        "updatedAt": "2026-07-13T11:05:00.000Z",
        "eTag": "KlMnOpQrStUvWx",
        "expiresAt": "2026-08-12T09:05:00.000Z"
    }
}
```

## Remove channel

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.removeChannel()` deletes a DataSync channel by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the channel. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The delete succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.removeChannel()` resolves with only the HTTP status.

### Examples

#### Syntax

```javascript
pubnub.dataSync.removeChannel({
    id: string,
    ifMatchesEtag: string,
}): Promise<DataSync.RemoveChannelResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.removeChannel({ id: 'channel-summer-sale' });
console.log('removeChannel response status:', response.status);
```

#### Returns

```json
{
    "status": 200
}
```

## Create membership

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.createMembership()` creates a DataSync membership through a PubNub client for JavaScript. The server generates the identifier unless the call supplies one.

A membership links a user to a channel and carries its own `payload`. In the running example, Alice is a member of `channel-summer-sale` with the payload `{ "role": "viewer" }`. See [memberships](https://www.pubnub.com/docs/data-storage/structured-data/built-in-types.md#memberships) for the concept.

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), whose two sides are surfaced as `channelId` and `userId`. Every membership response includes both `relationshipClass` and `relationshipClassVersion`. The class is assigned by the service, so the `createMembership` and `setMembership` methods take no class parameter.

Relationship classes [don't support inheritance](https://www.pubnub.com/docs/data-storage/structured-data/schemas.md#relationship-class-inheritance), and only the Global `Membership` class produces memberships, so `relationshipClass` is always `Membership` and `relationshipClassVersion` is the only part that varies.

The `userId` and `channelId` pair must be unique for the membership class. A membership that duplicates an existing pair is rejected with a `409`.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Optional | n/a | Identifier of the new DataSync membership. Omit it to let the server generate one. The identifier has at most 255 characters. |
| userId | string | Yes | n/a | Identifier of the user in the membership. |
| channelId | string | Yes | n/a | Identifier of the channel in the membership. |
| data | object | Yes | n/a | The mutable membership data. |
| > classVersion | number | Yes | n/a | Version of the membership class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |

### Returns

`pubnub.dataSync.createMembership()` resolves with the HTTP status and the created membership.

### Examples

#### Syntax

```javascript
pubnub.dataSync.createMembership({
    id: string,
    userId: string,
    channelId: string,
    data: { classVersion: number, status: string, payload: object },
}): Promise<DataSync.CreateMembershipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.createMembership({
  id: 'membership-alice-summer-sale',
  userId: 'user-alice',
  channelId: 'channel-summer-sale',
  data: {
    classVersion: 1,
    payload: { role: 'viewer' },
  },
});
console.log('createMembership response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "membership-alice-summer-sale",
        "channelId": "channel-summer-sale",
        "userId": "user-alice",
        "relationshipClass": "Membership",
        "relationshipClassVersion": 1,
        "payload": {
            "role": "viewer"
        },
        "createdAt": "2026-07-13T09:10:00.000Z",
        "updatedAt": "2026-07-13T09:10:00.000Z",
        "eTag": "MnOpQrStUvWxYz",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Get membership

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getMembership()` returns one DataSync membership by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the membership. |

### Returns

`pubnub.dataSync.getMembership()` resolves with the HTTP status and the requested membership.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getMembership({
    id: string,
}): Promise<DataSync.GetMembershipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getMembership({
  id: 'membership-alice-summer-sale',
});
console.log('getMembership response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "membership-alice-summer-sale",
        "channelId": "channel-summer-sale",
        "userId": "user-alice",
        "relationshipClass": "Membership",
        "relationshipClassVersion": 1,
        "payload": {
            "role": "viewer"
        },
        "createdAt": "2026-07-13T09:10:00.000Z",
        "updatedAt": "2026-07-13T09:10:00.000Z",
        "eTag": "MnOpQrStUvWxYz",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Get all memberships

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getMemberships()` returns a paginated list of DataSync memberships through a PubNub client for JavaScript.

All parameters are optional, so you can call `getMemberships()` with no arguments. You can also pass a callback as the only argument. Filter by `userId` to list a user's memberships or by `channelId` to list a channel's members. For pagination, filtering, and sorting, see [sorting and pagination](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#sorting-and-pagination).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| cursor | string | Optional | n/a | Opaque pagination cursor from the previous page. Omit it to get the first page. |
| limit | number | Optional | n/a | Maximum number of memberships to return in one page, up to 100. |
| filter | string | Optional | n/a | Filter expression evaluated against eventually consistent storage, so results can briefly lag writes. The parameter supports the full expression language over properties declared with filtering mode `full`. A call cannot combine it with `filterFast`. |
| filterFast | string | Optional | n/a | Filter expression evaluated against strongly consistent storage, so it reflects the latest writes. The parameter accepts up to 10 conditions by default (configurable per keyset) over properties declared with filtering mode `simple` or `full`. A call cannot combine it with `filter`. |
| sort | object \| string | Optional | n/a | Sort order of the returned memberships. An object that maps each field to `'asc'`, `'desc'`, or `null` (service default), or a comma-separated string such as `'createdAt:desc'`. |
| userId | string | Optional | n/a | List only memberships of this user. |
| channelId | string | Optional | n/a | List only memberships of this channel. |
| classVersion | number | Optional | n/a | Class version to list. Omit it to list memberships of every version of the class. |

### Returns

`pubnub.dataSync.getMemberships()` resolves with the HTTP status, an array of memberships, and paging metadata.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getMemberships({
    cursor: string,
    limit: number,
    filter: string,
    filterFast: string,
    sort: object | string,
    userId: string,
    channelId: string,
    classVersion: number,
}): Promise<DataSync.GetMembershipsResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getMemberships({
  userId: 'user-alice',
  limit: 20,
});
console.log('getMemberships response:', response.data);
console.log('getMemberships meta:', response.meta);
```

#### Returns

```json
{
    "status": 200,
    "data": [
        {
            "id": "membership-alice-summer-sale",
            "channelId": "channel-summer-sale",
            "userId": "user-alice",
            "relationshipClass": "Membership",
            "relationshipClassVersion": 1,
            "payload": {
                "role": "viewer"
            },
            "createdAt": "2026-07-13T09:10:00.000Z",
            "updatedAt": "2026-07-13T09:10:00.000Z",
            "eTag": "MnOpQrStUvWxYz",
            "expiresAt": "2026-08-12T09:00:00.000Z"
        }
    ],
    "meta": {
        "next_cursor": null,
        "has_next": false,
        "limit": 20
    }
}
```

#### List a channel's members with channelId

Pass `channelId` instead of `userId` to list the members of a channel rather than a user's memberships.

```javascript
// Pass `channelId` instead of `userId` to list the members of a channel rather than a user's
// memberships.
const response = await pubnub.dataSync.getMemberships({
  channelId: 'channel-summer-sale',
  limit: 20,
});
console.log('getMemberships (channelId) response:', response.data);
```

#### Filter with filter

`filter` runs over properties declared with filtering mode `full` and is evaluated against eventually consistent storage, so a very recent write may not be matched yet. Reference a declared property by its `name`, not its `path`.

The Membership class declares no `payload` properties and you can't extend it, so a filter can't reference a field inside the membership `payload`. A membership is filterable on the built-in fields `id`, `createdAt`, `updatedAt` and `status` instead. This example lists the memberships of `user-alice` whose `status` starts with `arch`. A membership without a `status` doesn't match.

```javascript
// `status` is a built-in field, so it is filterable without any declaration. The Membership class
// declares no `payload` properties, which means a filter cannot reference anything inside `payload`.
// `LIKE` takes `*` as a wildcard. This call returns Alice's memberships whose status starts with `arch`.
const response = await pubnub.dataSync.getMemberships({
  userId: 'user-alice',
  filter: 'status LIKE "arch*"',
});
console.log('getMemberships (filter) response:', response.data);
```

#### Filter with filterFast

`filterFast` is evaluated against strongly consistent storage, so it matches an object you just wrote. The expression runs over properties declared with filtering mode `simple` or `full` and accepts up to 10 conditions by default (configurable per keyset). The two parameters share the expression language of `filter`, so a call can send only one of them.

This example lists the memberships of `user-alice` whose `status` is exactly `archived`, and it also sees a membership you just wrote. It filters on the same built-in `status` field as the previous example.

```javascript
// `filterFast` is evaluated against strongly consistent storage, so it matches a membership you just
// wrote. It accepts the same built-in fields. This call returns Alice's archived memberships.
const response = await pubnub.dataSync.getMemberships({
  userId: 'user-alice',
  filterFast: 'status == "archived"',
});
console.log('getMemberships (filterFast) response:', response.data);
```

#### Page through results with cursor

Pass no `cursor` on the first call. Take `meta.next_cursor` from the response and pass it back as `cursor` on the next call. Stop when `meta.has_next` is `false`.

```javascript
let cursor: string | undefined;
let hasNext = true;
let page = 0;

while (hasNext) {
  const response = await pubnub.dataSync.getMemberships({
    userId: 'user-alice',
    limit: 20,
    cursor,
  });
  console.log(`Page ${++page}:`, response.data);
  cursor = response.meta?.next_cursor ?? undefined;
  hasNext = response.meta?.has_next ?? false;
}
```

## Set membership

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.setMembership()` replaces a DataSync membership in full through a PubNub client for JavaScript. The call clears a stored `status` or `payload` that it omits.

Resend `userId`, `channelId`, and `classVersion` along with the fields you want to keep. For a partial update, use [Update membership](#update-membership). To guard against concurrent writes, pass `ifMatchesEtag`. See [optimistic concurrency with ETags](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#optimistic-concurrency-with-etags).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the membership. |
| userId | string | Yes | n/a | Identifier of the user in the membership. |
| channelId | string | Yes | n/a | Identifier of the channel in the membership. |
| data | object | Yes | n/a | The replacement membership data. |
| > classVersion | number | Yes | n/a | Version of the membership class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The update succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.setMembership()` resolves with the HTTP status and the stored membership.

### Examples

#### Syntax

```javascript
pubnub.dataSync.setMembership({
    id: string,
    userId: string,
    channelId: string,
    data: { classVersion: number, status: string, payload: object },
    ifMatchesEtag: string,
}): Promise<DataSync.SetMembershipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.setMembership({
  id: 'membership-alice-summer-sale',
  userId: 'user-alice',
  channelId: 'channel-summer-sale',
  data: {
    classVersion: 1,
    payload: { role: 'moderator' },
  },
});
console.log('setMembership response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "membership-alice-summer-sale",
        "channelId": "channel-summer-sale",
        "userId": "user-alice",
        "relationshipClass": "Membership",
        "relationshipClassVersion": 1,
        "payload": {
            "role": "moderator"
        },
        "createdAt": "2026-07-13T09:10:00.000Z",
        "updatedAt": "2026-07-13T12:00:00.000Z",
        "eTag": "OpQrStUvWxYzAb",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Update membership

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.updateMembership()` applies a JSON Patch (RFC 6902) partial update to a DataSync membership through a PubNub client for JavaScript. The call needs at least one patch operation, and fields that no operation addresses stay unchanged.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the membership. |
| add | object | Optional | n/a | Full JSON Pointers mapped to values to add. |
| replace | object | Optional | n/a | Full JSON Pointers mapped to replacement values. |
| remove | array | Optional | n/a | Full JSON Pointers to remove. |
| move | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is removed and re-added at `path`. |
| copy | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is duplicated to `path`. |
| test | object | Optional | n/a | Full JSON Pointers mapped to expected values. The patch fails if any value does not match. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The patch succeeds only if it still matches, otherwise the server returns `412`. |

Paths can target fields inside `payload` or top-level stored fields. A JSON Pointer that starts with `/payload/` targets a payload field, and a root pointer such as `/status` targets a top-level field. See [partial update](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#partial-update) for the JSON Pointer rules.

At least one of `add`, `replace`, `remove`, `move`, `copy`, or `test` must be provided.

A single `updateMembership()` call can mix `add`, `replace`, `remove`, `move`, `copy`, and `test`. The order you write the keys in does not matter. The SDK always sends the operations in the same order, with `test` last.

### Returns

`pubnub.dataSync.updateMembership()` resolves with the HTTP status and the updated membership.

### Examples

#### Syntax

```javascript
pubnub.dataSync.updateMembership({
    id: string,
    add: object,
    replace: object,
    remove: string[],
    move: { from: string, path: string }[],
    copy: { from: string, path: string }[],
    test: object,
    ifMatchesEtag: string,
}): Promise<DataSync.UpdateMembershipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.updateMembership({
  id: 'membership-alice-summer-sale',
  replace: { '/payload/role': 'moderator' },
});
console.log('updateMembership response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "membership-alice-summer-sale",
        "channelId": "channel-summer-sale",
        "userId": "user-alice",
        "relationshipClass": "Membership",
        "relationshipClassVersion": 1,
        "payload": {
            "role": "moderator"
        },
        "createdAt": "2026-07-13T09:10:00.000Z",
        "updatedAt": "2026-07-13T12:05:00.000Z",
        "eTag": "QrStUvWxYzAbCd",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Remove membership

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.removeMembership()` deletes a DataSync membership by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the membership. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The delete succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.removeMembership()` resolves with only the HTTP status.

### Examples

#### Syntax

```javascript
pubnub.dataSync.removeMembership({
    id: string,
    ifMatchesEtag: string,
}): Promise<DataSync.RemoveMembershipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.removeMembership({
  id: 'membership-alice-summer-sale',
});
console.log('removeMembership response status:', response.status);
```

#### Returns

```json
{
    "status": 200
}
```

## Create entity

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.createEntity()` creates a DataSync entity through a PubNub client for JavaScript. The server generates the identifier unless the call supplies one.

Entities are instances of the custom [entity classes](https://www.pubnub.com/docs/admin-api/get-all-entity-class-entries.md) you define on your key set. In the running example, `product` is a class and `product-sneaker-42` is an instance. Use [Create a new entity class](https://www.pubnub.com/docs/admin-api/create-a-new-entity-class.md) to declare a class, and [Get entity class by ID](https://www.pubnub.com/docs/admin-api/get-entity-class-by-id.md) to read the property, filtering, and projection declarations that govern its instances.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Optional | n/a | Identifier of the new DataSync entity. Omit it to let the server generate one. The identifier has at most 255 characters. |
| class | string | Yes | n/a | Name of the entity class that this instance belongs to. The class is set at creation and immutable afterward. |
| classLevel | string | Optional | n/a | Class hierarchy level of `class`, either `Global` for a class the service provides or `SubKey` for one defined on your key set. The level is set at creation and immutable afterward. Omit it to let the service apply its default. |
| data | object | Yes | n/a | The mutable entity data. |
| > classVersion | number | Yes | n/a | Version of the entity class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |

### Returns

`pubnub.dataSync.createEntity()` resolves with the HTTP status and the created entity.

### Examples

#### Syntax

```javascript
pubnub.dataSync.createEntity({
    id: string,
    class: string,
    classLevel: string,
    data: { classVersion: number, status: string, payload: object },
}): Promise<DataSync.CreateEntityResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.createEntity({
  id: 'product-sneaker-42',
  class: 'product',
  data: {
    classVersion: 1,
    payload: { name: 'Retro Sneaker', price: 89.99, stock: 12 },
  },
});
console.log('createEntity response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "product-sneaker-42",
        "entityClass": "product",
        "entityClassVersion": 1,
        "payload": {
            "name": "Retro Sneaker",
            "price": 89.99,
            "stock": 12
        },
        "createdAt": "2026-07-13T09:15:00.000Z",
        "updatedAt": "2026-07-13T09:15:00.000Z",
        "eTag": "QrStUvWxYzAbCd",
        "expiresAt": "2026-08-13T09:15:00.000Z"
    }
}
```

## Get entity

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getEntity()` returns one DataSync entity by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the entity. |

### Returns

`pubnub.dataSync.getEntity()` resolves with the HTTP status and the requested entity.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getEntity({
    id: string,
}): Promise<DataSync.GetEntityResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getEntity({ id: 'product-sneaker-42' });
console.log('getEntity response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "product-sneaker-42",
        "entityClass": "product",
        "entityClassVersion": 1,
        "payload": {
            "name": "Retro Sneaker",
            "price": 89.99,
            "stock": 12
        },
        "createdAt": "2026-07-13T09:15:00.000Z",
        "updatedAt": "2026-07-13T09:15:00.000Z",
        "eTag": "QrStUvWxYzAbCd",
        "expiresAt": "2026-08-13T09:15:00.000Z"
    }
}
```

## Get all entities

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getEntities()` returns a paginated list of DataSync entities through a PubNub client for JavaScript.

The `class` parameter is required, entities are always listed within the context of their class. Listing a class also returns the entities of its subclasses, so `getEntities({ class: 'product' })` returns every `product` plus every instance of a class that extends `product`. For pagination, filtering, and sorting, see [sorting and pagination](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#sorting-and-pagination).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| class | string | Yes | n/a | Name of the entity class to list. The `class` parameter is required. |
| classVersion | number | Optional | n/a | Class version to list. Omit it to list entities of every version of the class. |
| classLevel | string | Optional | n/a | Class hierarchy level of `class`, either `Global` for a class the service provides or `SubKey` for one defined on your key set. Use it to disambiguate a class name defined at both levels. |
| cursor | string | Optional | n/a | Opaque pagination cursor from the previous page. Omit it to get the first page. |
| limit | number | Optional | n/a | Maximum number of entities to return in one page, up to 100. |
| filter | string | Optional | n/a | Filter expression evaluated against eventually consistent storage, so results can briefly lag writes. The parameter supports the full expression language over properties declared with filtering mode `full`. A call cannot combine it with `filterFast`. |
| filterFast | string | Optional | n/a | Filter expression evaluated against strongly consistent storage, so it reflects the latest writes. The parameter accepts up to 10 conditions by default (configurable per keyset) over properties declared with filtering mode `simple` or `full`. A call cannot combine it with `filter`. |
| sort | object \| string | Optional | n/a | Sort order of the returned entities. An object that maps each field to `'asc'`, `'desc'`, or `null` (service default), or a comma-separated string such as `'createdAt:desc'`. |

### Returns

`pubnub.dataSync.getEntities()` resolves with the HTTP status, an array of entities, and paging metadata.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getEntities({
    class: string,
    classVersion: number,
    classLevel: string,
    cursor: string,
    limit: number,
    filter: string,
    filterFast: string,
    sort: object | string,
}): Promise<DataSync.GetEntitiesResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getEntities({
  class: 'product',
  sort: { price: 'desc' },
  limit: 20,
});
console.log('getEntities response:', response.data);
console.log('getEntities meta:', response.meta);
```

#### Returns

```json
{
    "status": 200,
    "data": [
        {
            "id": "product-sneaker-42",
            "entityClass": "product",
            "entityClassVersion": 1,
            "payload": {
                "name": "Retro Sneaker",
                "price": 89.99,
                "stock": 12
            },
            "createdAt": "2026-07-13T09:15:00.000Z",
            "updatedAt": "2026-07-13T09:15:00.000Z",
            "eTag": "QrStUvWxYzAbCd",
            "expiresAt": "2026-08-13T09:15:00.000Z"
        }
    ],
    "meta": {
        "next_cursor": null,
        "has_next": false,
        "limit": 20
    }
}
```

#### Filter with filter

`filter` runs over properties declared with filtering mode `full` and is evaluated against eventually consistent storage, so a very recent write may not be matched yet. Reference a declared property by its `name`, not its `path`. Combine conditions with `&&` and `||`.

```javascript
// Reference a declared property by its `name` (not its `path`), and combine conditions with `&&`
// and `||`.
const response = await pubnub.dataSync.getEntities({
  class: 'product',
  filter: 'name LIKE "*sneaker*" && !(status == "discontinued")',
});
console.log('getEntities (filter) response:', response.data);
```

#### Filter with filterFast

`filterFast` is evaluated against strongly consistent storage, so it matches an object you just wrote. The expression runs over properties declared with filtering mode `simple` or `full` and accepts up to 10 conditions by default (configurable per keyset). The two parameters share the expression language of `filter`, so a call can send only one of them.

```javascript
const response = await pubnub.dataSync.getEntities({
  class: 'product',
  filterFast: 'price < 100 && stock > 0',
});
console.log('getEntities (filterFast) response:', response.data);
```

#### Page through results with cursor

Pass no `cursor` on the first call. Take `meta.next_cursor` from the response and pass it back as `cursor` on the next call. Stop when `meta.has_next` is `false`.

```javascript
let cursor: string | undefined;
let hasNext = true;
let page = 0;

while (hasNext) {
  const response = await pubnub.dataSync.getEntities({
    class: 'product',
    filterFast: 'price < 100',
    limit: 20,
    cursor,
  });
  console.log(`Page ${++page}:`, response.data);
  cursor = response.meta?.next_cursor ?? undefined;
  hasNext = response.meta?.has_next ?? false;
}
```

## Set entity

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.setEntity()` replaces a DataSync entity in full through a PubNub client for JavaScript. The call clears a stored `status` or `payload` that it omits.

The entity class is immutable after creation and cannot be sent. To guard against concurrent writes, pass `ifMatchesEtag`. See [optimistic concurrency with ETags](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#optimistic-concurrency-with-etags).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the entity. |
| data | object | Yes | n/a | The replacement entity data. The entity `class` is immutable and cannot be included. |
| > classVersion | number | Yes | n/a | Version of the entity class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The update succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.setEntity()` resolves with the HTTP status and the stored entity.

### Examples

#### Syntax

```javascript
pubnub.dataSync.setEntity({
    id: string,
    data: { classVersion: number, status: string, payload: object },
    ifMatchesEtag: string,
}): Promise<DataSync.SetEntityResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.setEntity({
  id: 'product-sneaker-42',
  data: {
    classVersion: 1,
    payload: { name: 'Retro Sneaker', price: 79.99, stock: 8 },
  },
});
console.log('setEntity response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "product-sneaker-42",
        "entityClass": "product",
        "entityClassVersion": 1,
        "payload": {
            "name": "Retro Sneaker",
            "price": 79.99,
            "stock": 8
        },
        "createdAt": "2026-07-13T09:15:00.000Z",
        "updatedAt": "2026-07-13T13:00:00.000Z",
        "eTag": "StUvWxYzAbCdEf",
        "expiresAt": "2026-08-13T09:15:00.000Z"
    }
}
```

## Update entity

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.updateEntity()` applies a JSON Patch (RFC 6902) partial update to a DataSync entity through a PubNub client for JavaScript. The call needs at least one patch operation, and fields that no operation addresses stay unchanged.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the entity. |
| add | object | Optional | n/a | Full JSON Pointers mapped to values to add. |
| replace | object | Optional | n/a | Full JSON Pointers mapped to replacement values. |
| remove | array | Optional | n/a | Full JSON Pointers to remove. |
| move | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is removed and re-added at `path`. |
| copy | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is duplicated to `path`. |
| test | object | Optional | n/a | Full JSON Pointers mapped to expected values. The patch fails if any value does not match. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The patch succeeds only if it still matches, otherwise the server returns `412`. |

Paths can target fields inside `payload` or top-level stored fields. A JSON Pointer that starts with `/payload/` targets a payload field, and a root pointer such as `/status` targets a top-level field. See [partial update](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#partial-update) for the JSON Pointer rules.

At least one of `add`, `replace`, `remove`, `move`, `copy`, or `test` must be provided.

A single `updateEntity()` call can mix `add`, `replace`, `remove`, `move`, `copy`, and `test`. The order you write the keys in does not matter. The SDK always sends the operations in the same order, with `test` last.

### Returns

`pubnub.dataSync.updateEntity()` resolves with the HTTP status and the updated entity.

### Examples

#### Syntax

```javascript
pubnub.dataSync.updateEntity({
    id: string,
    add: object,
    replace: object,
    remove: string[],
    move: { from: string, path: string }[],
    copy: { from: string, path: string }[],
    test: object,
    ifMatchesEtag: string,
}): Promise<DataSync.UpdateEntityResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.updateEntity({
  id: 'product-sneaker-42',
  replace: { '/payload/price': 79.99 },
});
console.log('updateEntity response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "product-sneaker-42",
        "entityClass": "product",
        "entityClassVersion": 1,
        "payload": {
            "name": "Retro Sneaker",
            "price": 79.99,
            "stock": 12
        },
        "createdAt": "2026-07-13T09:15:00.000Z",
        "updatedAt": "2026-07-13T13:05:00.000Z",
        "eTag": "UvWxYzAbCdEfGh",
        "expiresAt": "2026-08-13T09:15:00.000Z"
    }
}
```

#### Combine patch operations, and guard the write with ifMatchesEtag

A single `updateEntity()` call can mix `add`, `replace`, `remove`, `move`, `copy`, and `test`. The order you write the keys in does not matter. The SDK always sends the operations in the same order, with `test` last. Add `ifMatchesEtag` (the `eTag` from a prior read) to reject the patch with a `412` if the entity changed since you read it, instead of overwriting a concurrent change.

```javascript
// A single `updateEntity` call can mix `add`, `replace`, `remove`, `move`, `copy`, and `test`. Add
// `ifMatchesEtag` (the `eTag` from a prior read) to reject the patch with a 412 if the entity
// changed since you read it, instead of silently overwriting a concurrent change.
const response = await pubnub.dataSync.updateEntity({
  id: 'product-sneaker-42',
  test: { '/payload/stock': 8 },
  replace: { '/payload/price': 74.99 },
  add: { '/payload/tags/0': 'clearance' },
  remove: ['/payload/legacy/field'],
  move: [{ from: '/payload/legacyName', path: '/payload/displayName' }],
  copy: [{ from: '/payload/displayName', path: '/payload/previousName' }],
  ifMatchesEtag: 'StUvWxYzAbCdEf',
});
console.log('updateEntity (combined patch) response:', response.data);
```

## Remove entity

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.removeEntity()` deletes a DataSync entity by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the entity. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The delete succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.removeEntity()` resolves with only the HTTP status.

### Examples

#### Syntax

```javascript
pubnub.dataSync.removeEntity({
    id: string,
    ifMatchesEtag: string,
}): Promise<DataSync.RemoveEntityResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.removeEntity({ id: 'product-sneaker-42' });
console.log('removeEntity response status:', response.status);
```

#### Returns

```json
{
    "status": 200
}
```

## Create relationship

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.createRelationship()` creates a DataSync relationship through a PubNub client for JavaScript. The server generates the identifier unless the call supplies one.

A relationship links two entities and carries its own `payload`. In the running example, the `ProductOwner` relationship links `seller-bob` to `product-sneaker-42`.

Relationships are instances of the [relationship classes](https://www.pubnub.com/docs/admin-api/get-all-relationship-classes.md) you define on your key set. A relationship class declares the cardinality the service enforces (`one-to-one`, `one-to-many`, or `many-to-many`) and, optionally, which entity class each side must belong to. Use [Create a new relationship class](https://www.pubnub.com/docs/admin-api/create-a-new-relationship-class.md) to declare a class, and [Get relationship class by name and version](https://www.pubnub.com/docs/admin-api/get-relationship-class-by-name-and-version.md) to read it back.

The relationship class's cardinality is enforced on create. A relationship that violates its class's cardinality is rejected with a `409`.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Optional | n/a | Identifier of the new DataSync relationship. Omit it to let the server generate one. The identifier has at most 255 characters. |
| class | string | Yes | n/a | Name of the relationship class that this instance belongs to. The class is set at creation and immutable afterward. |
| entityAId | string | Yes | n/a | Identifier of the first linked entity. Immutable after creation. |
| entityBId | string | Yes | n/a | Identifier of the second linked entity. Immutable after creation. |
| data | object | Yes | n/a | The mutable relationship data. |
| > classVersion | number | Yes | n/a | Version of the relationship class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |

### Returns

`pubnub.dataSync.createRelationship()` resolves with the HTTP status and the created relationship.

### Examples

#### Syntax

```javascript
pubnub.dataSync.createRelationship({
    id: string,
    class: string,
    entityAId: string,
    entityBId: string,
    data: { classVersion: number, status: string, payload: object },
}): Promise<DataSync.CreateRelationshipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.createRelationship({
  id: 'rel-bob-owns-sneaker-42',
  class: 'ProductOwner',
  entityAId: 'seller-bob',
  entityBId: 'product-sneaker-42',
  data: {
    classVersion: 1,
    payload: { since: '2026-07-13' },
  },
});
console.log('createRelationship response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "rel-bob-owns-sneaker-42",
        "entityAId": "seller-bob",
        "entityBId": "product-sneaker-42",
        "relationshipClass": "ProductOwner",
        "relationshipClassVersion": 1,
        "payload": {
            "since": "2026-07-13"
        },
        "createdAt": "2026-07-13T09:20:00.000Z",
        "updatedAt": "2026-07-13T09:20:00.000Z",
        "eTag": "WxYzAbCdEfGhIj",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Get relationship

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getRelationship()` returns one DataSync relationship by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the relationship. |

### Returns

`pubnub.dataSync.getRelationship()` resolves with the HTTP status and the requested relationship.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getRelationship({
    id: string,
}): Promise<DataSync.GetRelationshipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getRelationship({
  id: 'rel-bob-owns-sneaker-42',
});
console.log('getRelationship response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "rel-bob-owns-sneaker-42",
        "entityAId": "seller-bob",
        "entityBId": "product-sneaker-42",
        "relationshipClass": "ProductOwner",
        "relationshipClassVersion": 1,
        "payload": {
            "since": "2026-07-13"
        },
        "createdAt": "2026-07-13T09:20:00.000Z",
        "updatedAt": "2026-07-13T09:20:00.000Z",
        "eTag": "WxYzAbCdEfGhIj",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Get all relationships

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.getRelationships()` returns a paginated list of DataSync relationships through a PubNub client for JavaScript.

The `class` parameter is required. Filter by `entityAId` or `entityBId` to list a specific entity's links. For pagination, filtering, and sorting, see [sorting and pagination](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#sorting-and-pagination).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| class | string | Yes | n/a | Name of the relationship class to list. The `class` parameter is required. |
| classVersion | number | Optional | n/a | Class version to list. Omit it to list relationships of every version of the class. |
| entityAId | string | Optional | n/a | List only relationships whose first entity has this identifier. |
| entityBId | string | Optional | n/a | List only relationships whose second entity has this identifier. |
| cursor | string | Optional | n/a | Opaque pagination cursor from the previous page. Omit it to get the first page. |
| limit | number | Optional | n/a | Maximum number of relationships to return in one page, up to 100. |
| filter | string | Optional | n/a | Filter expression evaluated against eventually consistent storage, so results can briefly lag writes. The parameter supports the full expression language over properties declared with filtering mode `full`. A call cannot combine it with `filterFast`. |
| filterFast | string | Optional | n/a | Filter expression evaluated against strongly consistent storage, so it reflects the latest writes. The parameter accepts up to 10 conditions by default (configurable per keyset) over properties declared with filtering mode `simple` or `full`. A call cannot combine it with `filter`. |
| sort | object \| string | Optional | n/a | Sort order of the returned relationships. An object that maps each field to `'asc'`, `'desc'`, or `null` (service default), or a comma-separated string such as `'createdAt:desc'`. |

### Returns

`pubnub.dataSync.getRelationships()` resolves with the HTTP status, an array of relationships, and paging metadata.

### Examples

#### Syntax

```javascript
pubnub.dataSync.getRelationships({
    class: string,
    classVersion: number,
    entityAId: string,
    entityBId: string,
    cursor: string,
    limit: number,
    filter: string,
    filterFast: string,
    sort: object | string,
}): Promise<DataSync.GetRelationshipsResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.getRelationships({
  class: 'ProductOwner',
  entityAId: 'seller-bob',
  limit: 20,
});
console.log('getRelationships response:', response.data);
console.log('getRelationships meta:', response.meta);
```

#### Returns

```json
{
    "status": 200,
    "data": [
        {
            "id": "rel-bob-owns-sneaker-42",
            "entityAId": "seller-bob",
            "entityBId": "product-sneaker-42",
            "relationshipClass": "ProductOwner",
            "relationshipClassVersion": 1,
            "payload": {
                "since": "2026-07-13"
            },
            "createdAt": "2026-07-13T09:20:00.000Z",
            "updatedAt": "2026-07-13T09:20:00.000Z",
            "eTag": "WxYzAbCdEfGhIj",
            "expiresAt": "2026-08-12T09:00:00.000Z"
        }
    ],
    "meta": {
        "next_cursor": null,
        "has_next": false,
        "limit": 20
    }
}
```

#### List an entity's incoming links with entityBId

Pass `entityBId` instead of `entityAId` to list relationships where the entity is on the second side of the link.

```javascript
// Pass `entityBId` instead of `entityAId` to list relationships where the entity is on the second
// side of the link.
const response = await pubnub.dataSync.getRelationships({
  class: 'ProductOwner',
  entityBId: 'product-sneaker-42',
  limit: 20,
});
console.log('getRelationships (entityBId) response:', response.data);
```

#### Filter with filter

`filter` runs over properties declared with filtering mode `full` and is evaluated against eventually consistent storage, so a very recent write may not be matched yet. Reference a declared property by its `name`, not its `path`.

```javascript
const response = await pubnub.dataSync.getRelationships({
  class: 'ProductOwner',
  filter: '!(tier == "platinum")',
});
console.log('getRelationships (filter) response:', response.data);
```

#### Filter with filterFast

`filterFast` is evaluated against strongly consistent storage, so it matches an object you just wrote. The expression runs over properties declared with filtering mode `simple` or `full` and accepts up to 10 conditions by default (configurable per keyset). The two parameters share the expression language of `filter`, so a call can send only one of them.

```javascript
const response = await pubnub.dataSync.getRelationships({
  class: 'ProductOwner',
  filterFast: 'tier == "gold"',
});
console.log('getRelationships (filterFast) response:', response.data);
```

#### Page through results with cursor

Pass no `cursor` on the first call. Take `meta.next_cursor` from the response and pass it back as `cursor` on the next call. Stop when `meta.has_next` is `false`.

```javascript
let cursor: string | undefined;
let hasNext = true;
let page = 0;

while (hasNext) {
  const response = await pubnub.dataSync.getRelationships({
    class: 'ProductOwner',
    entityAId: 'seller-bob',
    limit: 20,
    cursor,
  });
  console.log(`Page ${++page}:`, response.data);
  cursor = response.meta?.next_cursor ?? undefined;
  hasNext = response.meta?.has_next ?? false;
}
```

## Set relationship

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.setRelationship()` replaces a DataSync relationship in full through a PubNub client for JavaScript. The call clears a stored `status` or `payload` that it omits.

The linked entity identifiers are immutable, so resend the same `entityAId` and `entityBId`. To guard against concurrent writes, pass `ifMatchesEtag`. See [optimistic concurrency with ETags](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#optimistic-concurrency-with-etags).

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the relationship. |
| entityAId | string | Yes | n/a | Identifier of the first linked entity. Must match the value set at creation. |
| entityBId | string | Yes | n/a | Identifier of the second linked entity. Must match the value set at creation. |
| data | object | Yes | n/a | The replacement relationship data. The relationship `class` is immutable and cannot be included. |
| > classVersion | number | Yes | n/a | Version of the relationship class schema. |
| > status | string | Optional | n/a | Free-form lifecycle status. The maximum length is 100 characters. |
| > payload | object | Optional | n/a | Free-form JSON object holding your application data. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The update succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.setRelationship()` resolves with the HTTP status and the stored relationship.

### Examples

#### Syntax

```javascript
pubnub.dataSync.setRelationship({
    id: string,
    entityAId: string,
    entityBId: string,
    data: { classVersion: number, status: string, payload: object },
    ifMatchesEtag: string,
}): Promise<DataSync.SetRelationshipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.setRelationship({
  id: 'rel-bob-owns-sneaker-42',
  entityAId: 'seller-bob',
  entityBId: 'product-sneaker-42',
  data: {
    classVersion: 1,
    payload: { since: '2026-07-13', tier: 'gold' },
  },
});
console.log('setRelationship response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "rel-bob-owns-sneaker-42",
        "entityAId": "seller-bob",
        "entityBId": "product-sneaker-42",
        "relationshipClass": "ProductOwner",
        "relationshipClassVersion": 1,
        "payload": {
            "since": "2026-07-13",
            "tier": "gold"
        },
        "createdAt": "2026-07-13T09:20:00.000Z",
        "updatedAt": "2026-07-13T14:00:00.000Z",
        "eTag": "YzAbCdEfGhIjKl",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Update relationship

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.updateRelationship()` applies a JSON Patch (RFC 6902) partial update to a DataSync relationship through a PubNub client for JavaScript. The call needs at least one patch operation, and fields that no operation addresses stay unchanged.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the relationship. |
| add | object | Optional | n/a | Full JSON Pointers mapped to values to add. |
| replace | object | Optional | n/a | Full JSON Pointers mapped to replacement values. |
| remove | array | Optional | n/a | Full JSON Pointers to remove. |
| move | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is removed and re-added at `path`. |
| copy | array | Optional | n/a | Pairs of JSON Pointers, each with a `from` and a `path`. The value at `from` is duplicated to `path`. |
| test | object | Optional | n/a | Full JSON Pointers mapped to expected values. The patch fails if any value does not match. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The patch succeeds only if it still matches, otherwise the server returns `412`. |

Paths can target fields inside `payload` or top-level stored fields. A JSON Pointer that starts with `/payload/` targets a payload field, and a root pointer such as `/status` targets a top-level field. See [partial update](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md#partial-update) for the JSON Pointer rules.

At least one of `add`, `replace`, `remove`, `move`, `copy`, or `test` must be provided.

A single `updateRelationship()` call can mix `add`, `replace`, `remove`, `move`, `copy`, and `test`. The order you write the keys in does not matter. The SDK always sends the operations in the same order, with `test` last.

### Returns

`pubnub.dataSync.updateRelationship()` resolves with the HTTP status and the updated relationship.

### Examples

#### Syntax

```javascript
pubnub.dataSync.updateRelationship({
    id: string,
    add: object,
    replace: object,
    remove: string[],
    move: { from: string, path: string }[],
    copy: { from: string, path: string }[],
    test: object,
    ifMatchesEtag: string,
}): Promise<DataSync.UpdateRelationshipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.updateRelationship({
  id: 'rel-bob-owns-sneaker-42',
  replace: { '/payload/tier': 'platinum' },
});
console.log('updateRelationship response:', response.data);
```

#### Returns

```json
{
    "status": 200,
    "data": {
        "id": "rel-bob-owns-sneaker-42",
        "entityAId": "seller-bob",
        "entityBId": "product-sneaker-42",
        "relationshipClass": "ProductOwner",
        "relationshipClassVersion": 1,
        "payload": {
            "since": "2026-07-13",
            "tier": "platinum"
        },
        "createdAt": "2026-07-13T09:20:00.000Z",
        "updatedAt": "2026-07-13T14:05:00.000Z",
        "eTag": "AbCdEfGhIjKlMn",
        "expiresAt": "2026-08-12T09:00:00.000Z"
    }
}
```

## Remove relationship

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

`pubnub.dataSync.removeRelationship()` deletes a DataSync relationship by identifier through a PubNub client for JavaScript.

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| id | string | Yes | n/a | Identifier of the relationship. |
| ifMatchesEtag | string | Optional | n/a | The `eTag` from a prior read. The delete succeeds only if it still matches, otherwise the server returns `412`. |

### Returns

`pubnub.dataSync.removeRelationship()` resolves with only the HTTP status.

### Examples

#### Syntax

```javascript
pubnub.dataSync.removeRelationship({
    id: string,
    ifMatchesEtag: string,
}): Promise<DataSync.RemoveRelationshipResponse>
```

#### Basic usage

```javascript
const response = await pubnub.dataSync.removeRelationship({
  id: 'rel-bob-owns-sneaker-42',
});
console.log('removeRelationship response status:', response.status);
```

#### Returns

```json
{
    "status": 200
}
```

## Where each event is delivered

This method requires that the Access Manager and DataSync add-ons are [enabled](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) for your key in the [Admin Portal](https://admin.pubnub.com/). Related: [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md), [Enable DataSync](https://www.pubnub.com/docs/data-storage/structured-data/enable-structured-data.md).

The PubNub JavaScript SDK receives a DataSync change event on every channel whose name matches the identifier of an object that the change affects.

DataSync objects can publish `create`, `update`, and `delete` events that you receive in real time with a `dataSync` listener. Create a [DataSync SDK entity](https://www.pubnub.com/docs/sdks/javascript/api-reference/subscribe.md#datasync-sdk-entities) for the object, subscribe to it, and attach the listener, described in the [Add DataSync listener](https://www.pubnub.com/docs/sdks/javascript/api-reference/subscribe.md#add-datasync-listener) section of Publish and subscribe.

Each event names the change in `event`, identifies the object kind in `objectType` (`user`, `channel`, `membership`, `entity`, or `relationship`), and carries the object state in `data`. For a `delete` event, `data` holds only the `id` and, when the service reports it, `deletedAt`.

A relationship or membership change is delivered on the ids of the two objects it links, and never on a channel named after the relationship or membership id. A `create` for a user, channel, or entity is delivered only on its own `id`. An `update` or `delete` for a user, channel, or entity is also delivered on the `id` of every object connected to it at the time of the change. The connection can be a relationship or membership, in either direction.

The table shows the mapping for each kind of change.

| Change to | `create` delivered on | `update` / `delete` delivered on |
| --- | --- | --- |
| A user | The user's `id` | The user's `id`, plus the `id` of every entity, user, or channel connected to it by a relationship or membership |
| A channel | The channel's `id` | The channel's `id`, plus the `id` of every entity, user, or channel connected to it by a relationship or membership |
| An entity | The entity's `id` | The entity's `id`, plus the `id` of every entity, user, or channel connected to it by a relationship or membership |
| A membership | **Both** the `userId` and the `channelId` of the membership | **Both** the `userId` and the `channelId` of the membership |
| A relationship | **Both** the `entityAId` and the `entityBId` of the relationship | **Both** the `entityAId` and the `entityBId` of the relationship |

So to see memberships appear and disappear for Alice, observe `user-alice` rather than the membership id. Unlike entity/user/channel events, a membership or relationship `create` is delivered on both linked ids too, the same as its `update`/`delete`. A client observing both sides of the same link receives the change twice, once per channel. Deduplicate create and update events on `data.id` and `data.updatedAt`, and delete events on `data.id` and `data.deletedAt`.

DataSync events are off by default. You enable event publishing for each class in the [Admin Portal](https://admin.pubnub.com). See [enabling DataSync events](https://www.pubnub.com/docs/data-storage/structured-data/events.md#enable-datasync-events). See [receiving events](https://www.pubnub.com/docs/data-storage/structured-data/events.md) for the listener flow.

### Examples

#### Basic usage

```javascript
// DataSync objects publish create/update/delete events over a `dataSync` listener. Create a
// DataSync SDK entity for the object, subscribe to it, and attach the listener.
const productSubscription = pubnub.dataSyncEntity('product-sneaker-42').subscription();
productSubscription.onDataSync = (event) => console.log(event.message.event, event.message.data);
productSubscription.subscribe();
```

Last updated at: 2026-11-09T13:29:01.000Z
