App Context API for C SDK
App Context provides serverless storage for metadata about your application's users and channels, plus the membership relationships between them: which channels a user belongs to, and which users belong to a channel. All 12 App Context functions live on a single header, #include <pubnub/features/app_context.h>, gated by one compile-time flag, PUBNUB_ENABLE_APP_CONTEXT. Unlike Entities, no App Context function takes an entity handle. Every function takes a context plus plain string identifiers instead.
The two result structs that describe a relationship each embed the other side's metadata struct directly: a pubnub_membership_t (a user's view of a channel) embeds a full pubnub_channel_metadata_t in its channel field, and a pubnub_member_t (a channel's view of a user) embeds a full pubnub_uuid_metadata_t in its uuid field. Keep this in mind when reading the field tables below. The Memberships and Channel members sections do not repeat the metadata field tables already given under UUID metadata and Channel metadata.
custom_value ownership TRANSFERS to the SDK: the opposite of publish, signal, and presence
Every custom_value field in this header (pubnub_set_uuid_metadata_opts_t.custom_value, pubnub_set_channel_metadata_opts_t.custom_value, and custom_value inside pubnub_membership_input_t/pubnub_member_input_t) works differently from every other JSON-tree field in this SDK. The SDK consumes and frees the tree during the call, on both success and failure. Do not access or free it afterward. Doing so is a double free, and no diagnostic catches it for you.
This is the opposite of publish.h's message_value, signal.h's message_value, and presence.h's state_value, which are all borrowed: the caller retains ownership and must destroy them with pubnub_json_destroy() after the call returns. If you learned the borrowed pattern from Publish & Subscribe or Presence and apply it here, you will double-free the tree. App Context's custom_value is the sole outlier across all four headers.
Include flags
Every App Context list, get, and set-with-page operation accepts an include field of type uint32_t, a bitwise OR of the flags below. Pass 0 to get whichever fields the server returns by default. Combining flags costs nothing extra: opts.include = PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM | PUBNUB_APP_CONTEXT_INCLUDE_TOTAL_COUNT;.
| Flag | What it adds | Applies to |
|---|---|---|
PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM | The entity's own custom JSON object | Any single UUID or channel metadata result |
PUBNUB_APP_CONTEXT_INCLUDE_TYPE | The entity's own type label | Any single UUID or channel metadata result |
PUBNUB_APP_CONTEXT_INCLUDE_STATUS | The entity's own status label | Any single UUID or channel metadata result |
PUBNUB_APP_CONTEXT_INCLUDE_TOTAL_COUNT | Populates pubnub_app_context_page_t.total_count; otherwise it stays 0 | Every paginated list/set-with-page operation |
PUBNUB_APP_CONTEXT_INCLUDE_UUID | Populates the embedded pubnub_uuid_metadata_t inside a pubnub_member_t result | pubnub_get_channel_members, pubnub_set_channel_members |
PUBNUB_APP_CONTEXT_INCLUDE_UUID_CUSTOM | Adds custom to that embedded UUID metadata | Same |
PUBNUB_APP_CONTEXT_INCLUDE_UUID_TYPE | Adds type to that embedded UUID metadata | Same |
PUBNUB_APP_CONTEXT_INCLUDE_UUID_STATUS | Adds status to that embedded UUID metadata | Same |
PUBNUB_APP_CONTEXT_INCLUDE_CHANNEL | Populates the embedded pubnub_channel_metadata_t inside a pubnub_membership_t result | pubnub_get_memberships, pubnub_set_memberships |
PUBNUB_APP_CONTEXT_INCLUDE_CHANNEL_CUSTOM | Adds custom to that embedded channel metadata | Same |
PUBNUB_APP_CONTEXT_INCLUDE_CHANNEL_TYPE | Adds type to that embedded channel metadata | Same |
PUBNUB_APP_CONTEXT_INCLUDE_CHANNEL_STATUS | Adds status to that embedded channel metadata | Same |
Without the base PUBNUB_APP_CONTEXT_INCLUDE_UUID/_CHANNEL flag set, whether the corresponding _UUID_*/_CHANNEL_* sub-flags have any independent effect is not documented. Do not rely on a sub-flag alone. Always set the base flag first when you need the embedded struct populated.
pubnub_membership_t.channel and pubnub_member_t.uuid are documented as partial unless the matching include flag is set. The header does not enumerate a specific default subset of fields for the unflagged case, so do not assume which fields (if any) come back.
Pagination
Every list operation (pubnub_get_all_uuid_metadata, pubnub_get_all_channel_metadata, pubnub_get_memberships, pubnub_get_channel_members) and every set-with-page operation (pubnub_set_memberships, pubnub_set_channel_members) returns a pubnub_app_context_page_t:
typedef struct pubnub_app_context_page {
uint32_t total_count;
pubnub_string_view_t next;
pubnub_string_view_t prev;
uint32_t count;
} pubnub_app_context_page_t;
This is a real cursor, unlike Message Persistence history, which has none.
countis the number of items actually returned in this response. Read it, not thelimityou requested, to know how many results to iterate.total_countis only populated whenPUBNUB_APP_CONTEXT_INCLUDE_TOTAL_COUNTis set. Otherwise it stays0.- Advancing forward: pass the previous response's
nextcursor as the next call'sstartfield. - Going backward: pass the previous response's
prevcursor as the next call'sendfield. next.len == 0means there are no more pages.- Page size is
opts.limit(uint32_t).0lets the server pick its own default ("typically 100" per the header's doc comments). No client-side maximum is enforced. Whatever you set inlimitpasses straight through to the request unclamped. - Sorting:
opts.sort, a comma-separatedfield:directionstring passed verbatim (for example,"name:asc,updated:desc"or, for memberships,"channel.name:asc"). No enumerated list of valid field names exists in the header. Treat it as an opaque server-side contract. - Filtering:
opts.filter, passed verbatim as the server-side filter expression (for example,"status == \"active\""). No filter-expression grammar is documented in this SDK's source.
Every paginated _result() accessor returns a zero-initialized pubnub_app_context_page_t (count == 0) if the future is not ready or invalid. Check pubnub_future_status() to tell an empty result apart from an error. Every _at() accessor (for example, pubnub_get_all_uuid_metadata_result_uuid_at()) returns a zero-initialized struct, silently, for an out-of-range index. There is no bounds-checking error code, so always compare index < page.count yourself before calling one.
All string views returned by any App Context result, including every field inside pubnub_app_context_page_t, alias the parsed response body and stay valid only until pubnub_future_release() is called on the owning future. Read everything you need before releasing it.
Custom metadata
Every set-style App Context operation accepts custom data two mutually exclusive ways:
- A raw JSON string.
custom(const char*) pluscustom_len(size_t,0callsstrlen()). Must be a valid JSON object. Borrowed. The SDK does not take ownership, so there is no cleanup obligation beyond the string's normal lifetime. - A pre-built JSON value tree.
custom_value(struct pubnub_json_value*), built with thepubnub/json.h/pubnub/json_macros.hhelpers. Ownership transfers to the SDK. See the warning at the top of this page: the SDK consumes and frees this tree during the call, on both success and failure. Never callpubnub_json_destroy()on it afterward.
Setting both custom and custom_value non-NULL on the same struct (or the same array element, for memberships/members) fails immediately with PUBNUB_ERR_INVALID_ARGUMENT. This is readable from pubnub_future_status() without polling, since the check runs before any network I/O. Setting neither is not an error: custom is simply omitted from the request, preserving whatever is already stored.
Unsupported partial updates of custom metadata
The value of the custom metadata parameter sent in this method always overwrites the value stored on PubNub servers. If you want to add new custom data to an existing one, you must:
- Get the existing metadata and store it locally.
- Append the new custom metadata to the existing one.
- Set the entire updated custom object.
Reading custom data back
Every custom field in an App Context result (pubnub_uuid_metadata_t.custom, pubnub_channel_metadata_t.custom, and the custom field inside pubnub_membership_t/pubnub_member_t) is const struct pubnub_json_value*, an opaque JSON-tree node, not a string. Read it with the same pattern already established for subscribe payloads in Receiving messages: get the context's serialization provider with pubnub_serialization(ctx) (see Accessing the serialization provider), NULL-check the specific vtable accessor you need, then read.
static void print_custom_role(pubnub_context_t* ctx, const pubnub_json_value_t* custom)
{
if (NULL == custom) {
return;
}
pubnub_serialization_provider_t* serial = pubnub_serialization(ctx);
if (NULL == serial || NULL == serial->object_get || NULL == serial->value_as_string) {
return;
}
const pubnub_json_value_t* role = serial->object_get(custom, "role", 4);
if (NULL == role) {
return;
}
show all 22 linesFor a quick debug print of an entire node instead of walking individual keys, use the gated helper pubnub_json_to_debug_string(serial, node, buf, sizeof(buf)) (PUBNUB_CFG_JSON_HELPERS), shown in the Set UUID metadata sample below.
UUID metadata
Get all UUID metadata
Returns a paginated list of UUID metadata objects.
Method(s)
pubnub_future_t pubnub_get_all_uuid_metadata(
pubnub_context_t* ctx, const pubnub_get_all_uuid_metadata_opts_t* opts);
opts may be NULL; the SDK substitutes an all-defaults struct. Zero-initialize an explicit struct with PUBNUB_GET_ALL_UUID_METADATA_OPTS_INIT (expands to {0}).
| Parameter | Description |
|---|---|
includeType: uint32_tDefault: 0 | See Include flags. |
limitType: uint32_tDefault: 0 (server default) | Items per page. See Pagination. |
startType: const char*Default: NULL | Next-page cursor, borrowed, NUL-terminated. |
endType: const char*Default: NULL | Previous-page cursor, borrowed, NUL-terminated. |
filterType: const char*Default: NULL | Server-side filter expression. |
sortType: const char*Default: NULL | Comma-separated field:direction pairs. |
timeout_msType: uint32_tDefault: 0 (context default) | Per-request override. |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Types —
pubnub_get_all_uuid_metadata_opts_t,pubnub_app_context_page_t,pubnub_uuid_metadata_t - Prerequisite — an initialized context
- Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Ownership / lifetime — all string fields borrowed; no JSON-tree fields on this struct
- Blocking — never blocks; returns a
pubnub_future_timmediately
Sample code
Adapted from examples/app_context/get_all_uuid_metadata.c.
#include <pubnub/client.h>
#include <pubnub/features/app_context.h>
#include <pubnub/future.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.publish_key = "demo";
cfg.user_id = "my_unique_user_id";
pubnub_context_t* ctx = pubnub_create(&cfg);
show all 45 linesReturns
pubnub_app_context_page_t pubnub_get_all_uuid_metadata_result(pubnub_future_t future);
pubnub_uuid_metadata_t pubnub_get_all_uuid_metadata_result_uuid_at(pubnub_future_t future, size_t index);
Call both accessors only after the future completes with PUBNUB_OK, and before releasing the future. See Pagination for the bounds and lifetime rules that apply to every list result on this page.
typedef struct pubnub_uuid_metadata {
pubnub_string_view_t id;
pubnub_string_view_t name;
pubnub_string_view_t external_id;
pubnub_string_view_t profile_url;
pubnub_string_view_t email;
pubnub_string_view_t type;
pubnub_string_view_t status;
const struct pubnub_json_value* custom;
pubnub_string_view_t updated;
pubnub_string_view_t etag;
} pubnub_uuid_metadata_t;
Other examples
Walk multiple pages
static void walk_all_uuid_metadata(pubnub_context_t* ctx)
{
char next_buf[128] = { 0 };
pubnub_get_all_uuid_metadata_opts_t opts = PUBNUB_GET_ALL_UUID_METADATA_OPTS_INIT;
opts.include = PUBNUB_APP_CONTEXT_INCLUDE_TOTAL_COUNT;
opts.limit = 50;
for (;;) {
pubnub_future_t future = pubnub_get_all_uuid_metadata(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
if (PUBNUB_OK != result) {
pubnub_future_release(future);
break;
}
show all 37 linesGet UUID metadata
Returns metadata for a single UUID.
Method(s)
pubnub_future_t pubnub_get_uuid_metadata(
pubnub_context_t* ctx, const pubnub_get_uuid_metadata_opts_t* opts);
opts may be NULL. Zero-initialize an explicit struct with PUBNUB_GET_UUID_METADATA_OPTS_INIT. Unlike most App Context options macros, this one is not a flat {0}: it sets include to PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM by default.
| Parameter | Description |
|---|---|
uuidType: const char*Default: NULL = the context's own user_id | Borrowed, NUL-terminated. |
includeType: uint32_tDefault: PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM via PUBNUB_GET_UUID_METADATA_OPTS_INIT, 0 if you zero the struct by hand | See Include flags. |
timeout_msType: uint32_tDefault: 0 | Per-request override. |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Types —
pubnub_get_uuid_metadata_opts_t,pubnub_uuid_metadata_t - Prerequisite — an initialized context; if
uuidis omitted,user_idmust be set - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
Adapted from examples/app_context/get_uuid_metadata.c.
static void get_uuid_metadata_example(pubnub_context_t* ctx)
{
pubnub_get_uuid_metadata_opts_t opts = PUBNUB_GET_UUID_METADATA_OPTS_INIT;
opts.uuid = "my_unique_user_id";
pubnub_future_t future = pubnub_get_uuid_metadata(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
if (PUBNUB_OK == result) {
pubnub_uuid_metadata_t meta = pubnub_get_uuid_metadata_result(future);
printf("name: %.*s\n", (int)meta.name.len, meta.name.ptr);
}
else {
printf("get_uuid_metadata failed: %d\n", (int)result);
}
show all 18 linesReturns
pubnub_uuid_metadata_t pubnub_get_uuid_metadata_result(pubnub_future_t future);
Returns a zero-initialized struct if the future is not ready. Fields not covered by include are also zero-initialized.
Set UUID metadata
Creates or partially updates a UUID's metadata. opts is required. Passing NULL fails with PUBNUB_ERR_INVALID_ARGUMENT.
Method(s)
pubnub_future_t pubnub_set_uuid_metadata(
pubnub_context_t* ctx, const pubnub_set_uuid_metadata_opts_t* opts);
Zero-initialize with PUBNUB_SET_UUID_METADATA_OPTS_INIT. Like PUBNUB_GET_UUID_METADATA_OPTS_INIT, this defaults include to PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM rather than 0.
| Parameter | Ownership | Description |
|---|---|---|
uuidType: const char*Default: NULL = context's own user_id | Borrowed | |
nameType: const char*Default: NULL = unchanged | Borrowed | Partial PATCH. |
external_idType: const char*Default: NULL | Borrowed | |
profile_urlType: const char*Default: NULL | Borrowed | |
emailType: const char*Default: NULL | Borrowed | |
typeType: const char*Default: NULL | Borrowed | |
statusType: const char*Default: NULL | Borrowed | |
customType: const char*Default: NULL | Borrowed | Raw JSON string. See Custom metadata. |
custom_lenType: size_tDefault: 0 = strlen(custom) | — | |
custom_valueType: struct pubnub_json_value*Default: NULL | Transfers to the SDK | See the warning at the top of this page. Setting both custom and custom_value is PUBNUB_ERR_INVALID_ARGUMENT. |
includeType: uint32_tDefault: PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM via PUBNUB_SET_UUID_METADATA_OPTS_INIT, 0 if zeroed by hand | See Include flags. | |
if_matchType: const char*Default: NULL = unconditional | Borrowed, must stay valid until the function returns | Sent as an If-Match header. The server rejects the update if the current ETag does not match; no specific pubnub_res_t is documented for that case. |
timeout_msType: uint32_tDefault: 0 |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Types —
pubnub_set_uuid_metadata_opts_t,pubnub_uuid_metadata_t - Prerequisite — an initialized context;
optsnon-NULL - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Ownership / lifetime —
custom/other strings borrowed;custom_value, if set, is consumed and freed by the SDK during the call - Blocking — never blocks
Sample code
Adapted from examples/app_context/set_uuid_metadata.c. Uses the raw-string custom field, read back with pubnub_json_to_debug_string().
#include <pubnub/client.h>
#include <pubnub/features/app_context.h>
#include <pubnub/future.h>
#include <pubnub/json.h>
#include <pubnub/providers/serialization.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.publish_key = "demo";
cfg.user_id = "my_unique_user_id";
show all 48 linesReturns
pubnub_uuid_metadata_t pubnub_set_uuid_metadata_result(pubnub_future_t future);
Other examples
Build a custom_value JSON tree
This is exactly the case the ownership warning above exists for: notice there is no pubnub_json_destroy() call anywhere in this sample, on either the success or the failure path.
#include <pubnub/client.h>
#include <pubnub/features/app_context.h>
#include <pubnub/future.h>
#include <pubnub/json.h>
#include <pubnub/json_macros.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.publish_key = "demo";
cfg.user_id = "my_unique_user_id";
show all 49 linesConditional update with if_match
static void set_uuid_metadata_if_match(pubnub_context_t* ctx, const pubnub_uuid_metadata_t* current)
{
char etag_buf[128];
size_t len = current->etag.len < sizeof(etag_buf) ? current->etag.len : sizeof(etag_buf) - 1;
memcpy(etag_buf, current->etag.ptr, len);
etag_buf[len] = '\0';
pubnub_set_uuid_metadata_opts_t opts = PUBNUB_SET_UUID_METADATA_OPTS_INIT;
opts.status = "active";
opts.if_match = etag_buf;
pubnub_future_t future = pubnub_set_uuid_metadata(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
if (PUBNUB_OK != result) {
show all 22 linesError responses
- Setting both
customandcustom_valuefails immediately withPUBNUB_ERR_INVALID_ARGUMENT. - Passing
opts == NULLfails withPUBNUB_ERR_INVALID_ARGUMENT. - See Error handling for the universal
pubnub_res_tcatalog.
Remove UUID metadata
Method(s)
pubnub_future_t pubnub_remove_uuid_metadata(
pubnub_context_t* ctx, const pubnub_remove_uuid_metadata_opts_t* opts);
opts may be NULL. This removes the context's own user_id. Zero-initialize an explicit struct with PUBNUB_REMOVE_UUID_METADATA_OPTS_INIT (expands to {0}).
| Parameter | Description |
|---|---|
uuidType: const char*Default: NULL = context's own user_id | Borrowed. |
timeout_msType: uint32_tDefault: 0 |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Types —
pubnub_remove_uuid_metadata_opts_t - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
Adapted from examples/app_context/remove_uuid_metadata.c.
static void remove_uuid_metadata_example(pubnub_context_t* ctx)
{
pubnub_remove_uuid_metadata_opts_t opts = PUBNUB_REMOVE_UUID_METADATA_OPTS_INIT;
opts.uuid = "my_unique_user_id";
pubnub_future_t future = pubnub_remove_uuid_metadata(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
printf("remove_uuid_metadata: %s\n", (PUBNUB_OK == result) ? "ok" : "failed");
pubnub_future_release(future);
}
Returns
No result accessor exists for this operation. Success is determined solely from the pubnub_res_t returned by pubnub_future_status()/pubnub_await(): PUBNUB_OK on success, an error code otherwise.
Channel metadata
Get all channel metadata
Same shape as Get all UUID metadata, without a uuid field. It lists every channel.
Method(s)
pubnub_future_t pubnub_get_all_channel_metadata(
pubnub_context_t* ctx, const pubnub_get_all_channel_metadata_opts_t* opts);
opts may be NULL. Zero-initialize an explicit struct with PUBNUB_GET_ALL_CHANNEL_METADATA_OPTS_INIT (expands to {0}).
| Parameter | Description |
|---|---|
includeType: uint32_tDefault: 0 | See Include flags. |
limitType: uint32_tDefault: 0 (server default) | See Pagination. |
startType: const char*Default: NULL | Borrowed. |
endType: const char*Default: NULL | Borrowed. |
filterType: const char*Default: NULL | |
sortType: const char*Default: NULL | |
timeout_msType: uint32_tDefault: 0 |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Types —
pubnub_get_all_channel_metadata_opts_t,pubnub_app_context_page_t,pubnub_channel_metadata_t - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
Adapted from examples/app_context/get_all_channel_metadata.c.
static void get_all_channel_metadata_example(pubnub_context_t* ctx)
{
pubnub_get_all_channel_metadata_opts_t opts = PUBNUB_GET_ALL_CHANNEL_METADATA_OPTS_INIT;
opts.include = PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM | PUBNUB_APP_CONTEXT_INCLUDE_TOTAL_COUNT;
opts.limit = 25;
pubnub_future_t future = pubnub_get_all_channel_metadata(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
if (PUBNUB_OK == result) {
pubnub_app_context_page_t page = pubnub_get_all_channel_metadata_result(future);
for (uint32_t i = 0; i < page.count; i++) {
pubnub_channel_metadata_t m = pubnub_get_all_channel_metadata_result_channel_at(future, i);
printf("channel: %.*s\n", (int)m.id.len, m.id.ptr);
}
show all 22 linesReturns
pubnub_app_context_page_t pubnub_get_all_channel_metadata_result(pubnub_future_t future);
pubnub_channel_metadata_t pubnub_get_all_channel_metadata_result_channel_at(pubnub_future_t future, size_t index);
typedef struct pubnub_channel_metadata {
pubnub_string_view_t id;
pubnub_string_view_t name;
pubnub_string_view_t description;
pubnub_string_view_t type;
pubnub_string_view_t status;
const struct pubnub_json_value* custom;
pubnub_string_view_t updated;
pubnub_string_view_t etag;
} pubnub_channel_metadata_t;
Get channel metadata
Method(s)
pubnub_future_t pubnub_get_channel_metadata(
pubnub_context_t* ctx, const pubnub_get_channel_metadata_opts_t* opts);
opts and opts->channel are both required. Either being absent fails with PUBNUB_ERR_INVALID_ARGUMENT. Zero-initialize with PUBNUB_GET_CHANNEL_METADATA_OPTS_INIT, which defaults include to PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM rather than 0.
| Parameter | Description |
|---|---|
channelType: const char*Default: — | Borrowed, NUL-terminated. |
includeType: uint32_tDefault: PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM via PUBNUB_GET_CHANNEL_METADATA_OPTS_INIT | |
timeout_msType: uint32_tDefault: 0 |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
Adapted from examples/app_context/get_channel_metadata.c.
static void get_channel_metadata_example(pubnub_context_t* ctx)
{
pubnub_get_channel_metadata_opts_t opts = PUBNUB_GET_CHANNEL_METADATA_OPTS_INIT;
opts.channel = "my_channel";
pubnub_future_t future = pubnub_get_channel_metadata(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
if (PUBNUB_OK == result) {
pubnub_channel_metadata_t meta = pubnub_get_channel_metadata_result(future);
printf("name: %.*s\n", (int)meta.name.len, meta.name.ptr);
}
else {
printf("get_channel_metadata failed: %d\n", (int)result);
}
show all 18 linesReturns
pubnub_channel_metadata_t pubnub_get_channel_metadata_result(pubnub_future_t future);
Error responses
opts == NULL or opts->channel == NULL fails with PUBNUB_ERR_INVALID_ARGUMENT.
Set channel metadata
opts and opts->channel are both required.
Method(s)
pubnub_future_t pubnub_set_channel_metadata(
pubnub_context_t* ctx, const pubnub_set_channel_metadata_opts_t* opts);
Zero-initialize with PUBNUB_SET_CHANNEL_METADATA_OPTS_INIT, which defaults include to PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM rather than 0.
| Parameter | Ownership | Description |
|---|---|---|
channelType: const char*Default: — | Borrowed | |
nameType: const char*Default: NULL | Borrowed | |
descriptionType: const char*Default: NULL | Borrowed | |
typeType: const char*Default: NULL | Borrowed | |
statusType: const char*Default: NULL | Borrowed | |
customType: const char*Default: NULL | Borrowed | See Custom metadata. |
custom_lenType: size_tDefault: 0 = strlen | — | |
custom_valueType: struct pubnub_json_value*Default: NULL | Transfers to the SDK | Same rule as Set UUID metadata. |
includeType: uint32_tDefault: PUBNUB_APP_CONTEXT_INCLUDE_CUSTOM via PUBNUB_SET_CHANNEL_METADATA_OPTS_INIT | ||
if_matchType: const char*Default: NULL | Borrowed, valid until return | Sent as If-Match. |
timeout_msType: uint32_tDefault: 0 |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Ownership / lifetime — same as Set UUID metadata: strings borrowed,
custom_valuetransfers - Blocking — never blocks
Sample code
Adapted from examples/app_context/set_channel_metadata.c.
static void set_channel_metadata_example(pubnub_context_t* ctx)
{
pubnub_set_channel_metadata_opts_t opts = PUBNUB_SET_CHANNEL_METADATA_OPTS_INIT;
opts.channel = "my_channel";
opts.name = "Support Channel";
opts.description = "Customer support";
opts.custom = "{\"topic\":\"support\"}";
pubnub_future_t future = pubnub_set_channel_metadata(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
if (PUBNUB_OK != result) {
printf("set_channel_metadata failed: %d\n", (int)result);
}
show all 17 linesReturns
pubnub_channel_metadata_t pubnub_set_channel_metadata_result(pubnub_future_t future);
Error responses
Same as Set UUID metadata: both custom and custom_value set, or a missing channel, both fail with PUBNUB_ERR_INVALID_ARGUMENT.
Remove channel metadata
Method(s)
pubnub_future_t pubnub_remove_channel_metadata(
pubnub_context_t* ctx, const pubnub_remove_channel_metadata_opts_t* opts);
opts and opts->channel are both required. Unlike UUID metadata, there is no context-level fallback for a channel. Zero-initialize an explicit struct with PUBNUB_REMOVE_CHANNEL_METADATA_OPTS_INIT (expands to {0}).
| Parameter | Description |
|---|---|
channelType: const char*Default: — | Borrowed. |
timeout_msType: uint32_tDefault: 0 |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
Adapted from examples/app_context/remove_channel_metadata.c.
static void remove_channel_metadata_example(pubnub_context_t* ctx)
{
pubnub_remove_channel_metadata_opts_t opts = PUBNUB_REMOVE_CHANNEL_METADATA_OPTS_INIT;
opts.channel = "my_channel";
pubnub_future_t future = pubnub_remove_channel_metadata(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
printf("remove_channel_metadata: %s\n", (PUBNUB_OK == result) ? "ok" : "failed");
pubnub_future_release(future);
}
Returns
No result accessor exists for this operation, the same pattern as Remove UUID metadata. Check the pubnub_res_t only.
Memberships
A membership is a UUID's view of its channel relationships: pubnub_membership_t.channel embeds the full pubnub_channel_metadata_t documented under Channel metadata.
typedef struct pubnub_membership {
pubnub_channel_metadata_t channel;
pubnub_string_view_t status;
pubnub_string_view_t type;
const struct pubnub_json_value* custom;
pubnub_string_view_t updated;
pubnub_string_view_t etag;
} pubnub_membership_t;
channel is populated only as far as the PUBNUB_APP_CONTEXT_INCLUDE_CHANNEL* flags request. See Include flags.
Get memberships
Lists the channels a UUID belongs to.
Method(s)
pubnub_future_t pubnub_get_memberships(
pubnub_context_t* ctx, const pubnub_get_memberships_opts_t* opts);
opts may be NULL. This queries the context's own user_id. Zero-initialize an explicit struct with PUBNUB_GET_MEMBERSHIPS_OPTS_INIT (expands to {0}).
| Parameter | Description |
|---|---|
uuidType: const char*Default: NULL = context's own user_id | Borrowed. |
includeType: uint32_tDefault: 0 | See Include flags. |
limitType: uint32_tDefault: 0 (server default) | See Pagination. |
startType: const char*Default: NULL | |
endType: const char*Default: NULL | |
filterType: const char*Default: NULL | |
sortType: const char*Default: NULL | For example, "channel.name:asc". |
timeout_msType: uint32_tDefault: 0 |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
Adapted from examples/app_context/get_memberships.c.
static void get_memberships_example(pubnub_context_t* ctx)
{
pubnub_get_memberships_opts_t opts = PUBNUB_GET_MEMBERSHIPS_OPTS_INIT;
opts.include = PUBNUB_APP_CONTEXT_INCLUDE_CHANNEL
| PUBNUB_APP_CONTEXT_INCLUDE_CHANNEL_CUSTOM
| PUBNUB_APP_CONTEXT_INCLUDE_TOTAL_COUNT;
opts.sort = "channel.name:asc";
pubnub_future_t future = pubnub_get_memberships(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
if (PUBNUB_OK == result) {
pubnub_app_context_page_t page = pubnub_get_memberships_result(future);
for (uint32_t i = 0; i < page.count; i++) {
pubnub_membership_t ms = pubnub_get_memberships_result_membership_at(future, i);
show all 24 linesReturns
pubnub_app_context_page_t pubnub_get_memberships_result(pubnub_future_t future);
pubnub_membership_t pubnub_get_memberships_result_membership_at(pubnub_future_t future, size_t index);
Set memberships
Adds and/or removes channel memberships for a UUID in a single call. opts is required. The header documents that a call where both set_count and remove_count are zero fails with PUBNUB_ERR_INVALID_ARGUMENT (a no-op call), so build every call with at least one non-empty array. There is no if_match field on this struct: conditional update is not available for membership set operations.
Method(s)
pubnub_future_t pubnub_set_memberships(
pubnub_context_t* ctx, const pubnub_set_memberships_opts_t* opts);
| Parameter | Description |
|---|---|
uuidType: const char*Default: NULL = context's own user_id | Borrowed. |
setType: const pubnub_membership_input_t*Default: NULL = no additions | Borrowed array. |
set_countType: size_tDefault: 0 | Element count of set. |
removeType: const pubnub_membership_input_t*Default: NULL = no removals | Borrowed array. Only channel_id is read for a removal; the rest of each element is ignored. |
remove_countType: size_tDefault: 0 | Element count of remove. |
includeType: uint32_tDefault: 0 | |
limitType: uint32_tDefault: 0 | Applies to the response page, not the set/remove arrays. |
start / end / filter / sortType: const char*Default: NULL | Same as Get memberships. |
timeout_msType: uint32_tDefault: 0 |
pubnub_membership_input_t, used for each element of set:
typedef struct pubnub_membership_input {
const char* channel_id;
const char* status;
const char* type;
const char* custom;
size_t custom_len;
struct pubnub_json_value* custom_value;
} pubnub_membership_input_t;
channel_id is required per element. custom/custom_value follow the same two-mode, mutual-exclusion, ownership-transfer rule described in Custom metadata, applied per element rather than once for the whole call.
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Ownership / lifetime —
set/removearrays borrowed; each element'scustom_value, if set, transfers to the SDK - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
examples/app_context/get_memberships.c only reads. The shipped "set" example under this area, set_members.c, demonstrates the channel-side pubnub_set_channel_members instead (see Set channel members). This sample follows the same shape for the membership side.
#include <pubnub/client.h>
#include <pubnub/features/app_context.h>
#include <pubnub/future.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.publish_key = "demo";
cfg.user_id = "my_unique_user_id";
pubnub_context_t* ctx = pubnub_create(&cfg);
show all 51 linesReturns
pubnub_app_context_page_t pubnub_set_memberships_result(pubnub_future_t future);
pubnub_membership_t pubnub_set_memberships_result_membership_at(pubnub_future_t future, size_t index);
Error responses
opts == NULLfails withPUBNUB_ERR_INVALID_ARGUMENT.set_count == 0 && remove_count == 0fails withPUBNUB_ERR_INVALID_ARGUMENT(a no-op call). This is documented in the header's own doc comment.- Setting both
customandcustom_valueon the same array element fails withPUBNUB_ERR_INVALID_ARGUMENT.
Channel members
A member is a channel's view of its UUID relationships: pubnub_member_t.uuid embeds the full pubnub_uuid_metadata_t documented under UUID metadata.
typedef struct pubnub_member {
pubnub_uuid_metadata_t uuid;
pubnub_string_view_t status;
pubnub_string_view_t type;
const struct pubnub_json_value* custom;
pubnub_string_view_t updated;
pubnub_string_view_t etag;
} pubnub_member_t;
uuid is populated only as far as the PUBNUB_APP_CONTEXT_INCLUDE_UUID* flags request. See Include flags.
Get channel members
Lists the UUIDs that belong to a channel. opts and opts->channel are both required.
Method(s)
pubnub_future_t pubnub_get_channel_members(
pubnub_context_t* ctx, const pubnub_get_channel_members_opts_t* opts);
Zero-initialize an explicit struct with PUBNUB_GET_CHANNEL_MEMBERS_OPTS_INIT (expands to {0}).
| Parameter | Description |
|---|---|
channelType: const char*Default: — | Borrowed. |
includeType: uint32_tDefault: 0 | |
limitType: uint32_tDefault: 0 (server default) | |
start / end / filter / sortType: const char*Default: NULL | Same as Get memberships. |
timeout_msType: uint32_tDefault: 0 |
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
Adapted from examples/app_context/get_channel_members.c.
static void get_channel_members_example(pubnub_context_t* ctx)
{
pubnub_get_channel_members_opts_t opts = PUBNUB_GET_CHANNEL_MEMBERS_OPTS_INIT;
opts.channel = "my_channel";
opts.include = PUBNUB_APP_CONTEXT_INCLUDE_UUID | PUBNUB_APP_CONTEXT_INCLUDE_TOTAL_COUNT;
pubnub_future_t future = pubnub_get_channel_members(ctx, &opts);
pubnub_res_t result = pubnub_await(future);
if (PUBNUB_OK == result) {
pubnub_app_context_page_t page = pubnub_get_channel_members_result(future);
for (uint32_t i = 0; i < page.count; i++) {
pubnub_member_t mem = pubnub_get_channel_members_result_member_at(future, i);
printf("uuid: %.*s\n", (int)mem.uuid.id.len, mem.uuid.id.ptr);
}
show all 22 linesReturns
pubnub_app_context_page_t pubnub_get_channel_members_result(pubnub_future_t future);
pubnub_member_t pubnub_get_channel_members_result_member_at(pubnub_future_t future, size_t index);
Error responses
opts == NULL or opts->channel == NULL fails with PUBNUB_ERR_INVALID_ARGUMENT.
Set channel members
Adds and/or removes UUIDs from a channel's member list in a single call. opts and opts->channel are required. Exactly like Set memberships, the header documents that a call where both set_count and remove_count are zero fails with PUBNUB_ERR_INVALID_ARGUMENT (a no-op call). There is no if_match field on this struct either.
Method(s)
pubnub_future_t pubnub_set_channel_members(
pubnub_context_t* ctx, const pubnub_set_channel_members_opts_t* opts);
| Parameter | Description |
|---|---|
channelType: const char*Default: — | Borrowed. |
setType: const pubnub_member_input_t*Default: NULL | Borrowed array. |
set_countType: size_tDefault: 0 | |
removeType: const pubnub_member_input_t*Default: NULL | Borrowed array. Only uuid_id is read for a removal. |
remove_countType: size_tDefault: 0 | |
includeType: uint32_tDefault: 0 | |
limitType: uint32_tDefault: 0 | Response page size. |
start / end / filter / sortType: const char*Default: NULL | |
timeout_msType: uint32_tDefault: 0 |
pubnub_member_input_t, used for each element of set:
typedef struct pubnub_member_input {
const char* uuid_id;
const char* status;
const char* type;
const char* custom;
size_t custom_len;
struct pubnub_json_value* custom_value;
} pubnub_member_input_t;
uuid_id is required per element. custom/custom_value follow the same rule as Custom metadata, per element.
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Ownership / lifetime —
set/removearrays borrowed; each element'scustom_value, if set, transfers to the SDK - Feature flag —
PUBNUB_ENABLE_APP_CONTEXT - Blocking — never blocks
Sample code
Adapted from examples/app_context/set_members.c.
#include <pubnub/client.h>
#include <pubnub/features/app_context.h>
#include <pubnub/future.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.publish_key = "demo";
cfg.user_id = "my_unique_user_id";
pubnub_context_t* ctx = pubnub_create(&cfg);
show all 47 linesReturns
pubnub_app_context_page_t pubnub_set_channel_members_result(pubnub_future_t future);
pubnub_member_t pubnub_set_channel_members_result_member_at(pubnub_future_t future, size_t index);
Error responses
opts == NULLoropts->channel == NULLfails withPUBNUB_ERR_INVALID_ARGUMENT.set_count == 0 && remove_count == 0fails withPUBNUB_ERR_INVALID_ARGUMENT.- Setting both
customandcustom_valueon the same array element fails withPUBNUB_ERR_INVALID_ARGUMENT.
Receiving App Context events
For the generic listener, subscription, and entity plumbing this section assumes, see Publish & Subscribe. App Context change notifications arrive through the same pubnub_subscribe_event_t delivered to a listener's on_app_context callback as every other subscribe event type.
App context event extractor
Method(s)
pubnub_res_t pubnub_subscribe_event_app_context(
pubnub_context_t* ctx,
const pubnub_subscribe_event_t* event,
pubnub_subscribe_app_context_event_t* out);
typedef enum pubnub_app_context_event_type {
PUBNUB_APP_CONTEXT_SET = 0,
PUBNUB_APP_CONTEXT_REMOVED = 1
} pubnub_app_context_event_type_t;
typedef enum pubnub_app_context_object_type {
PUBNUB_APP_CONTEXT_OBJECT_UNKNOWN = 0,
PUBNUB_APP_CONTEXT_OBJECT_UUID = 1,
PUBNUB_APP_CONTEXT_OBJECT_CHANNEL = 2,
PUBNUB_APP_CONTEXT_OBJECT_MEMBERSHIP = 3
} pubnub_app_context_object_type_t;
typedef struct pubnub_subscribe_app_context_event {
pubnub_app_context_event_type_t event;
pubnub_app_context_object_type_t object_type;
show all 19 linespubnub_subscribe_event_app_context() is declared in #include <pubnub/features/subscribe.h>, not app_context.h. The two enums and the event struct above are declared in #include <pubnub/features/subscribe_types.h> (included transitively by subscribe.h), not in app_context.h either. app_context.h only forward-declares struct pubnub_subscribe_app_context_event (for the typed sub-accessor signatures in Typed sub-accessors below) and is not required to call this extractor on its own.
C-family contract
- Header —
#include <pubnub/features/subscribe.h>(declares the function; pulls insubscribe_types.h, which declares the enums and event struct) - Feature flag —
PUBNUB_ENABLE_SUBSCRIBE - Blocking — never blocks; called synchronously inside a listener callback
object_type is an enum, not a string view. Switch on it rather than treating it like channel/subscription. Reading object_type.len/.ptr does not compile.
Sample code
This follows the same pattern as examples/subscribe/callback.c's on_app_context handler: switch on the enum, then read channel/subscription as string views.
#include <pubnub/client.h>
#include <pubnub/features/app_context.h>
#include <pubnub/features/subscribe.h>
#include <pubnub/features/subscribe_types.h>
#include <stdio.h>
static void on_app_context(const pubnub_subscribe_event_t* event, void* user_data)
{
pubnub_context_t* ctx = (pubnub_context_t*)user_data;
pubnub_subscribe_app_context_event_t obj;
if (PUBNUB_OK != pubnub_subscribe_event_app_context(ctx, event, &obj)) {
return;
}
show all 25 linesReturns
PUBNUB_OK on success. PUBNUB_ERR_INVALID_ARGUMENT if any argument is NULL or the event is not an App Context event. PUBNUB_ERR_SERIALIZATION on parse failure. obj.data, when non-NULL, is a JSON-tree node. Read it with the pattern in Reading custom data back, or use one of the typed sub-accessors below instead of walking it manually.
Typed sub-accessors
Three functions parse obj.data from the extractor above into the same typed metadata structs used by the request/response operations on this page. Gated by both PUBNUB_ENABLE_APP_CONTEXT and PUBNUB_ENABLE_SUBSCRIBE.
The SDK's own on_app_context handler in examples/subscribe/callback.c calls the generic extractor above and walks obj.data manually. The typed sub-accessors below do that parsing for you when you already know which App Context object type you expect.
Method(s)
pubnub_res_t pubnub_subscribe_app_context_uuid_metadata(
pubnub_context_t* ctx,
const struct pubnub_subscribe_app_context_event* event,
pubnub_uuid_metadata_t* out);
pubnub_res_t pubnub_subscribe_app_context_channel_metadata(
pubnub_context_t* ctx,
const struct pubnub_subscribe_app_context_event* event,
pubnub_channel_metadata_t* out);
pubnub_res_t pubnub_subscribe_app_context_membership(
pubnub_context_t* ctx,
const struct pubnub_subscribe_app_context_event* event,
pubnub_membership_t* out);
Call the one matching event->object_type (PUBNUB_APP_CONTEXT_OBJECT_UUID/_CHANNEL/_MEMBERSHIP).
C-family contract
- Header —
#include <pubnub/features/app_context.h> - Feature flag —
PUBNUB_ENABLE_APP_CONTEXTandPUBNUB_ENABLE_SUBSCRIBE - Ownership / lifetime —
outis caller-provided; every field inside it is valid only for the duration of the listener callback that producedevent. This is shorter than the lifetime of REST results on this page, which stay valid until you callpubnub_future_release(). Copy anything you need before the callback returns. - Blocking — never blocks
Sample code
Combining the three accessors with the extractor above:
#include <pubnub/client.h>
#include <pubnub/features/app_context.h>
#include <pubnub/features/subscribe.h>
#include <pubnub/features/subscribe_types.h>
#include <stdio.h>
static void on_app_context_typed(const pubnub_subscribe_event_t* event, void* user_data)
{
pubnub_context_t* ctx = (pubnub_context_t*)user_data;
pubnub_subscribe_app_context_event_t obj;
if (PUBNUB_OK != pubnub_subscribe_event_app_context(ctx, event, &obj)) {
return;
}
show all 44 linesReturns
PUBNUB_OK on success, including when event->data == NULL, in which case *out is left zero-initialized. PUBNUB_ERR_INVALID_ARGUMENT if ctx, event, or out is NULL. PUBNUB_ERR_SERIALIZATION if the context's serialization provider is unavailable.
Error handling
pubnub_res_t is the single status type shared across this SDK. App Context has no per-function error accessors. See Status Events for the full result-code catalog and the pubnub_response_service_error() pattern for server-side error detail.
Two validation errors are specific to this feature area and worth keeping close at hand:
- Setting both
customandcustom_valuenon-NULLon the same struct (or the samesetarray element) isPUBNUB_ERR_INVALID_ARGUMENT, checked before any network I/O. pubnub_set_memberships/pubnub_set_channel_memberswithset_count == 0 && remove_count == 0isPUBNUB_ERR_INVALID_ARGUMENT(a no-op call), as documented on each function's own doc comment.