---
source_url: https://www.pubnub.com/docs/general/data-sync/access-control
title: Access control and limits in DataSync
updated_at: 2026-09-08T16:52:30.000Z
---

# Access control and limits in DataSync

## Documentation index

To discover more PubNub resources:

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

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

DataSync is secure by default. Access Manager must be enabled on your keyset, and every request must be authorized. There is no anonymous access.

DataSync extends Access Manager. It uses the same tokens that already protect your channels, with new resource types for entities, relationships, and memberships. Refer to [Access control and permissions management](https://www.pubnub.com/docs/general/security/access-control.md) for how Access Manager tokens work in general. This page covers what DataSync adds on top.

## Authorizing requests

Each DataSync request carries exactly one credential: an Access Manager token (for your clients) or a request signature (for your servers). Sending both is an error, and sending neither is rejected with a 401.

Over REST, the credential is a query parameter, `auth` for a token or `signature` for a signed request. Both are documented on every DataSync endpoint, for example on [Get entities](https://www.pubnub.com/docs/sdks/rest-api/get-entities.md).

:::warning Access Manager is required
If Access Manager is not enabled on your keyset, DataSync requests fail. Enable Access Manager in [Admin Portal](https://admin.pubnub.com) before using DataSync.
:::

DataSync itself is a separate keyset switch. If DataSync isn't enabled on the keyset, every request fails with a `403`, whatever credential it carries. Refer to [Configuration](https://www.pubnub.com/docs/general/data-sync/overview.md#configuration).

The typical split follows the same pattern as the rest of PubNub: your servers hold signing credentials and grant tokens, and your clients use the tokens they're granted.

###### Every operation is authorized

Access Manager governs every read and write on DataSync objects. Refer to [Data operations](https://www.pubnub.com/docs/general/data-sync/data-operations.md).

## Permissions and resource types

Access Manager tokens grant permissions on resources. DataSync adds three resource types:

* `datasync:entities`
* `datasync:relationships`
* `datasync:memberships`

User and channel entities reuse the existing `channels` and `users` resource types.

:::warning DataSync user entities Access Manager grants
DataSync checks the `users` resource type for user entities. It does not check `uuids`, the resource type App Context and the general [Access control](https://www.pubnub.com/docs/general/security/access-control.md) examples use for granting access to a User ID's metadata.
A token that only grants `uuids` permissions does not authorize DataSync operations on the corresponding user entity. Grant `users` explicitly.
`users` and `uuids` both take a User ID, but they are separate scopes that different products read, which is why a `uuids`-only token fails here. A single grant request may still carry only one of them: SDKs reject a request that names both, and the check spans the whole request, so `resources.users` can't be paired with `patterns.uuids` either. `authorizedUserId` and `authorized_uuid` pair up the same way. Keep `uuids` only while you still grant App Context UUID metadata permissions. Refer to [Grant DataSync permissions](https://www.pubnub.com/docs/general/security/access-control.md#grant-datasync-permissions) for the grant call itself.
:::

Each DataSync object kind authorizes against a specific resource type. Generic entities, relationships, and memberships use the new `datasync:` types, while user and channel entities reuse the existing `users` and `channels` types, not `uuids`:

Every DataSync resource takes the same four CRUD permissions, and so does `users`:

| Resource type | Permissions |
| --- | --- |
| `users` | `create`, `get`, `update`, `delete` |
| `channels` | `read`, `write`, `get`, `manage`, `update`, `join`, `delete` |
| `datasync:entities`, `datasync:relationships`, `datasync:memberships` | `create`, `get`, `update`, `delete` |

`read`, `write`, `manage`, and `join` stay messaging concepts. They aren't available on `users` or on any `datasync:` resource type. On `channels` they gate publishing, subscribing, and Presence, so a DataSync read of a channel entity turns on `get`, not `read`.

Grants target either exact ids or patterns (regular expressions), following the same model Access Manager already uses for channels.

In [Bob's marketplace](https://www.pubnub.com/docs/general/data-sync/overview.md#running-example-bobs-live-marketplace), Bob's token could grant `update` on `product-sneaker-42` by exact ID, and `get` on every product, including `product-sneaker-42` and `product-cap-7`, through a single pattern matching the `product-` prefix. Reading a DataSync object is `get`, not `read`. `read` is a channel permission, and on a DataSync object it governs only whether you can subscribe to its events.

:::note Subscribing to events
Receiving DataSync events requires subscribe permission on every channel an event reaches, granted the same way as any other channel grant. An entity's own ID channel is always one of them for events about that entity.
A relationship or membership has no ID channel of its own, its events reach only the ID channels of the two entities it links. Refer to [Events](https://www.pubnub.com/docs/general/data-sync/events.md#where-events-are-published) for the full routing model.
Beyond the object's own ID channel, each named projection on the class has a channel of its own, `__<projection>__<id>`. Subscribing to the ID channel gives you the `__default__` view of the payload, and subscribing to a projection channel gives you that projection's view. A client that needs the `admin` view of `product-sneaker-42` in real time needs subscribe permission on `__admin__product-sneaker-42`, not on `product-sneaker-42`.
This holds however the client subscribes. A [DataSync SDK entity](https://www.pubnub.com/docs/general/entities.md#datasync-sdk-entities) with a `projection` option resolves to the same channel name, so it needs the same grant.
:::

## Field-level access with projections

Beyond granting access to a resource, a token can also select which fields of that resource it can read and write. Tokens carry a `pn-projections` map inside their `meta` section, with two nested maps:

* `res` for exact resource ids
* `pat` for regular-expression patterns

Each is keyed by object kind plus ID (or pattern), with each value naming the projection granted for that resource. These keys use their own `datasync:` prefixes, which are separate from the resource types you grant permissions on above. Users and channels are granted through `users` and `channels`, but their projection keys are `datasync:users:` and `datasync:channels:`:

* `datasync:users:` for users
* `datasync:entities:` for generic entities
* `datasync:channels:` for channels
* `datasync:relationships:` for relationships
* `datasync:memberships:` for memberships

```json
{
  "meta": {
    "pn-projections": {
      "res": {
        "datasync:users:user-alice": "admin"
      },
      "pat": {}
    }
  }
}
```

With no matching entry in `res` or `pat`, a token gets the `__default__` projection. Requesting a [projection](https://www.pubnub.com/docs/general/data-sync/projections.md) name that doesn't exist on the class is rejected with a 403.

A replace is scoped to the token's projection, which is a guarantee worth designing around: a token holding a narrow projection can't wipe the fields it can't see, because fields outside the projection keep their stored values.

:::note Projections and events
Projections scope events as well as API reads and writes. Each event is published once per projection declared on the object's class: to the object's ID channel for `__default__`, and to `__<projection>__<id>` for each named projection, with the payload on each channel limited to that projection's fields. Grant subscribe permission on the projection channel your client needs. Refer to [Events](https://www.pubnub.com/docs/general/data-sync/events.md) and [Projections](https://www.pubnub.com/docs/general/data-sync/projections.md) for the full model.
:::

## Limits

Current service limits for DataSync:

| Limit | Value |
| --- | --- |
| Page size (`limit`) | 1 to 100, default 20 |
| Pagination direction | forward only |
| Projections per class | 3, including `__default__` |
| Projection name length | 64 characters |
| `id` length | 255 characters |
| `id` characters | No whitespace, commas, colons, asterisks, forward slashes, backslashes, or control characters |
| Class name length | 128 characters |
| `status` length | 100 characters |
| Filter predicate count | 10 by default, configurable per keyset |
| TTL (`config.ttlSec`) | 0 to 315569260 seconds (about 10 years), default 2678400 seconds (31 days) |

## Error responses

Every error body uses the same envelope, and `errorCode` is the stable programmatic contract, not the message text. A validation failure can return more than one item:

```json
{
  "errors": [
    { "errorCode": "DS-0650", "message": "...", "path": "..." }
  ]
}
```

Every DataSync code carries the `DS-` prefix. `path` appears only on codes that can point at one input, and its form follows the code: a JSON Pointer into the request payload, a query parameter name such as `filter` or `sort`, a field name, or a class property name.

Each Core REST API endpoint lists the exact codes its own responses can return, per status. Refer to the Responses section on any endpoint, for example [Create entity](https://www.pubnub.com/docs/sdks/rest-api/create-entity.md).

Common failure conditions and the status code DataSync returns for each:

| Condition | Status |
| --- | --- |
| No credential | 401 |
| An expired, revoked, malformed, or otherwise unauthorized token | 403 |
| A projection name that doesn't exist on the class | 403 |
| An object `id` that doesn't exist, on get, replace, partial update, or delete | 404 |
| A class, or a specific class version, that doesn't exist on create | 404 |
| A relationship or membership endpoint where a linked entity doesn't exist | 404 |
| An unknown subscribe key | 404 |
| An `id` that already exists on create | 409 |
| A relationship that would violate its class's cardinality | 409 |
| A stale `eTag` on `If-Match` | 412 |
| A patch that touches any path other than `/status`, the class version, and `/payload` or anything under it | 400 |
| A payload that fails a declared property's type or nullability check | 400 |
| Both `filter_fast` and `filter` on one request | 400 |
| A malformed filter, or more predicates than the limit allows | 400 |
| Sorting by a property that isn't sortable in that context, or an invalid cursor | 400 |
| Malformed JSON in the request body | 400 |
| An unsupported `Content-Type` | 415 |
| An `Accept` header DataSync can't satisfy | 406 |
| A write outside the token's resolved projection | 403 |
| Modifying the definition of a class defined at the Global level, such as `User`, `Channel`, or `Membership` | 403 |
| A class property named `id`, `createdAt`, or `updatedAt`, or a `status` property at any path other than `/status` | 400 |
| Deleting a class version that another class extends | 409 |

The two projection-related 403s, an unknown projection name and a write outside the resolved projection, share one error code and the deliberately generic message `Invalid value for 'auth' parameter`. Neither response names the projection or the field involved. That's by design, so the response doesn't disclose which projections or fields exist.

In Bob's marketplace, granting `user-alice` `get` access to `product-sneaker-42` and to every `product-` entity by pattern, scoped to the `public` projection, looks like this:

### JavaScript

```javascript
const token = await pubnub.grantToken({
    ttl: 15,
    authorizedUserId: 'user-alice',
    resources: {
        dataSync: {
            entities: {
                'product-sneaker-42': { get: true },
            },
        },
    },
    patterns: {
        dataSync: {
            entities: {
                'product-.*': { get: true },
            },
        },
    },
    dataSyncProjections: {
        resources: {
            entities: {
                'product-sneaker-42': 'public',
            },
        },
        patterns: {
            entities: {
                'product-.*': 'public',
            },
        },
    },
})
```

### C#

```csharp
PNResult<PNAccessManagerTokenResult> grantResponse = await pubnub.GrantToken()
    .TTL(15)
    .AuthorizedUuid("user-alice")
    .Resources(new PNTokenResources
    {
        DataSync = new PNDataSyncTokenScopes
        {
            Entities = new Dictionary<string, PNTokenAuthValues>
            {
                { "product-sneaker-42", new PNTokenAuthValues { Get = true } },
            },
        },
    })
    .Patterns(new PNTokenPatterns
    {
        DataSync = new PNDataSyncTokenScopes
        {
            Entities = new Dictionary<string, PNTokenAuthValues>
            {
                { "product-.*", new PNTokenAuthValues { Get = true } },
            },
        },
    })
    .DataSyncProjections(new PNDataSyncProjections
    {
        Resources = new PNDataSyncProjectionScope
        {
            Entities = new Dictionary<string, string>
            {
                { "product-sneaker-42", "public" },
            },
        },
        Patterns = new PNDataSyncProjectionScope
        {
            Entities = new Dictionary<string, string>
            {
                { "product-.*", "public" },
            },
        },
    })
    .ExecuteAsync();
```

Refer to [Grant token (JavaScript)](https://www.pubnub.com/docs/sdks/javascript/api-reference/access-manager.md#grant-token) and [Grant token (C#)](https://www.pubnub.com/docs/sdks/c-sharp/api-reference/access-manager.md#grant-token) for the full parameter reference, including how `users` and `channels` permissions cover DataSync's built-in user and channel entities.

## Terms in this document

* **Access Manager** - A cryptographic, token-based permission administrator that allows you to regulate clients' access to PubNub resources, such as channels, channel groups, and user IDs.
* **Class** - A versioned type definition (name plus integer version) for entities or relationships in DataSync. Classes carry property definitions that declare which payload fields are validated, filterable, and scoped by projections.
* **Membership** - A relationship in DataSync that links a channel to a user, using the built-in many-to-many Membership class.
* **Projection** - A named view over an object's payload fields in DataSync, controlling which fields a client can read and write based on its Access Manager token.
* **Relationship** - A typed link between two entities in DataSync, with its own payload and system fields, typed by a relationship class that declares cardinality.

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