Quickstart
Get your first ContentFlow block rendering in a live app in about five minutes. You'll install the SDK, authenticate with a workspace key, sync live blocks, and render one on screen.
1 · Install
$ npm install @contentflow/sdk # or $ yarn add @contentflow/sdk
2 · Initialize
Grab a workspace key from Dashboard → Settings → Developers. The key resolves to your tenant automatically, you never pass a tenant id.
import { ContentFlow } from '@contentflow/sdk' export const cf = new ContentFlow({ key: process.env.CF_KEY, // cf_live_... or cf_test_... env: 'production', })
3 · Sync & render
await cf.sync() // pull live blocks for this tenant const hero = cf.block('home_hero') return <CFBlock block={hero} /> // ✓ renders live content
cf.handshake() confirms your key, tenant, and env resolve before you ship. A failed handshake is the #1 cause of "empty block" bugs.Authentication
Every request is authenticated with a workspace key. A key maps to exactly one tenant (workspace) and one environment. There is no separate tenant id to manage.
| Prefix | Meaning | Use in |
|---|---|---|
cf_live_ | Production key | Live apps |
cf_test_ | Sandbox key | Dev & CI |
Over REST, pass the key as a bearer token:
$ curl https://app.contentflow.click/api/v1/sdk/sync \ -H "Authorization: Bearer cf_live_xxx"
cf_live_ key. In mobile apps, ship the key via your build's secret store, not the repo.Core concepts
- Block type, a UI shape defined in code (banner, card, tile, hero, list, carousel, popup, tooltip, toast, panel). Versioned in git via the CLI.
- Block instance, filled content of a given type, managed in the dashboard or seeded over the API with a stable
externalId. - Sync, the SDK pulls the live set of instances for your tenant. Idempotent and cache-friendly.
- Channel, where a block ships: push, WhatsApp, SMS, popup, or in-app.
- Segment, the audience a block or campaign targets.
See the Wiki glossary for the full vocabulary.
SDK · install & init
Clients are available for Web, React Native, iOS, and Android. All share the same lifecycle: init → sync → render.
| Platform | Package |
|---|---|
| Web / React | @contentflow/sdk |
| React Native | @contentflow/react-native |
| iOS (Swift) | ContentFlow (SPM) |
| Android (Kotlin) | click.contentflow:sdk |
Sync & render
sync() pulls every live instance for the tenant behind your key. The SDK strips unsupported url() wrappers from image values and normalizes field types, so what you render matches what you defined.
await cf.sync() // one block by key cf.block('home_hero') // all instances of a type cf.blocksOfType('card') // subscribe to live updates (no redeploy) cf.on('content.published', () => rerender())
Localization
ContentFlow extracts strings from your blocks, auto-translates them, and delivers only review-approved translations. Nothing half-translated reaches production.
cf.strings(locale), fetch approved strings for a locale (served from/sdk/strings).- Source language shows first in the dashboard; English is shown as reference when a source locale is selected.
- Hidden strings are soft-hidden, kept but never delivered.
/sdk/strings. Draft and machine-only translations stay in the dashboard.CLI · cards-as-code
New block types are defined in code, not the UI. Keep them in your repo, review them in a PR, and ship them with the CLI.
$ npx contentflow login $ npx contentflow init # scaffold blocks/ dir $ npx contentflow push # publish type defs to your tenant ✓ pushed 3 block types
Block types
Every dynamic block picks a design type. The renderer and dashboard badge follow the type automatically.
banner
Full-width strip.
card
Boxed content unit.
tile
Compact grid item.
hero
Large lead unit.
carousel
Swipeable set.
list
Stacked rows.
popup
Modal overlay.
tooltip / toast
Transient hints.
panel
Docked container.
REST API
Base URL https://app.contentflow.click/api/v1. All requests are JSON and authenticated with a bearer key. Below are the most-used endpoints.
Blocks
Strings / Localization
Campaigns
Webhooks
Register an endpoint and subscribe to events to keep your systems in sync with what's live.
| Event | Fires when |
|---|---|
content.published | A block instance goes live |
content.rolledback | A block is reverted |
string.approved | A translation is review-approved |
campaign.sent | A campaign finishes dispatch |