---
source_url: https://www.pubnub.com/docs/data-storage/metadata/overview
title: App Context
updated_at: 2026-09-30T07:20:08.000Z
---

# App Context

## 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.

:::note Starting a new app? Use DataSync
[DataSync](https://www.pubnub.com/docs/data-storage/structured-data/overview.md) is the successor to App Context. It does everything App Context does for users, channels, and memberships, and adds typed schemas, partial updates with ETags, field-level access control, and per-class expiry. Refer to [How DataSync compares to App Context](https://www.pubnub.com/docs/data-storage/structured-data/overview.md) for a feature-by-feature comparison.
DataSync is currently available to new accounts and to accounts that are not actively using App Context. If your keysets already use App Context, keep using it for now. App Context remains fully supported and these pages stay accurate.
:::

App Context stores structured metadata about the [users](https://www.pubnub.com/docs/architecture/core-concepts.md#user-id), [channels](https://www.pubnub.com/docs/architecture/core-concepts.md#channel), and [memberships](https://www.pubnub.com/docs/architecture/core-concepts.md#membership) in your application, so you don't run a separate database for profiles, channel descriptions, or channel rosters. You read and write it from an SDK or the REST API directly. No add-on to enable. This page explains:

* the three entities App Context stores and what each one holds
* the two directions from which you can read and write a membership
* how App Context notifies your application in real time when a record changes
* how to filter App Context data, and how to turn the feature on

One distinction matters throughout: App Context is stored, structured state, not a [message](https://www.pubnub.com/docs/architecture/core-concepts.md#message) and not [Presence](https://www.pubnub.com/docs/presence/overview.md) state. App Context data persists on its own, independent of any single publish or connection, until you change or remove it.

## What App Context stores

App Context has three entity types.

| Entity | Identified by | Built-in fields | Answers |
| --- | --- | --- | --- |
| User metadata | [User ID](https://www.pubnub.com/docs/architecture/core-concepts.md#user-id) | `name`, `email`, `externalId`, `profileUrl`, `status`, `type` | Who is this user? |
| Channel metadata | [Channel](https://www.pubnub.com/docs/architecture/core-concepts.md#channel) name | `name`, `description`, `status`, `type` | What is this channel for? |
| Membership | A User ID and a channel name together | `status`, `type`, a last-read timetoken | Which channels does this user belong to? |

Every entity also accepts a `custom` object where you add whatever else your application needs, such as a nickname, an avatar color, or a support-tier flag. Avoid putting personally identifiable information such as an email address or an IP address in a `custom` field if you plan to map that field into [Illuminate](https://www.pubnub.com/docs/analytics/decisions/overview.md) for analytics, since Illuminate reads and stores whatever you map into it.

:::note User ID / UUID
User ID is also referred to as **UUID/uuid** in some APIs and server responses but **holds the value** of the **userId** parameter you [set during initialization](https://www.pubnub.com/docs/architecture/core-concepts.md).
:::

## Turn on App Context and configure it

App Context is a keyset-level feature. Turn it on from your keyset's overview page, alongside the other add-ons described in [Set up your account](https://www.pubnub.com/docs/architecture/authentication/set-up-your-account.md#enable-the-features-your-app-calls). Once it's on, a handful of options in the Admin Portal decide what App Context stores and shares:

| Option | What it controls |
| --- | --- |
| **Bucket Region** | Where PubNub stores your App Context data. Choose a region close to your users to reduce latency. You can't change it after you save it. |
| **User Metadata Events**, **Channel Metadata Events**, **Membership Events** | Whether a set or delete on that entity type produces the matching [real-time event](#real-time-updates-when-metadata-changes). Each toggles independently. |
| **Disallow Get All Channel Metadata**, **Disallow Get All User Metadata** | Whether a valid [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md) token can call the "get all" operation for that entity type without that operation being listed in the token's own permissions. Leave unchecked to allow it by default. |
| **Enforce referential integrity for memberships** | Whether a membership requires its user and channel to already exist, and whether deleting either cascades to delete the membership. See [Membership connects users and channels](#membership-connects-users-and-channels). |

## Set, read, and remove metadata

Every App Context entity supports the same four operations:

* set (which also creates the record on first call)
* get one
* get all
* remove

The following sets a `name` and a `custom` field on one user's metadata record, taken from each SDK's own reference sample. Pick your language above:

### JavaScript

```javascript
// Using UUID from the config  - default when uuid is not passed in the method
try {
  const response = await pubnub.objects.setUUIDMetadata({
    data: {
      name: 'John Doe',
    },
  });
  console.log('setUUIDMetadata response:', response);
} catch (error) {
  console.error(`Set UUID metadata error: ${error}`);
}

// Using the passed in UUID
try {
  const response = await pubnub.objects.setUUIDMetadata({
    uuid: 'myUuid',
    data: {
      email: 'john.doe@example.com',
    },
  });
  console.log('setUUIDMetadata response:', response);
} catch (error) {
  console.error(`Set UUID metadata error: ${error}`);
}
```

### Java

```java
PNSetUUIDMetadataResult pnSetUUIDMetadataResult = pubNub.setUUIDMetadata()
        .name("Foo")
        .profileUrl("http://example.com")
        .email("foo@example.com")
        .includeCustom(true)
        .sync();
```

### Kotlin

```kotlin
pubnub.setUUIDMetadata()
    .async { result ->
        result.onFailure { exception ->
            // Handle error
        }.onSuccess { value ->
            // Handle successful method result
        }
    }
```

### C#

```csharp
using PubnubApi;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = new Pubnub(pnConfiguration);
        
// Set Metadata for UUID set in the pubnub instance
PNResult<PNSetUuidMetadataResult> setUuidMetadataResponse = await pubnub.SetUuidMetadata()
    .Uuid(config.Uuid)
    .Name("John Doe")
    .Email("john.doe@user.com")
    .ExecuteAsync();
PNSetUuidMetadataResult setUuidMetadataResult = setUuidMetadataResponse.Result;
PNStatus status = setUuidMetadataResponse.Status;
```

### Go

```go
// Replace with your package name (usually "main")
package pubnub_samples_test

import (
	"fmt"
	"time"

	pubnub "github.com/pubnub/go/v10"
)

// Example_setUUIDMetadata demonstrates setting user metadata (UUID metadata)
func Example_setUUIDMetadata() {
	config := pubnub.NewConfigWithUserId(pubnub.UserId("demo-user"))
	config.SubscribeKey = "demo" // Replace with your subscribe key
	config.PublishKey = "demo"   // Replace with your publish key

	pn := pubnub.NewPubNub(config)

	// Set user metadata with profile information
	response, status, err := pn.SetUUIDMetadata().
		UUID("user-123").                                // User ID
		Name("John Doe").                                // Display name
		Email("john.doe@example.com").                   // Email address
		ProfileURL("https://example.com/profiles/john"). // Profile URL
		ExternalID("ext-123").                           // External system ID
		Custom(map[string]interface{}{                   // Custom metadata
			"role":     "admin",
			"language": "en",
		}).
		Execute()

	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	if status.StatusCode == 200 {
		fmt.Printf("User metadata set for UUID: %s\n", response.Data.ID)
	}

	// Output:
	// User metadata set for UUID: user-123
}
```

### Swift

```swift
// Set user metadata for a specific identifier
let userMetadataToSet = PubNubUserMetadataBase(
  metadataId: "some-id",
  name: "Some User",
  custom: ["department": "Engineering"]
)

pubnub.setUserMetadata(userMetadataToSet) { result in
  switch result {
  case let .success(userMetadata):
    print("The metadata for `\(userMetadata.metadataId)`: \(userMetadata)")
  case let .failure(error):
    print("Create request failed with error: \(error.localizedDescription)")
  }
}
```

### PHP

```php
foreach ($sampleUsers as $user) {
    $setUserMetadataResult = $pubnub->setUuidMetadata()
        ->uuid($user['id'])
        ->name($user['name'])
        ->email($user['email'])
        ->externalId($user['externalId'])
        ->profileUrl($user['profileUrl'])
        ->custom($user['custom'])
        ->sync();
    assert($setUserMetadataResult->getId());
    assert($setUserMetadataResult->getName() === $user['name']);
    assert($setUserMetadataResult->getEmail() === $user['email']);
    assert($setUserMetadataResult->getExternalId() === $user['externalId']);
    assert($setUserMetadataResult->getProfileUrl() === $user['profileUrl']);
    assert(json_encode($setUserMetadataResult->getCustom()) === json_encode($user['custom']));
}
```

### Unity

```csharp
using PubnubApi;
using PubnubApi.Unity;

// Configuration
PNConfiguration pnConfiguration = new PNConfiguration(new UserId("myUniqueUserId"))
{
    SubscribeKey = "demo",
    PublishKey = "demo",
    Secure = true
};

// Initialize PubNub
Pubnub pubnub = PubnubUnityUtils.NewUnityPubnub(pnConfiguration);

// If you're using Unity Editor setup you can get the Pubnub instance from PNManagerBehaviour
// For more details, see https://www.pubnub.com/docs/sdks/unity#configure-pubnub
/*
[SerializeField] private PNManagerBehaviour pubnubManager;
Pubnub pubnub = pubnubManager.pubnub;
*/

// Set Metadata for UUID set in the pubnub instance
PNResult<PNSetUuidMetadataResult> setUuidMetadataResponse = await pubnub.SetUuidMetadata()
    .Uuid(config.Uuid)
    .Name("John Doe")
    .Email("john.doe@user.com")
    .ExecuteAsync();
PNSetUuidMetadataResult setUuidMetadataResult = setUuidMetadataResponse.Result;
PNStatus status = setUuidMetadataResponse.Status;
```

Check the API reference for your platform in [Available SDKs](https://www.pubnub.com/docs/sdks.md) documentation.

Every entity type follows the same shape: swap the entity (user, channel, or membership) and the operation (set, get one, get all, or remove) for the equivalent method in your SDK. Every SDK groups these under a dedicated "App Context" or "Objects" section. For the paginated list calls, refer to [Get metadata for all users](https://www.pubnub.com/docs/data-storage/metadata/get-metadata-for-all-users.md) and [Get metadata for all channels](https://www.pubnub.com/docs/data-storage/metadata/get-metadata-for-all-channels.md). For how to receive the event this produces, refer to [Real-time updates when metadata changes](#real-time-updates-when-metadata-changes).

Beyond an SDK, you can manage the same data through the [REST API](https://www.pubnub.com/docs/sdks/rest-api.md) for server-to-server calls, or through BizOps Workspace in the [Admin Portal](https://admin.pubnub.com/) for a no-code UI. [User Management](https://www.pubnub.com/docs/data-storage/metadata/manage-user-metadata.md) and [Channel Management](https://www.pubnub.com/docs/data-storage/metadata/manage-channel-metadata.md) create, edit, and delete the same user, channel, and membership records that the SDK and REST calls do. A record you create in one place is visible and editable in the others.

## Membership connects users and channels

A [membership](https://www.pubnub.com/docs/architecture/core-concepts.md#membership) is the persistent record that a User ID belongs to a channel. Adding a membership is not the same as [subscribing](https://www.pubnub.com/docs/pub-sub/subscribe/overview.md). A membership is a stored relationship your application queries later, and it doesn't by itself deliver any messages. For the full comparison between a stored membership and live [Presence](https://www.pubnub.com/docs/presence/overview.md), refer to [Core concepts](https://www.pubnub.com/docs/architecture/core-concepts.md#membership).

App Context exposes that one relationship from two directions, and the direction you pick matches how your application already has the data on hand:

* **Memberships operations start from a user.** They act on the channels it belongs to, through `getMemberships`, `setMemberships`, and `removeMemberships`. Use this direction to show one user's channel list or to add a user to a handful of channels.
* **Members operations start from a channel.** They act on the users that belong to it, through `getChannelMembers`, `setChannelMembers`, and `removeChannelMembers`. Use this direction to show a channel's roster, or to add many users to one channel at once, since setting members is the more efficient path for that kind of bulk write.

Both directions create or remove the same underlying membership record, so a membership set through one direction is immediately visible through the other. For the maximum number of memberships per user and members per channel, refer to [What App Context stores](#what-app-context-stores).

By default, App Context lets you create a membership for a User ID or channel that doesn't have its own metadata record yet. Deleting a user or channel's metadata also leaves any memberships pointing at it in place. Turning on **Enforce referential integrity for memberships** in the Admin Portal reverses both behaviors. A membership then requires the user and channel to already exist as their own metadata records, and deleting either one also cascades to delete their memberships.

## Real-time updates when metadata changes

App Context publishes an event every time a set or delete operation succeeds, so a client stays in sync without polling. This event travels through the same subscription and [event listener](https://www.pubnub.com/docs/pub-sub/subscribe/event-listeners.md) infrastructure as messages and presence, arriving through a dedicated `onObjects` handler. Its internal [message type](https://www.pubnub.com/docs/pub-sub/overview.md#message-types-categorize-traffic-on-a-shared-channel) is `2`. For the full payload shape, why it only ever reports `set` or `delete`, and how it relates to Events & Actions, refer to [App Context events](https://www.pubnub.com/docs/data-storage/metadata/events.md).

Where the event is delivered depends on which entity changed:

* A **channel metadata** event publishes on the channel itself, so anyone already subscribed to that channel receives it.
* A **user metadata** event publishes on a channel named after that User ID, so a client must subscribe to that User-ID-named channel to receive changes to a user it's watching.
* A **membership** event publishes to both: the affected User ID's own channel, and every channel the membership names.

Receiving any of these requires App Context enabled on your keyset, and the matching **User Metadata Events**, **Channel Metadata Events**, or **Membership Events** toggle turned on in the Admin Portal. You can also process these events server-side without a subscribed client, using [Functions](https://www.pubnub.com/docs/message-processing/serverless/overview.md) or [Events & Actions](https://www.pubnub.com/docs/integrations/event-forwarding/overview.md).

## Filter App Context data

The "get all" calls, along with `getMemberships` and `getChannelMembers`, accept a `filter` expression. That way, you retrieve only matching records instead of paging through everything and filtering client-side. The language supports comparison operators (`==`, `!=`, `<`, `>`, `<=`, `>=`), logical operators (`&&`, `||`), and SQL-like `LIKE` pattern matching with `*` as a wildcard.

Which fields you can filter on depends on which side of the membership relationship you're querying: a memberships call accepts `channel.*` fields, and a members call accepts `uuid.*` fields. The two are not interchangeable, so a filter written for one rejects the other's fields.

For applications with many users, channels, or memberships, filter on exact ID equality, such as `id == "channel-123"`, rather than on `LIKE` pattern matching or a `custom` field. An exact-match filter on `id` uses a database index, while a pattern match or a `custom` field lookup scans every record. This same rule applies to the quick search box versus the **Filters** button in BizOps Workspace.

Refer to [App Context filtering](https://www.pubnub.com/docs/data-storage/metadata/filtering.md) for the complete field list, operators, and syntax.

## App Context and access control

When [Access Manager](https://www.pubnub.com/docs/security/access-control/overview.md) is enabled on your keyset, App Context operations are gated by [token](https://www.pubnub.com/docs/architecture/core-concepts.md#token) permissions on two resource types. User metadata operations (`get`, `update`, `delete`) require a permission grant on that User ID, and channel metadata operations require the same three permissions on the channel. A channel's members and memberships add two more permission bits, `manage` and `join`, and setting or removing a membership additionally needs a grant on both the channel and the User ID in the same token. Refer to [Set, get, and remove members](https://www.pubnub.com/docs/data-storage/metadata/manage-members.md#add-members-to-a-channel) and [Set, get, and remove memberships](https://www.pubnub.com/docs/data-storage/metadata/manage-memberships.md#add-a-user-to-channels) for the exact permission each operation needs.

A "get all" call is a special case. An App Context "get all" call needs no per-resource permission grant unless you've turned on the matching **Disallow Get All** option, in which case the token needs an explicit grant for that operation.

## Next steps

* [Set, get, and remove user metadata](https://www.pubnub.com/docs/data-storage/metadata/manage-user-metadata.md). Store and read the current user's profile.
* [Set, get, and remove channel metadata](https://www.pubnub.com/docs/data-storage/metadata/manage-channel-metadata.md). Give a channel a name, description, and custom fields.
* [Set, get, and remove memberships](https://www.pubnub.com/docs/data-storage/metadata/manage-memberships.md). Manage a user's channel list.
* [Set, get, and remove members](https://www.pubnub.com/docs/data-storage/metadata/manage-members.md). Manage a channel's user roster.
* [App Context filtering](https://www.pubnub.com/docs/data-storage/metadata/filtering.md). The full filter field list, operators, and syntax.
* [App Context events](https://www.pubnub.com/docs/data-storage/metadata/events.md). The complete `onObjects` payload and server-side event types.
* [App Context API limits](https://www.pubnub.com/docs/data-storage/metadata/api-limits.md). Field lengths and record-count recommendations.
* [Core concepts](https://www.pubnub.com/docs/architecture/core-concepts.md). User IDs, channels, memberships, and tokens.
* [Presence](https://www.pubnub.com/docs/presence/overview.md). The live counterpart to a stored membership.

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