---
source_url: https://www.pubnub.com/docs/sdks/c/migration-guides/migrating-from-c-core-v7
title: Migrating from C-Core v7
updated_at: 2026-09-24T12:25:41.000Z
sdk_name: PubNub C SDK
sdk_version: 1.0.0
---

# Migrating from C-Core v7

PubNub C SDK, use the latest version: 1.0.0

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

This guide is for anyone with an existing application built on C-Core SDK v7 (`PUBNUB_SDK_VERSION` reporting `7.3.1`) who wants to move it to the new C SDK.

Read this before you start porting. **This is a full rewrite, not an incremental upgrade.** Every public symbol changed. The context lifecycle, the asynchronous model, the error model, the build system, and the platform-abstraction approach are all different from v7, and there is no mechanical find-and-replace path from one to the other. If you maintain a custom platform port, a custom build script, or code that inspects `enum pubnub_res` values, budget real engineering time for this migration. Don't treat it as a version bump.

This guide is for you if any of the following is true:

* You have application code that calls v7 functions such as `pubnub_alloc()`, `pubnub_publish()`, `pubnub_subscribe()`, or anything under `core/pubnub_coreapi.h`.
* You maintain a custom platform port (a target other than POSIX, Windows, or FreeRTOS), a custom Makefile fragment, or a hand-edited `pubnub_config.h`.
* You rely on a specific `PNR_*` result code, on DNS-server configuration functions, or on the SDK's built-in UUID generators.

