ContentFlow Wiki
The shared brain for building on ContentFlow, concepts, architecture, and the words we use. If the docs tell you how, the wiki tells you why.
Concepts
Blocks, instances, channels, segments.
Architecture
SDK ↔ gateway ↔ services ↔ Mongo.
Glossary
Every term, defined once.
Concepts
Blocks vs. instances
A block type is a UI shape defined in code (a hero, a card, a carousel). A block instance is that shape filled with content, a specific hero on your home screen. Types are shipped by developers with the CLI; instances are managed by anyone in the dashboard.
Channels
A channel is where a block is delivered: push, whatsapp, sms, popup, or in-app. Popup and in-app render as block-style image cards; SMS and WhatsApp run through configured Twilio channels.
Segments & targeting
A segment is an audience. Blocks and campaigns target a segment, a locale, or a single user. The special "All users" segment requires contacts to have phone + consent before SMS/WhatsApp can reach them.
Architecture
ContentFlow runs as a small set of services behind an API gateway. Your app only ever talks to the SDK, which talks to the gateway.
Your app │ @contentflow/sdk (key → tenant) ▼ API Gateway /api/v1 ├──► auth-service workspaces, members, keys ├──► content-service blocks, instances, sync ├──► strings-service extract, translate, deliver └──► campaign-service segments, dispatch │ ▼ MongoDB (per-tenant data)
Content lifecycle
- Define, a developer adds a block type in code and runs
cf push. - Fill, someone creates an instance in the dashboard (or seeds it via
/cards/sync). - Target, pick a segment, locale, and channel.
- Publish, the block goes live;
content.publishedfires. - Sync, apps pull it on next
cf.sync(), no redeploy. - Roll back, revert instantly if needed;
content.rolledbackfires.
Glossary
| Term | Definition |
|---|---|
| Key | Workspace credential (cf_live_ / cf_test_) that resolves to one tenant + env. |
| Tenant | A workspace. All data is scoped to it. |
| Block type | A UI shape defined in code and shipped with the CLI. |
| Block instance | A filled block, managed in the dashboard or seeded via API. |
| externalId | Stable id you set so sync stays idempotent (no duplicates). |
| Handshake | A check that confirms key + tenant + env resolve before shipping. |
| Channel | Delivery surface: push, WhatsApp, SMS, popup, in-app. |
| Segment | An audience a block or campaign targets. |
FAQ
Do I need to redeploy my app to change content?
No. Publishing an instance makes it live on the next cf.sync(). That's the whole point.
Why is my block empty after conversion?
Almost always a failed handshake or a mismatched key/env. Run cf.handshake() and confirm the key prefix matches your environment.
Why didn't my SMS / WhatsApp campaign deliver?
The audience likely had zero contacts with phone + consent, or the tenant has no configured Twilio channel. See Campaigns.
Can non-developers manage content?
Yes. Developers define block types in code; anyone can create and edit instances in the dashboard.