---
source_url: https://www.pubnub.com/docs/data-storage/structured-data/data-model
title: DataSync data model
updated_at: 2026-09-30T07:20:08.000Z
---

# DataSync data model

## 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 stores everything with two primitives: entities (objects) and relationships (links between two objects). Both are typed by classes. Users, channels, and memberships are built-in classes, so everything on this page applies to them too.

A class types each entity and relationship. In a wishlist example, the entity class Product v1 types the entity `product-sneaker-42`. The relationship class Wishlist v1 types the relationship `wishlist-alice-sneaker`, which links entity A (`product-sneaker-42`) to entity B (`user-alice`, an instance of User v1).

```mermaid
flowchart LR
    PRODUCT_CLASS["<b>Product v1</b><br/>entity class"]
    PRODUCT["<b>product-sneaker-42</b><br/>Product v1"]
    WISHLIST_CLASS["<b>Wishlist v1</b><br/>relationship class"]
    WISHLIST["<b>wishlist-alice-sneaker</b><br/>Wishlist v1"]
    USER["<b>user-alice</b><br/>User v1"]

    PRODUCT_CLASS -->|"types"| PRODUCT
    PRODUCT -->|"entity A"| WISHLIST
    WISHLIST_CLASS -->|"types"| WISHLIST
    WISHLIST -->|"entity B"| USER

    class PRODUCT_CLASS,WISHLIST_CLASS emphasis
```

## Entities

A DataSync entity is a JSON object made up of system fields and a `payload` field that holds your application data. Every entity is an instance of exactly one entity class, and records which version of that class it currently pins to. The class name is fixed at creation, while the version can be changed later.

:::note Don't confuse this with an SDK entity
An entity here is a stored server-side record. An SDK entity is a local handle you create on the client, with no stored state of its own.
:::

The entity endpoints are `Get entities`, `Get entity`, `Create entity`, `Update entity`, `Patch entity`, and `Delete entity`.

System fields on every entity:

| Field | Purpose |
| --- | --- |
| `id` | The entity's identifier |
| `status` | A free-form application lifecycle string, 1 to 100 characters |
| `eTag` | Optimistic concurrency marker |
| `createdAt` | Creation timestamp |
| `updatedAt` | Last modification timestamp |
| `expiresAt` | Expiry timestamp |
| `entityClass` | The name of the entity class this instance belongs to |
| `entityClassVersion` | The class version this instance pins to |
| `entityClassLevel` | Whether that class is `Global` or `SubKey` (keyset-level) |

The `payload` field sits alongside these system fields at the same level, not nested inside them. It is where your application data lives.

You can supply your own `id` on create, or let the server generate a UUID for you. An `id` must be 1 to 255 characters and can't contain whitespace, commas, colons, asterisks, forward slashes, backslashes, or control characters. It must be unique within the keyset: supplying an `id` that is already taken is rejected with a `409 Conflict`.

An `id` becomes the name of the channel the object's events publish to, so the IDs you pick decide what a client can subscribe to. Periods are allowed, which means dot-namespaced IDs such as `product.sneaker-42` can be followed as a group with [Wildcard Subscribe](https://www.pubnub.com/docs/pub-sub/subscribe/overview.md).

## Relationships

A relationship is a typed link between two entities, entity A and entity B, with its own `payload` field. Every relationship is an instance of exactly one relationship class.

The relationship endpoints are `Get relationships`, `Get relationship`, `Create relationship`, `Update relationship`, `Patch relationship`, and `Delete relationship`.

System fields on every relationship:

| Field | Purpose |
| --- | --- |
| `id` | The relationship's identifier |
| `entityAId` | The linked entity A's ID, immutable after creation |
| `entityBId` | The linked entity B's ID, immutable after creation |
| `status` | A free-form application lifecycle string, 1 to 100 characters |
| `eTag` | Optimistic concurrency marker |
| `createdAt` | Creation timestamp |
| `updatedAt` | Last modification timestamp |
| `expiresAt` | Expiry timestamp |
| `relationshipClass` | The name of the relationship class this instance belongs to |
| `relationshipClassVersion` | The class version this instance pins to |