If you are new to the SDK and have no existing v7 code, skip this guide and start with [Getting Started](https://www.pubnub.com/docs/sdks/c.md) instead. If you still need v7's own documentation for reference while you migrate, it lives under [C-Core SDK (legacy)](https://www.pubnub.com/docs/sdks/c-core.md).

## What has changed

| Area | C-Core v7 | New SDK |
| --- | --- | --- |
| [Context lifecycle](#context-lifecycle) | `pubnub_alloc()` / `pubnub_free()`, heap only; the static-vs-`malloc` backend choice is invisible at the call site | `pubnub_create()` / `pubnub_destroy()` (heap) or `pubnub_init()` / `pubnub_deinit()` (caller-provided memory) |
| [The async model](#the-async-model) | A header/build choice: `pubnub_sync.h` vs. `pubnub_callback.h`, optionally overridden at runtime with `pubnub_enforce_api()` | One `pubnub_future_t` per call; three consumption styles chosen per call site, no header or build choice |
| [The error model](#the-error-model) | `enum pubnub_res`, 41 values | `pubnub_res_t`, 16 values, with server-side detail moved to a normalized `pubnub_service_error_t` |
| [The provider model](#the-provider-model) | A fixed per-platform file stack (`posix/`, `windows/`, `freertos/`, `mbedtls/`, `microchip_harmony/`, `qt/`, `unreal/`, `cpp/`) | Six swappable provider vtables under `include/pubnub/providers/`, validated at context-creation time |
| [The build system](#the-build-system) | Makefiles plus a hand-edited `pubnub_config.h` per platform | CMake with `full` / `minimal` / `embedded` profiles and a generated config header |
| `pubnub_delete_messages()`'s `end` bound | Inclusive | **Exclusive**, see [Silent behavior changes](#silent-behavior-changes) |
| History entry point | `pubnub_fetch_history()` | `pubnub_fetch_messages()`, see [Renames and restructuring](#renames-and-restructuring) |
| Message Actions `type` | Closed 3-value enum on the deprecated call path; a free-form string, already, on the recommended v7 replacement | Always a free-form string, up to 15 characters |
| Access Manager | Hand-built JSON via `sprintf`; the issued token is raw CBOR you decode yourself | Typed options struct; the token is parsed for you into indexed accessors |
| Files, Mobile Push | Did not exist | Full APIs, see [What's new](#whats-new) |
| [Platform support](#platform-support) | Includes Microchip Harmony, Qt, Unreal Engine, mbedTLS-as-transport, and a working C++ class | POSIX, Windows, FreeRTOS, and ESP-IDF only; the C++ wrapper is an unimplemented placeholder |

## Silent behavior changes

These six changes compile fine against the new SDK and produce a different result, with no compiler warning and no runtime diagnostic. Read this section before you port any code, not after something breaks in production.

### Delete messages: end flipped from inclusive to exclusive

:::warning Ported delete code silently leaves one message undeleted
In C-Core v7, `pubnub_delete_messages()`'s `end` timetoken was **inclusive**. It deleted the message at that exact timetoken along with everything after it. In the new SDK, `end` is **exclusive**, so the message at that timetoken is kept. A v7 delete call ported without changing `end` compiles, runs, returns `PUBNUB_OK`, and silently leaves exactly one message in place. Nothing flags this as an error.
:::

###### Before (C-Core v7)

```text
/* C-Core v7 — end is INCLUSIVE: the message AT "end" is deleted too */
pubnub_t* pb = pubnub_alloc();
pubnub_init(pb, "demo", "demo");

pubnub_delete_messages(pb, "my_channel",
                        "17001234567890000",   /* start, exclusive */
                        "17001234567890123");  /* end, deleted in v7 */

pubnub_free(pb);
```

###### After (new SDK)

```c
#include <pubnub/client.h>
#include <pubnub/features/history.h>
#include <pubnub/future.h>
#include <pubnub/response.h>

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    pubnub_config_t cfg = pubnub_config_defaults();
    cfg.subscribe_key = "demo";
    cfg.user_id       = "my_unique_user_id";

    pubnub_context_t* ctx = pubnub_create(&cfg);
    if (NULL == ctx) {
        return EXIT_FAILURE;
    }

    pubnub_delete_messages_opts_t opts = PUBNUB_DELETE_MESSAGES_OPTS_INIT;
    opts.channel = "my_channel";
    /* end is EXCLUSIVE now — bump it by one to also delete the message
       that used to be included by v7's inclusive "end" */
    opts.end     = "17001234567890124";

    pubnub_future_t future = pubnub_delete_messages(ctx, &opts);
    pubnub_res_t     result = pubnub_await(future);

    if (PUBNUB_OK == result) {
        printf("delete OK\n");
    }
    else {
        pubnub_string_view_t err = pubnub_response_error_message(future);
        printf("delete failed: %s (%.*s)\n", pubnub_res_str(result), (int)err.len, err.ptr);
    }

    pubnub_future_release(future);
    pubnub_destroy(ctx);
    return EXIT_SUCCESS;
}
```

Full field reference and error handling: [Delete messages](https://www.pubnub.com/docs/sdks/c/api-reference/storage-and-playback.md#delete-messages). Note that `pubnub_fetch_messages()`'s own `end` field is inclusive, unchanged from v7. Only delete's boundary changed. Don't assume fetch and delete share one rule on either SDK.

### Message action value no longer needs quote-wrapping — and the new SDK won't tell you if you still send it that way

:::warning danger
Ported code that still quote-wraps
value
stores a wrong value, silently
C-Core v7 required the caller to manually wrap `value` in literal quote characters and rejected a value missing them with a parameter error. The new SDK's `value` field is a plain string with no such requirement and no such check. Code that still sends `"\"thumbs_up\""` instead of `"thumbs_up"` compiles, sends successfully, and stores a literally-quoted string as the action value. Nothing flags the mismatch.
:::

See [Add message action](https://www.pubnub.com/docs/sdks/c/api-reference/message-actions.md#add-message-action) for the current field reference.

### The old 100-item cap on pubnub_get_message_actions()'s limit is gone, silently

:::warning A caller mistake that used to be caught now proceeds instead
C-Core v7 documented `limit` as valid only in the range 1–100 and returned an error for anything higher. The new SDK's `limit` is a plain `uint32_t` with no documented or enforced maximum. Code that relied on the old error to catch an out-of-range value passed in by mistake no longer gets that safety net. The call proceeds instead of failing.
:::

See [Get message actions](https://www.pubnub.com/docs/sdks/c/api-reference/message-actions.md#get-message-actions).

### JSON-tree ownership is not uniform across features

:::warning Applying one feature's ownership rule to another is a double free or a leak
This trap is not old-versus-new. It is new-SDK-internal, but it is the single highest-risk issue for anyone porting several features from v7 in one pass, so it belongs here. Several options structs accept a `pubnub_json_value_t*` tree, and the ownership rule differs by feature. `publish.h`'s `message_value`, `signal.h`'s `message_value`, and `presence.h`'s `state_value` are all **caller-retains-ownership**: you destroy the tree yourself with `pubnub_json_destroy()` after the call returns. `app_context.h`'s `custom_value` is the outlier. Its ownership **transfers to the SDK**, which consumes and frees the tree during the call, on both success and failure. A developer who ports Publish first, correctly learns "I own it, I free it," and then reaches App Context and applies the same rule gets a double free. Applying the App Context rule to `message_value` or `state_value` instead leaks the tree. Neither mistake produces a diagnostic. Check the ownership rule for every JSON-tree field individually. Never generalize from one feature to another.
:::

### Heartbeat can be silently off by default

:::warning A "config-driven" heartbeat is not the same as an automatic one
C-Core v7 required an explicit `pubnub_heartbeat()` call, or an opt-in compiled auto-heartbeat module with its own watcher threads, so v7 users expect to turn heartbeat on explicitly. The new SDK has no callable heartbeat function at all. It's driven entirely by the `presence_timeout` and `heartbeat_interval` config fields and starts automatically when a subscription activates. But leaving both fields at their zero defaults, which is exactly what `pubnub_config_defaults()` produces, computes a heartbeat interval of `0`. That means **no automatic heartbeat is ever sent**, with no error or log line to say so. Set `presence_timeout` or `heartbeat_interval` explicitly to get automatic heartbeat.
:::

###### Before (C-Core v7)

```text
/* C-Core v7 — heartbeat was an explicit call, or an opt-in compiled
   module (core/pbauto_heartbeat.h) with its own watcher threads */
pubnub_heartbeat(pb, "my_channel", NULL);
```

###### After (new SDK)

```c
#include <pubnub/client.h>

static void enable_heartbeat(void)
{
    pubnub_config_t cfg = pubnub_config_defaults();
    cfg.subscribe_key = "demo";
    cfg.user_id       = "my_unique_user_id";

    /* Heartbeat has no callable function. Leaving presence_timeout and
       heartbeat_interval at their zero defaults sends NO heartbeat at all. */
    cfg.presence_timeout = 120; /* heartbeat_interval is then auto-computed */
}
```

Full explanation: [Heartbeat](https://www.pubnub.com/docs/sdks/c/api-reference/presence.md#heartbeat).

### Access Manager's TTL cap moved from server-side to client-side

:::note Same limit, different failure point
The 43,200-minute (30-day) TTL cap is unchanged in value between v7 and the new SDK, but the new SDK enforces it client-side, before any network call, whereas the v7 documentation describes server-side enforcement. Not dangerous, but if you're debugging why an out-of-range `ttl` now fails instantly instead of after a round trip, that's why. See [Grant token](https://www.pubnub.com/docs/sdks/c/api-reference/access-manager.md#grant-token).
:::

## Context lifecycle

C-Core v7 had one lifecycle. `pubnub_alloc()` returned a heap-allocated context and `pubnub_free()` released it. Whether that heap allocation came from `malloc` or from a fixed-size static array was a link-time choice invisible at the call site. Application code couldn't tell which backend it was linked against, and there was no caller-provided-memory option at all.

The new SDK has two lifecycle models, selected by which pair of functions you call:

* **Heap.** `pubnub_create()` / `pubnub_destroy()`. Deep-copies every string field in your `pubnub_config_t`. Compiled out entirely when `PUBNUB_CFG_NO_HEAP=1` (the default in the `embedded` CMake profile).
* **Caller-provided memory.** `pubnub_init()` / `pubnub_deinit()`. Borrows every string field, so you must keep them valid for the context's lifetime.

:::note A name collision to watch for
The new SDK's `pubnub_init()` is not a renamed continuation of v7's `pubnub_init()`. v7's version took a context already produced by `pubnub_alloc()` plus key strings. The new version takes a context pointer to memory **you** supply plus a `pubnub_config_t*`. The two have different parameter lists, so a straight port won't compile as-is. And once you do get it to compile, don't assume the identical function name means identical behavior.
:::

###### Before (C-Core v7)

```text
/* C-Core v7 — one lifecycle, heap-only */
pubnub_t* pb = pubnub_alloc();
pubnub_init(pb, "demo", "demo");

/* ... use pb for publish/subscribe/etc ... */

pubnub_free(pb);   /* cancels any in-progress transaction, then frees */
```

###### After (new SDK) — heap

```c
#include <stddef.h>
#include <pubnub/client.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);
    if (ctx == NULL) {
        /* config was NULL, failed validation, or allocation failed */
        return 1;
    }

    /* ctx is ready — pass it to any feature call, e.g. pubnub_publish() */

    pubnub_destroy(ctx);
    return 0;
}
```

If your v7 target was a no-heap embedded platform, use `pubnub_init()`/`pubnub_deinit()` on a caller-provided buffer instead. See [Initialization](https://www.pubnub.com/docs/sdks/c/api-reference/configuration.md#initialization) for the full pattern, including the alignment requirement for a statically declared context buffer.

## The async model

C-Core v7 selected sync-versus-callback at the **header level**, per platform. Including `pubnub_sync.h` compiled in the blocking model, and including `pubnub_callback.h` (with `PUBNUB_CALLBACK_API` defined) compiled in the callback model. `pubnub_enforce_api()` could also switch between them at runtime, but only when both were compiled in, and only if called in the right order relative to initialization.

The new SDK has exactly one model: every feature entry point returns a `pubnub_future_t` by value. You choose how to consume it **per call site**, with three functions from `pubnub/future.h`:

* Cooperative polling — `pubnub_process()` + `pubnub_future_is_ready()`
* Blocking — `pubnub_await()`
* Callback — `pubnub_async()`

There is no header to pick and no build flag to set. `pubnub_sync.h`, `pubnub_callback.h`, and `pubnub_enforce_api()` have no counterpart in the new SDK.

### Before (C-Core v7)

```text
/* C-Core v7 — the async model was a header choice, per platform */
#include "posix/pubnub_callback.h"   /* or posix/pubnub_sync.h for blocking */

pubnub_t* pb = pubnub_alloc();
pubnub_init(pb, "demo", "demo");

pubnub_publish(pb, "my_channel", "\"hello\"");
/* result arrives via the model that pubnub_callback.h / pubnub_sync.h
   compiled in — not chosen at this call site */
```

### After (new SDK) — pick a style per call

```c
#include <pubnub/client.h>
#include <pubnub/features/publish.h>
#include <pubnub/future.h>

#include <stdio.h>

int main(void)
{
    pubnub_config_t cfg = pubnub_config_defaults();
    cfg.subscribe_key = "demo";
    cfg.publish_key   = "demo";
    cfg.user_id       = "device-042";

    pubnub_context_t* ctx = pubnub_create(&cfg);
    if (ctx == NULL) {
        return 1;
    }

    pubnub_publish_opts_t opts = PUBNUB_PUBLISH_OPTS_INIT;
    opts.channel = "sensors";
    opts.message = "\"temperature:21\"";

    /* This call is the same regardless of consumption style. */
    pubnub_future_t future = pubnub_publish(ctx, &opts);

    /* Blocking style, chosen right here: */
    pubnub_res_t result = pubnub_await(future);
    if (result != PUBNUB_OK) {
        fprintf(stderr, "publish did not complete cleanly: %d\n", (int)result);
    }
    pubnub_future_release(future);

    pubnub_destroy(ctx);
    return 0;
}
```

Cooperative-polling and callback variants of the same call appear in [Calling patterns](https://www.pubnub.com/docs/sdks/c/environment-setup.md#calling-patterns).

## The error model

C-Core v7's `enum pubnub_res` had 41 values, each mapping fairly directly to one failure cause. The new SDK's `pubnub_res_t` has 16 values, range-coded by class. Server-side cause detail moved into a separate, normalized `pubnub_service_error_t` you retrieve on demand.

Three groups of old codes map cleanly onto a single new value each:

* 11 old per-feature server-error codes (`PNR_PUBLISH_FAILED`, `PNR_OBJECTS_API_ERROR`, `PNR_GRANT_TOKEN_API_ERROR`, `PNR_ACCESS_DENIED`, and seven others) all collapse into `PUBNUB_ERR_SERVER`.
* 3 old timeout variants (`PNR_WAIT_CONNECT_TIMEOUT`, `PNR_CONNECTION_TIMEOUT`, `PNR_TIMEOUT`) collapse into `PUBNUB_ERR_TIMEOUT`.
* 5 old network-failure codes (`PNR_ADDR_RESOLUTION_FAILED`, `PNR_CONNECT_FAILED`, `PNR_ABORTED`, `PNR_IO_ERROR`, `PNR_HTTP_ERROR`) collapse into `PUBNUB_ERR_TRANSPORT`.

:::note The remaining codes don't map one-to-one
Roughly 22 other v7 codes (for example `PNR_INVALID_CHANNEL`, `PNR_OUT_OF_MEMORY`, `PNR_TX_BUFF_TOO_SMALL`) don't each correspond to a single new value the way the three groups above do. Treat them at the class level shown above and in the full catalog at [The pubnub_res_t result catalog](https://www.pubnub.com/docs/sdks/c/status-events.md#the-pubnub_res_t-result-catalog). This guide doesn't provide a precise one-to-one table for those codes.
:::

`PNR_GOT_ALL_ACTIONS` has no counterpart at all. Pagination for message actions is now a `has_more` field on the result, not a distinct status. The new SDK also has no per-feature error accessor anywhere: every operation reports through the same `pubnub_res_t`, and server-side detail comes from `pubnub_response_service_error()`.

###### Before (C-Core v7)

```text
/* C-Core v7 — 41 distinct enum pubnub_res values */
enum pubnub_res result = pubnub_last_result(pb);
switch (result) {
    case PNR_OK:
        break;
    case PNR_TIMEOUT:
    case PNR_CONNECTION_TIMEOUT:
        /* two of three old timeout variants */
        break;
    case PNR_PUBLISH_FAILED:
    case PNR_GRANT_TOKEN_API_ERROR:
        /* two of eleven old per-feature server errors */
        break;
    default:
        break;
}
```

###### After (new SDK) — 16 values, normalized detail

```c
#include <pubnub/error.h>
#include <pubnub/future.h>

static void handle_result(pubnub_res_t result)
{
    switch (result) {
        case PUBNUB_OK:
            break;
        case PUBNUB_ERR_TRANSPORT:
        case PUBNUB_ERR_TIMEOUT:
            /* likely transient — consider retrying */
            break;
        case PUBNUB_ERR_SERVER:
            /* call pubnub_response_service_error() for normalized detail */
            break;
        default:
            break;
    }
}
```

Full pattern for reading server-side detail out of `pubnub_service_error_t`: [Retrieving server error detail](https://www.pubnub.com/docs/sdks/c/status-events.md#retrieving-server-error-detail).

## The provider model

This is the single biggest structural gain for anyone who maintained a custom port under v7.

C-Core v7 had no provider or vtable concept. Transport, TLS, and JSON handling were selected by **linking a fixed set of platform-specific .c files**: `posix/`, `windows/`, `freertos/`, `mbedtls/`, `microchip_harmony/`, `qt/`, `unreal/`. Porting to an unsupported platform meant writing new files that matched the old tree's internal function names and file-layout conventions. No documented list said which functions were mandatory, and no build-time or load-time check confirmed a port was complete. An incomplete port surfaced as a link error or undefined runtime behavior, not a diagnosable status code.

The new SDK exposes six independently swappable provider families: allocator, transport, serialization, platform, crypto, and logger. Each is a public vtable struct under `include/pubnub/providers/`. You select a backend at CMake configure time with a `PUBNUB_PROVIDER_<FAMILY>` cache variable, or override any single family per context by pointing the matching `pubnub_config_t` field at a vtable instance you implement yourself. Four of the six families are validated at context-creation time. A missing mandatory vtable member returns `PUBNUB_ERR_PROVIDER_MISSING` instead of failing silently.

### Before (C-Core v7) — porting to a new platform

```text
/* C-Core v7 — porting meant writing new .c files under the old tree's
   internal naming and layout conventions, with no documented mandatory
   list and no check that the port was complete. A gap surfaced as a
   link error or undefined behavior at runtime, not a status code. */
```

### After (new SDK) — porting is implementing a documented vtable

```c
#include <pubnub/client.h>

static void configure_providers(void)
{
    pubnub_config_t cfg = pubnub_config_defaults();
    cfg.subscribe_key = "demo";
    cfg.user_id       = "my_unique_user_id";

    /* Leave a field NULL to use the compiled-in default, or point it
       at a vtable you implement yourself to port to a new platform. */
    cfg.allocator     = NULL;
    cfg.transport     = NULL;
    cfg.serialization = NULL;
    cfg.platform      = NULL;
    cfg.crypto_module = NULL;
    cfg.logger        = NULL;
}
```

The full six vtables, their exact member prototypes, and the validation/resolution order are documented in [Providers](https://www.pubnub.com/docs/sdks/c/api-reference/providers.md#how-provider-resolution-and-validation-works) and its [custom-provider section](https://www.pubnub.com/docs/sdks/c/api-reference/providers.md#writing-and-registering-a-custom-provider). Backend-selection strings per family are in [Provider backends](https://www.pubnub.com/docs/sdks/c/environment-setup.md#provider-backends).

## The build system

C-Core v7 built with Makefiles (`posix.mk`, `windows.mk`, dispatching into per-platform `make/*.mk` fragments). Compile-time sizing and feature knobs lived in a hand-edited, per-platform header named identically across directories: `posix/pubnub_config.h`, `windows/pubnub_config.h`, `freertos/pubnub_config.h`, and so on. A representative knob was `PUBNUB_CTX_MAX`, sizing a fixed context array when the old SDK's static allocation backend was linked in.

The new SDK builds with CMake (3.16 or later). Instead of hand-editing a config header per platform, you select a named **build profile**, `full`, `minimal`, or `embedded`, that fixes a coherent combination of feature flags, provider backends, and buffer sizes. Then override individual `PUBNUB_CFG_*` cache variables as needed. There's no file-for-file translation from a v7 Makefile fragment or `pubnub_config.h` to the new system. You re-express your choices as CMake variables from a documented starting point (a profile), not by porting old build files directly.

### Before (C-Core v7)

```bash
# C-Core v7 — Makefiles, dispatched per platform
make -C posix -f posix.mk
make -C openssl -f posix.mk

# Feature/sizing knobs were hand-edited directly in posix/pubnub_config.h,
# e.g. #define PUBNUB_CTX_MAX 4
```

### After (new SDK)

```bash
# New SDK — CMake, profile-driven
cmake --preset full
cmake --build --preset full
ctest --preset full

# Override an individual tunable on top of the profile's defaults:
cmake --preset full -DPUBNUB_CFG_MAX_IN_FLIGHT_REQUESTS=8
```

There's no installable CMake package for the new SDK. The only integration pattern shown by its own examples is `add_subdirectory()` plus `target_link_libraries()`. Full profile, provider, and tunable reference: [Build profiles and presets](https://www.pubnub.com/docs/sdks/c/environment-setup.md#build-profiles-and-presets) and [Provider backends](https://www.pubnub.com/docs/sdks/c/environment-setup.md#provider-backends).

## Renames and restructuring

These v7 capabilities still exist in the new SDK, restructured or renamed, rather than removed.

| C-Core v7 | New SDK | Notes |
| --- | --- | --- |
| `pubnub_fetch_history()` | `pubnub_fetch_messages()` | Also the multi-channel entry point; single-shot only in v7 |
| Channel-group functions in `pubnub_coreapi.h` (no dedicated header) | Dedicated `pubnub_channel_group_*()` functions | See [Channel groups](https://www.pubnub.com/docs/sdks/c/api-reference/channel-groups.md) |
| `pubnub_add_message_action()` (deprecated, closed 3-value type enum) / `pubnub_add_message_action_str()` (recommended, free-form string) | One `pubnub_add_message_action()`, always a free-form `type` string, up to 15 characters | If you had already migrated off the deprecated v7 function, the type representation doesn't change here, see [Add message action](https://www.pubnub.com/docs/sdks/c/api-reference/message-actions.md#add-message-action) |
| `pubnub_get_message_actions_more()` + `PNR_GOT_ALL_ACTIONS` | `has_more` / `more_start` / `more_end` / `more_limit` fields fed back into a repeated call to `pubnub_get_message_actions()` | No separate continuation function or status code |
| `pubnub_history_with_message_actions()` + its own `_more()` pair | `include_message_actions` flag on `pubnub_fetch_messages_opts_t` | One flag on one function replaces two v7 functions and their continuation pair |
| `pubnub_grant_token(pb, char const* perm_obj)`, hand-`sprintf`'d JSON | `pubnub_grant_token(ctx, const pubnub_grant_token_opts_t*)`, typed permission arrays | See [Grant token](https://www.pubnub.com/docs/sdks/c/api-reference/access-manager.md#grant-token) |
| `pubnub_parse_token()`, returns raw CBOR you Base64-decode and parse yourself | `pubnub_parse_token()`, returns a fully decoded `pubnub_parsed_token_t` | See [Parse token](https://www.pubnub.com/docs/sdks/c/api-reference/access-manager.md#parse-token) |
| `pubnub_state_get()` | `pubnub_get_state()` | Renamed, options-struct rewrite |
| App Context (Objects v2), existed in v7 source under `pubnub_objects_api.h`, positional string/JSON arguments, never documented | Options-struct API, 12-flag `include` bitmask, pagination | Same 12 logical operations, see [App Context](https://www.pubnub.com/docs/sdks/c/api-reference/app-context.md) |

## Gone with no counterpart

These v7 symbols and capabilities have no equivalent anywhere in the new SDK's public headers:

* `pubnub_alloc()`, `pubnub_free()`, `pubnub_cancel()`. See [Context lifecycle](#context-lifecycle) for the replacement model. (`pubnub_future_cancel()` cancels one in-flight request, not the context as a whole.)
* `pubnub_sync.h`, `pubnub_callback.h`, `pubnub_enforce_api()`. See [The async model](#the-async-model).
* `PUBNUB_ASSERT` and the whole assert-handler subsystem.
* The 14 individual DNS server configuration functions (`pubnub_dns_set_primary_server_ipv4` and its siblings) are gone, replaced by a single `pubnub_set_dns_servers()` call that takes a primary and secondary server string. See [DNS servers](https://www.pubnub.com/docs/sdks/c/api-reference/misc.md#dns-servers). Every UUID-generation function (`pubnub_generate_uuid_v1_time` through `_v5_name_sha1`, plus `srand_from_pubnub_time`) has no counterpart at all. See [No UUID-generation API](https://www.pubnub.com/docs/sdks/c/api-reference/misc.md#no-uuid-generation-api).
* `pubnub_publish_ex()` and its GZIP-over-POST option. Message compression is compile-time only now (`PUBNUB_ENABLE_COMPRESSION` / `PUBNUB_ENABLE_REQUEST_COMPRESSION`), with no callable compression function.
* `pubnub_subscribe_v2()` and `PUBNUB_USE_SUBSCRIBE_EVENT_ENGINE`. There's exactly one subscribe model now; see [Entities](https://www.pubnub.com/docs/sdks/c/api-reference/publish-and-subscribe.md#entities) and [Create a subscription](https://www.pubnub.com/docs/sdks/c/api-reference/publish-and-subscribe.md#create-a-subscription).
* `pubnub_global_here_now()`, a distinct, working v7 function with no counterpart here. `pubnub_here_now()` requires at least one channel or channel group. Calling it with both `channels` and `channel_groups` left `NULL` fails with `PUBNUB_ERR_INVALID_ARGUMENT` instead of returning occupancy across every channel. If you need that, query each channel or channel group explicitly.
* Here-now pagination (`limit`/`offset` on the options struct). `pubnub_here_now_opts_t` has no pagination fields at all.

## What's new

Not everything about this rewrite is loss. Three areas are genuine gains for a v7 user:

* **Files.** Didn't exist in v7 at all. Send, list, get-URL, download, and delete, plus a publish-file-message recovery path. See [Files](https://www.pubnub.com/docs/sdks/c/api-reference/files.md).
* **Mobile Push.** Didn't exist in v7 at all. APNs (HTTP/2) and FCM device-channel registration management. See [Mobile Push](https://www.pubnub.com/docs/sdks/c/api-reference/mobile-push.md).
* **The provider model.** Covered in full above. Ports that required editing SDK-internal source under v7 can now be expressed as one or more documented vtables, validated at context-creation time instead of failing silently.
* **Access Manager token parsing.** `pubnub_parse_token()` now decodes CBOR internally and hands back a typed, indexed-accessor result. v7 required an external CBOR parser.
* **CMake build profiles.** `full` / `minimal` / `embedded` replace hand-edited per-platform `pubnub_config.h` files with one declarative, overridable configuration surface.

## Platform support

State this plainly before you invest porting effort. Some v7 targets have **no counterpart** in the new SDK.

| Platform / capability | C-Core v7 | New SDK |
| --- | --- | --- |
| POSIX | Supported | Supported (`PUBNUB_PROVIDER_PLATFORM=posix`) |
| Windows | Supported | Supported (`PUBNUB_PROVIDER_PLATFORM=windows`) |
| FreeRTOS | Supported | Supported (`PUBNUB_PROVIDER_PLATFORM=freertos`) |
| ESP-IDF | Supported through the general-purpose `mbedtls/` module, not as a first-class target | A dedicated, separate build path (`cmake/PubnubESP.cmake`), new capability, not carried forward from v7's approach |
| Microchip Harmony | Full platform port (`microchip_harmony/`) | **No counterpart.** No provider or directory targets it. |
| Qt | Full integration with its own C++ class (`qt/`) | **No counterpart.** |
| Unreal Engine | Integration as an Unreal module (`unreal/`), documented in its own README as not yet fully functional | **No counterpart.** |
| mbedTLS as transport TLS | Supported for embedded TLS (`mbedtls/`) | mbedTLS survives only as a **crypto** provider backend (payload encryption/HMAC). It doesn't perform transport-level TLS. The transport family is `curl` / `socket` / `custom` only, so a v7 user relying on `mbedtls/` for embedded transport TLS needs to write a `custom` transport provider. |
| A working C++ API | A real wrapper class (`cpp/`) with per-platform futures backends | The new C++ wrapper is an unimplemented placeholder. Anyone depending on the C++ surface, as opposed to plain C, has nothing to migrate to yet. |

If you're on Microchip Harmony, Qt, or Unreal Engine, or you rely on mbedTLS for transport-level TLS rather than payload crypto, or you use the C++ wrapper rather than the C API, confirm there's a path forward for you **before** investing further porting effort. See [Provider backends](https://www.pubnub.com/docs/sdks/c/environment-setup.md#provider-backends) for exactly what a `custom` provider requires, and [Platform Support (legacy)](https://www.pubnub.com/docs/sdks/c-core/platform-support.md) for what v7 supported on your platform today.

## Migration steps

1. **Pick a CMake profile**, `full`, `minimal`, or `embedded`, before anything else. It fixes every feature flag and provider default your build starts from. There's no Makefile-fragment-by-fragment translation from v7; this is a from-scratch decision. See [Build profiles and presets](https://www.pubnub.com/docs/sdks/c/environment-setup.md#build-profiles-and-presets).
2. **Decide your context lifecycle model**: heap (`pubnub_create()` / `pubnub_destroy()`) or caller-provided memory (`pubnub_init()` / `pubnub_deinit()`, mandatory if your profile sets `PUBNUB_CFG_NO_HEAP=1`, which `embedded` does). This decision sets the string-ownership rule, borrow versus deep-copy, that every config field you set next must respect.
3. **Re-express your v7 platform configuration**, a hand-edited `pubnub_config.h` and, if you maintained a custom platform port, its file-level implementation, as `PUBNUB_CFG_*` CMake cache variables and one or more provider vtables. Do this before touching any feature code, since every feature call needs a working context first.
4. **Rewrite context setup and your call pattern** for one simple operation, such as publish, before porting any feature's business logic. Every other feature's call depends on the one-future, three-consumption-styles model, so validating your provider and config choices against a trivial publish first is cheaper than discovering a config mistake after porting several features.
5. **Port feature-by-feature** using [Renames and restructuring](#renames-and-restructuring) and the per-feature API reference pages linked throughout this guide, in whatever order matches your application. While doing so, check every call against [Silent behavior changes](#silent-behavior-changes), especially the `pubnub_delete_messages()` boundary and the JSON-tree ownership rule if you touch more than one JSON-tree-accepting feature.
6. **Re-check any platform you relied on that isn't POSIX, Windows, or FreeRTOS** against [Platform support](#platform-support) before investing further porting effort. Microchip Harmony, Qt, and Unreal Engine users have no target to port to today. mbedTLS-as-transport users need a custom transport provider, not a drop-in replacement.
7. **Rebuild your error-handling code last.** The 41-to-16 result-code collapse touches every call site uniformly, so it's mechanical once every call site itself compiles. Doing it last avoids editing error-handling code twice as feature signatures change underneath it.

## Additional resources

* [Environment Setup](https://www.pubnub.com/docs/sdks/c/environment-setup.md): build profiles, provider backends, feature flags, and the three async consumption styles in full.
* [Configuration](https://www.pubnub.com/docs/sdks/c/api-reference/configuration.md): the complete `pubnub_config_t` field reference.
* [Status Events](https://www.pubnub.com/docs/sdks/c/status-events.md): the full `pubnub_res_t` catalog and server-error detail pattern.
* [Providers](https://www.pubnub.com/docs/sdks/c/api-reference/providers.md): all six provider vtables and how to write a custom one.
* [C-Core SDK documentation (legacy)](https://www.pubnub.com/docs/sdks/c-core.md): v7's own reference, if you still need it during migration.
* For questions or issues, contact [PubNub support](https://support.pubnub.com/).

Last updated at: 2026-09-24T12:25:41.000Z
