Release notes

What changed in each ContentFlow SDK release, what you need to do when you upgrade, and how to upgrade. Downloads and checksums are on Releases and downloads; each SDK's full changelog is linked from there.

Upgrading to a new version

The upgrade steps for every platform (swap the archive, rebuild, check the version, roll back) are in Install and upgrade the SDK. Before you upgrade, read the Action needed notes below for every version you skip. For 2.2.1 those are the certificate pins and the legacy base URL; for 2.2.2, Kotlin's deprecated no-op options and Flutter's identify before setChannelConsent; for 2.3.0, the stop on a rejected key, the license, Swift's new ContentFlowError.invalidKey case, Flutter's deleteIdentity throwing on a partial erasure, and the location requirements of on-device geofencing; for 2.4.0, the new X-CF-Session-Id header behind a JavaScript proxy, the new environment value in Swift and Kotlin, Kotlin's new ContentFlowClient members, and the explicit location grant for geofencing in Kotlin and Flutter.

What's new in 2.4.1

Action needed:

Added (all SDKs): block popups in the built-in card: fields #title, #body, #image (https only), #cta_label, #cta_url and #cta_visible (false, or the text "false", hides the button), with the same theme, layout, right-to-left, accessibility, delay and link rules as campaigns, and tracked once per presentation through the block events.

Fixed: a closed in-app message no longer comes back on every sync when analytics consent is not granted: the SDK remembers, on the device, each message the user closed or tapped (8 days, at most 200, cleared on logout, a user change, deleteIdentity() and resetFrequency()). Flutter: every pending in-app message is kept, not one per key.

Changed: JavaScript: the package's sourcemaps no longer embed the TypeScript sources; the package is about half the size.

What's new in 2.4.0

Action needed:

Added (all SDKs):

Fixed: one session id per session in every SDK, sent as X-CF-Session-Id on content reads and as context.sessionId on event batches; it rotates on a cold start, after 30 minutes or more in the background, and on logout(), deleteIdentity() and resetFrequency(), and events keep the session they were queued in. Swift sent no session id with event batches before. Swift keeps events in order after a 429: event flushes run one at a time, so a rate-limited batch is no longer requeued behind newer events. Swift and Kotlin no longer treat a created _cfpk test key as live. Swift, Kotlin and Flutter: getConsentConfig(preset) returns the whole preset (topics, legal framework and the rest), not only the id and required. Kotlin saves a channel grant only once the server confirms it, and keeps a revoke made before identify() until it can be sent.

Changed: every SDK now declares X-CF-SDK-Capabilities: erasure-partial-v1,frequency-v1. It is informational; server-side caps also apply to SDKs before 2.4.0. JavaScript: ScheduleMode is now 'always' | 'payday' | 'ramadan' | 'custom', and BlockSchedule gains tz and precision (type-only).

What's new in 2.3.0

Action needed:

Fixed (all SDKs): retries cover only transport failures, 429 and 5xx, with exponential backoff and jitter (1 s up to 5 minutes), and Retry-After (seconds or an HTTP date, capped at 1 hour) is the minimum wait; other 4xx responses are dropped. Every retry and offline queue holds at most 500 items and drops the oldest, reported at most once a minute. Location is sent at most 100 points per request, oldest first, so a backlog is no longer refused whole. Swift also handles location fixes in order, no longer re-sends delivered location batches, drops refused ones, and keeps an app-set diagnostics level. Flutter reconnects live updates after a drop.

Added (all SDKs):

Changed: every SDK now declares X-CF-SDK-Capabilities: erasure-partial-v1. deleteIdentity() keeps the device id on a partial erasure in every SDK, so the gateway now answers it with 207 and the remaining parts instead of 502. No SDK declares sponsored-surface-v1 unless a JS app opts in with sponsored: true. Swift: CFSecureStorage no longer falls back to an XOR-obfuscated copy in UserDefaults.

Deprecated: getIndustryProfile() in every SDK (returns null without a request); Swift: CFRequestSigner and CFSecureStorage.

What's new in 2.2.2

Action needed:

Fixed: Flutter sync() returns blocks from the live gateway, and Flutter setChannelConsent is accepted. The JS root entry exports consentPathsToConfig and the error classes, and @contentflow/sdk/embed reads its data-cf-* attributes. Kotlin's ContentFlowDiagnostics.setLevel(...) is no longer overwritten by start().

Added: every SDK sends X-CF-SDK-Version: <platform>/<version> (js, react-native, swift, kotlin or flutter, e.g. kotlin/2.2.2) and X-CF-SDK-Capabilities on every request. Capabilities are empty unless a JS app opts in to sponsored content with sponsored: true.

Deprecated (Kotlin): request signing, the offline event queue and secure storage; they never had an effect.

What's new in 2.2.1

Action needed:

Changed: all four SDKs default to https://api.contentflow.click/sdk/v1. Only apps that don't set baseUrl are affected, and the old hosts keep working.

Added: the base URL can be configured without a code change (Info.plist, AndroidManifest meta-data, a JVM system property or environment variable, --dart-define, or a page global on the web). See Configuring the base URL.

What's new in 2.2.0

All four SDKs now share the same core capabilities — keys, consent, content, events, push and location — with platform-appropriate APIs. This is not parity of every capability: certificate pinning and request signing are not available in JavaScript, and location support differs by platform. See Platform support for the current exceptions, and each SDK's CHANGELOG.md for the full list.

All four SDKs

JS

Swift

Kotlin

Flutter


Upgrading from 2.1.0

Breaking / action needed

Broken in 2.1.0, fixed in 2.2.0. If you worked around any of these, remove the workaround:


All versions, in brief

v2.4.1 (2026-10-11)

v2.4.0 (2026-10-10)

v2.3.0 (2026-10-03)

v2.2.2 (2026-10-01)

v2.2.1 (2026-09-30)

v2.2.0 (2026-09-26)

v2.1.0 (2026-09-25)

v2.0.1 (2026-09-24)

v2.0.0 (2026-09-23)

Generated from the ContentFlow repository by sdk-release/scripts/render-docs.mjs. Downloads and checksums are on Releases and downloads. The SDKs are licensed under the ContentFlow SDK License.