DataSync event format reference
DataSync events are PubNub messages with message type 5. This distinguishes them from ordinary messages. Each event carries a versioned envelope with a metadata section and a data section.
Envelope schema
{
"version": "1.0",
"metadata": {
"event": "create" | "update" | "delete",
"source": "data-sync",
"type": "user" | "channel" | "membership" | "entity" | "relationship",
"className": "Product",
"classLevel": "Global" | "SubKey",
"classVersion": 1
},
"data": { ... }
}
Metadata fields
| Field | Type | Description |
|---|---|---|
version | string | Envelope version. Always 1.0 for DataSync events |
metadata.event | string | create, update, or delete |
metadata.source | string | Always data-sync |
metadata.type | string | The object kind: user, channel, membership, entity, or relationship |
metadata.className | string | The class name, for example Product or User. Read together with classLevel to distinguish a built-in class from a same-named keyset-level class |
metadata.classLevel | string | Global (PubNub-owned) or SubKey (keyset-level) |
metadata.classVersion | number | The class version the object pins to |
metadata.type is derived from the Global class the class descends from, not from its name. A keyset-level class extending the built-in User reports type: "user" alongside its own className and classLevel: "SubKey". A keyset-level class merely named User that extends nothing reports type: "entity".
Data field shapes
For create and update events, data carries the object's current state, scoped to the projection for the channel it's delivered on:
| Field | Present on |
|---|---|
id | All object kinds |
createdAt | All object kinds |
updatedAt | All object kinds |
expiresAt | All object kinds (omitted if not set) |
eTag | All object kinds |
status | All object kinds (omitted if not set or outside projection) |
payload | All object kinds (omitted if empty or outside projection) |
entityAId | Relationships only |
entityBId | Relationships only |
channelId | Memberships only |
userId | Memberships only |
For delete events, data is exactly { id, deletedAt } for every object kind alike. No payload or status is included.
Fields that have no value are omitted from data rather than sent as null. Read every field except id defensively.
Example events
- Update event
- Delete event
- Membership create event
{
"version": "1.0",
"metadata": {
"event": "update",
"source": "data-sync",
"type": "entity",
"className": "Product",
"classLevel": "SubKey",
"classVersion": 1
},
"data": {
"id": "product-sneaker-42",
"status": "active",
"eTag": "a1b2c3",
"createdAt": "2026-06-01T10:00:00Z",
show all 20 lines{
"version": "1.0",
"metadata": {
"event": "delete",
"source": "data-sync",
"type": "entity",
"className": "Product",
"classLevel": "SubKey",
"classVersion": 1
},
"data": {
"id": "product-sneaker-42",
"deletedAt": "2026-07-03T09:20:00Z"
}
}
A membership event carries type: "membership" and names its sides channelId and userId:
{
"version": "1.0",
"metadata": {
"event": "create",
"source": "data-sync",
"type": "membership",
"className": "Membership",
"classLevel": "Global",
"classVersion": 1
},
"data": {
"id": "membership-alice-sale",
"status": "active",
"eTag": "d4e5f6",
"createdAt": "2026-07-03T09:15:00Z",
show all 22 linesA membership event never carries entityAId or entityBId. A relationship of your own carries those two fields instead and never channelId or userId.
How the event is received per SDK
- JavaScript
- C#
1subscription.onDataSync = (event) => {
2 const change = event.message
3 console.log(change.event, change.className, change.data.payload?.price)
4}
The JavaScript SDK flattens the wire envelope: metadata.event arrives as event.message.event, metadata.className as event.message.className, and so on. event.message.data is the raw data object.
1var listener = new SubscribeCallbackExt(
2 (Pubnub pn, PNDataSyncEventResult dataSyncEvent) =>
3 {
4 Console.WriteLine(dataSyncEvent.Event);
5 Console.WriteLine(dataSyncEvent.ClassName);
6 Console.WriteLine(dataSyncEvent.EntityData?.Payload["price"]);
7 },
8 (Pubnub pn, PNStatus status) => { /* handle status */ });
The C# SDK exposes the same fields as properties on PNDataSyncEventResult: Event, ClassName, ClassLevel, ClassVersion, and Source, with the object state on EntityData (or RelationshipData for memberships and relationships).
Using the REST subscribe endpoint directly, the raw response carrying an event looks like:
1curl -N 'https://ps.pndsn.com/v2/subscribe/{subKey}/product-sneaker-42/0?tt=0&auth=<token>'
[
[
{
"version": "1.0",
"metadata": {
"event": "update",
"source": "data-sync",
"type": "entity",
"className": "Product",
"classLevel": "SubKey",
"classVersion": 1
},
"data": {
"id": "product-sneaker-42",
"status": "active",
show all 25 linesRefer to Add DataSync listener (JavaScript) and Add DataSync listener (C#) for the full SDK field reference.
Comparison with App Context events
DataSync events use a different format from App Context events. Existing App Context event consumers are unaffected.
| Aspect | App Context | DataSync |
|---|---|---|
| Message type | 2 | 5 |
source | objects | data-sync |
| Verbs | set, delete | create, update, delete |
| Envelope version | 2.0 | 1.0 |