:::note Memberships name their sides
A membership is a relationship, but it exposes its two sides as `channelId` and `userId` rather than the generic `entityAId` and `entityBId`. The channel is entity A and the user is entity B. Refer to [Built-in types](https://www.pubnub.com/docs/data-storage/structured-data/built-in-types.md).
:::

Relationship classes declare a cardinality, and DataSync enforces it when a relationship is created:

* **One-to-one**: an entity can participate in at most one relationship instance of the class, on either side.
* **One-to-many**: an entity is bound to whichever side it first appears on for that class. Once bound, it can appear on that same side any number of times, but never on the other side.
* **Many-to-many**: no side constraints. The same entity can appear on either side any number of times. Only the exact ordered pair must be unique within the class.

A relationship that violates its class's cardinality is rejected with a `409 Conflict`. Both linked entities must already exist, or the request is rejected with a `404 Not Found`.

A relationship expires when the earlier of its two entities' expiry times arrives, computed once when the relationship is created.

## Classes

A class is a versioned type: a name plus an integer version. Multiple versions of the same class can coexist. Each instance records which version it currently pins to. You choose the version at creation, and you can move an instance to another existing version of the same class by setting `entityClassVersion` or `relationshipClassVersion` on an update or a partial update. The class name itself can't change.

Every class is described by two independent things: what it types (an entity or a relationship) and who owns it (PubNub or you). These are separate axes, so every class has one answer on each.

### Entity classes and relationship classes

* An **entity class** types a stored object. Entity classes support inheritance, so a class you define can extend a Global class and add its own fields. Read one back with [Get entity class by ID](https://www.pubnub.com/docs/admin-api/get-entity-class-by-id.md).
* A **relationship class** types a link between two entities. Relationship classes declare a cardinality and can restrict which entity class is allowed on each side. They don't support inheritance. Read one back with [Get relationship class by name and version](https://www.pubnub.com/docs/admin-api/get-relationship-class-by-name-and-version.md).

### Global classes and SubKey classes

* **Global classes** (`entityClassLevel: Global`) are seeded by PubNub and shared. The built-in `User`, `Channel`, and `Membership` classes are Global and can't be modified.
* **SubKey classes** (`entityClassLevel: SubKey`), also called keyset-level classes, are the ones you define on your own keyset.

|  | Global (PubNub-owned) | SubKey (keyset-level, yours) |
| --- | --- | --- |
| **Entity class** | `User`, `Channel` | Your custom classes |
| **Relationship class** | `Membership` | Your custom classes |

Classes are managed with the Admin API. Entity classes have their own endpoints: [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). Relationship classes have the same set: [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).

## Built-in classes

`User` and `Channel` are Global entity classes, and `Membership` is a Global relationship class linking a channel to a user. When you create a user, a channel, or a membership, the API sets the class for you. Refer to [Built-in types](https://www.pubnub.com/docs/data-storage/structured-data/built-in-types.md) for the operational details.

## Payloads

The payload is the value of the `payload` field on an entity or relationship. It is a free-form, arbitrarily nested JSON object holding your application data, stored as you send it. It must be a JSON object: an array or a scalar is rejected.

What is enforced comes from the class's property declarations. A value at a declared property path must be a scalar of the declared type, and a non-nullable property must be present. Violations return a `400`. Fields you haven't declared are stored as-is and aren't checked.

Individual payload fields are addressable by JSON Pointer (RFC 6901). A pointer starts at the whole object, where `payload` sits alongside the system fields, so the price on a product entity is `/payload/price`. This same coordinate space is used for partial updates and for declaring class properties. Refer to [Schemas](https://www.pubnub.com/docs/data-storage/structured-data/schemas.md) and [Data operations](https://www.pubnub.com/docs/data-storage/structured-data/data-operations.md).

:::tip Declare the fields you filter on
Only payload fields declared as class properties with a filtering mode other than `none` are searchable. If you plan to filter or sort on a field, declare it as a property on the class first. Refer to [Schemas](https://www.pubnub.com/docs/data-storage/structured-data/schemas.md).
:::

Last updated at: 2026-09-30T07:20:08.000Z
