Folloze API
The Folloze API lets you build personalized buyer experiences programmatically — generate boards from templates, sync your content library, and attach the right content to the right account — all driven from your CRM, CMS, or AI pipeline.
The platform exposes two complementary REST APIs, both served from the same host (https://content-api.folloze.com). Most integrations use both:
| API | What it does |
|---|---|
| Board Creation API | Generates Folloze boards from template boards — the structure, sections, widgets, headers, and footers of the page itself. |
| Content API | Creates content items in Folloze from any source (URLs, files, videos, PDFs — the asset doesn't have to exist in Folloze first), and places items on boards with per-board overrides. |
Both APIs share the same base URL, authentication, error model, and conventions described in the next few sections. A single API token issued to your organization unlocks both.
Typical use cases
- Bulk-personalized boards — one board per target account, campaign, or event, populated from your CRM.
- AI / content pipelines — pipe LLM-generated copy and curated assets straight into Folloze.
- CMS sync — mirror an internal content library into the Folloze Content Center.
- 1:1 deal rooms — same source asset, different presentation per opportunity, with no duplicate items in your library.
Which API do I need? #
Pick by what you're trying to ship to a buyer:
- I need a new branded page per account / campaign / event → Board Creation API. You'll start from a template and fill in titles, subtitles, CTAs, and column content.
- I need to bring a new asset into Folloze (a file, a URL, a video — from anywhere, even if it's never been in Folloze before) → Content API —
POST /v1/items. - I need to place an existing Folloze item onto a board → Content API —
POST /v1/board_items, optionally with per-board title / thumbnail overrides. - I need to search or audit items already in Folloze → Content API —
GET /v1/items.
End-to-end example
A typical "generate a personalized board for an account" flow uses both APIs:
GET /v1/board_templates— list templates in your organization.GET /v1/board_templates/:id/describe— read a template's full structure (sections, widgets, header, footer).POST /v1/boards— create a new board from a template.POST /v1/items— create a content item.GET /v1/items— list and search content items.POST /v1/board_items— place a content item on a board.
Board Creation API: overview #
The Board Creation API turns a template board in your organization into the source-of-truth for many personalized boards. You read the template's structure, fill in your content, and post it back to create a new board.
Core endpoints
| Endpoint | Purpose |
|---|---|
GET /v1/board_templates | List all template boards in your organization — useful for discovering template ids, names, and descriptions. |
GET /v1/board_templates/:id/describe | Read a full template structure, including header and footer, in the same shape used for creation. |
POST /v1/boards | Create a new board from a template, with section content (and optionally header/footer). |
PATCH /v1/boards/:board_id | Update an existing board's sections, widgets, slug, and settings. |
Prerequisites #
- Feature enabled. The
board_creation_apifeature must be enabled for your organization. Contact your Folloze account manager if it is not. - API token. An API token issued by Folloze (a “Content Integration Client” token). To get a token, contact your Folloze account representative — tokens are provisioned per organization and cannot be self-served.
- A template board. A board that is defined as a template in your Folloze instance.
Authentication #
All requests are authenticated with your API token in the Authorization header, using the Basic scheme:
Authorization: Basic <your_api_token>
Requests without a valid token return 401. Requests where the API is not enabled in your Folloze organization return 403.
Required headers #
These header rules apply to both the Board Creation API and the Content API.
All POST / PUT requests
Every write call must include these headers in addition to Authorization:
| Header | Value | Why |
|---|---|---|
Content-Type | application/json | Required — bodies are parsed as JSON. |
User-Agent | Folloze | Required — identifies the request as an official Folloze API client. Requests without it may be rejected. |
Folloze-Initiator | Folloze | Required — identifies the integration source so Folloze can route the call through the appropriate ingress path. |
Example
POST https://content-api.folloze.com/v1/items
Authorization: Basic <your_api_token>
Content-Type: application/json
User-Agent: Folloze
Folloze-Initiator: Folloze
{ ... }
User-Agent. Many HTTP clients leave User-Agent off by default. Set it explicitly to Folloze on every write call.
Base URL #
Both the Board Creation API and the Content API are served from the same host:
https://content-api.folloze.com
All endpoint paths in this documentation are relative to this base URL.
Response codes #
| Status | Meaning |
|---|---|
| 200 | Success — payload returned in the response body. |
| 400 | Malformed request (e.g. sections missing or not an array). |
| 401 | Authentication missing or invalid. |
| 403 | The API is not enabled in your Folloze organization. |
| 404 | Template board (or referenced footer_id) not found in the organization. |
| 500 | Unexpected server error. |
Concepts: sections, widgets, ribbons #
A Folloze board is built from sections (e.g. a banner, a content block, a form). Each section contains one or more widgets (the actual content — a banner widget with title / subtitle / cta) and optionally ribbon parts (background and decorative layers).
When you call the API, you describe each section as a JSON object that mirrors the API schema of the widgets that section uses.
API schema descriptors #
Each widget exposes a small, stable contract — only the fields meant for external content are exposed. For example, the Banner widget exposes:
{
"title": "string",
"subtitle": "string",
"cta": { "text": "string" },
"secondary_cta": { "text": "string" }
}
The Promo — Columns widget exposes a repeatable list of columns:
{
"title": "string",
"subtitle": "string",
"cta": { "text": "string" },
"repeatable": {
"columns": [
{
"title": "string",
"subtitle": "string",
"paragraph": "string",
"cta": { "text": "string" },
"visibility": "boolean"
}
]
}
}
Other widgets follow the same pattern (Single Item, Image Section, Side-by-side, Q&A, Multi-tab, Carousel, etc.). Use GET /v1/board_templates/:id/describe to retrieve the exact descriptor for any template.
Section & widget identity #
The API matches sections by section_id — the id you receive from GET /v1/board_templates/:id/describe (stable across calls for the same template).
The same applies to widgets within a section: provide widget_id to update specific widgets; otherwise widgets are matched by their order.
- If you don't include a
section_idfor a section, that section will not be added to the new board. - The order of sections in your
sectionsarray is meaningful — the new board's sections will appear in the same order you send them.
Merge semantics #
- Omitted fields fall back to the template. If you don't include a field in your payload, the API uses the equivalent value from the template. Empty strings, on the other hand, are sent as-is and overwrite the template value.
- Nested objects are merged field-by-field — you only provide the keys you want to change.
- Repeatable arrays (e.g.
repeatable.columns,tabs.items,repeatable.items) — items you don't include in your payload are removed from the new board. Include every item you want to keep. - Sections present in the template but missing from your request are removed from the new board. Headers and footers are always preserved.
Configuring CTAs #
Call-to-action buttons can be configured on any section that exposes a cta or secondary_cta field — headers, banners, body sections, navigation items, and carousel columns. Every CTA follows the same object shape regardless of where it appears.
CTA object
| Field | Type | Description | |
|---|---|---|---|
text | string | required | The button label displayed to visitors. |
type | string | optional | Button style. One of "flz-primary", "flz-secondary", or "flz-link". Navigation items default to "flz-link". |
action | object | optional | What happens when the visitor clicks. Contains a type field plus a matching config key. See action types below. |
Button styles
| Value | Appearance |
|---|---|
flz-primary | Solid, high-emphasis button — the main call to action. |
flz-secondary | Outlined or lower-emphasis button — a supporting action. |
flz-link | Styled as an inline link. Default for header navigation items. |
Action types
The action object always has a type field that selects the behaviour, plus a sibling key of the same name (or item_viewer for open_content_item) containing the type-specific configuration.
action.type | Config key | Fields | Description |
|---|---|---|---|
open_url |
open_url |
url (string), open_in_new_window (bool) |
Navigate to an external or internal URL. Set open_in_new_window: true to open in a new tab. |
form |
form |
form_id (string) |
Open a Folloze form by its id. |
registration |
registration |
form_id (string) |
Open a registration form (same shape as form, but treated as event registration). |
anchor |
anchor |
hash (string) |
Scroll to a named anchor on the same board page. |
contact |
contact |
privacy_message_id (string) |
Open a “contact me” dialog tied to a privacy message. |
message |
message |
privacy_message_id (string) |
Open a “send a message” dialog tied to a privacy message. |
share |
share |
privacy_message_id (string) |
Open the board share dialog tied to a privacy message. |
send_email_clicked |
send_email_clicked |
email (string), subject (string) |
Open the visitor's email client with a pre-filled recipient and subject line. |
open_content_item |
item_viewer |
flz_item_id (integer | object), design (string), allow_download (boolean) |
Open a content item in the viewer overlay. flz_item_id accepts a plain integer or { "content_center_item_id": <id> }. See detailed guide. |
Example: banner with primary and secondary CTA
"cta": {
"text": "Download the report",
"type": "flz-primary",
"action": {
"type": "open_content_item",
"item_viewer": {
"flz_item_id": { "content_center_item_id": 775972 },
"design": "sidebar",
"allow_download": true
}
}
},
"secondary_cta": {
"text": "Schedule a Demo",
"type": "flz-secondary",
"action": {
"type": "open_url",
"open_url": {
"url": "https://example.com/demo",
"open_in_new_window": true
}
}
}
Example: header navigation with mixed actions
"navigation": {
"items": [
{
"text": "About Us",
"type": "flz-link",
"action": {
"type": "anchor",
"anchor": { "hash": "about" }
}
},
{
"text": "Contact",
"type": "flz-link",
"action": {
"type": "form",
"form": { "form_id": "86102" }
}
},
{
"text": "Share",
"type": "flz-link",
"action": {
"type": "share",
"share": { "privacy_message_id": "11869" }
}
}
]
}
Where CTAs appear
| Location | Fields | Notes |
|---|---|---|
| Header widget | cta | Single CTA in the top bar. Controlled by visibility.cta. |
| Header navigation | navigation.items[] | Each item is a CTA object. Typically uses flz-link style. Controlled by visibility.navigation. |
| Banner widget | cta, secondary_cta | Primary and secondary buttons. Controlled by visibility.cta, visibility.secondary_cta, and the parent visibility.showButtons toggle. |
| Body sections | cta, secondary_cta | Supported by text, image, carousel, and other body section widgets. |
| Carousel columns | repeatable.columns[].cta | Per-column CTA. May omit the type field (inherits the column default style). |
true. Always check and set the matching visibility key when adding or enabling a CTA. For banners, the showButtons flag is a parent toggle that must also be true for either button to appear.
Configuring images #
Many widgets expose editable image fields that you can set when creating a board. The API accepts any public image URL or base64-encoded data URI and automatically uploads it to the Folloze CDN (Cloudinary) — no separate upload step is required.
Image data object
Every editable image follows the same shape:
| Field | Type | Description | |
|---|---|---|---|
url | string | required | The image source. Accepts a public URL (https://...), a base64 data URI (data:image/png;base64,...), or an existing Folloze CDN URL. |
alt | string | optional | Alt text for accessibility. |
link | string | optional | Click-through URL — if set, the image becomes a clickable link. |
Example: setting an image on a section
"image": {
"url": "https://cdn.example.com/hero-banner.jpg",
"alt": "Product dashboard screenshot",
"link": "https://example.com/product-tour"
}
Accepted input formats
| Input | What happens |
|---|---|
Public URL (https://cdn.example.com/photo.jpg) | Downloaded and uploaded to Folloze CDN. The response returns the hosted CDN URL. |
Base64 data URI (data:image/png;base64,iVBOR...) | Decoded and uploaded to Folloze CDN. |
Existing Folloze CDN URL (https://images.folloze.com/image/upload/...) | Normalized (version stripped) and passed through — not re-uploaded. |
Response: optimized URL and image config
When you read a board via /describe or /html_sections, image fields come back with additional read-only properties:
| Field | Type | Description |
|---|---|---|
url | string | The canonical CDN URL of the original image. |
optimized_url | string | A Cloudinary-transformed URL with the board designer's image settings (crop, resize, filters) baked in. Use this for display. |
image_config | object | The transformation settings applied to produce the optimized URL. Read-only — set by the board designer in the Folloze UI. |
alt | string | Alt text. |
link | string | Click-through URL. |
image_config fields (read-only)
| Field | Type | Description |
|---|---|---|
maxWidth | number | Maximum display width in pixels. |
maxHeight | number | Maximum display height in pixels. |
flipX | boolean | Flip horizontally. |
flipY | boolean | Flip vertically. |
tint | object | { "color": "#hex", "alpha": 0.5 } — colour overlay. |
crop | object | { "x", "y", "width", "height", "aspect", "radius", "crop", "unit" } — crop region. |
shape | string | One of "square", "rectangle", "circle", or "none". |
artisticFilter | string | Cloudinary artistic filter name. |
sharpness | boolean | Whether sharpening is applied. |
Where images appear
The following widgets expose editable image fields. The exact paths depend on the widget type — always read the /describe response for your template to see the actual structure.
| Widget | Image field paths |
|---|---|
| Image section | image |
| Single item | image |
| Video section | preview_image.image |
| Simulive event / Zoom | views.pre.image, views.during.preview_image.image, views.post.placeholder.image |
| Carousel (promo) | repeatable.columns[].icon |
| Promo columns | repeatable.columns[].icon |
| Running carousel | repeatable.columns[].icon |
| Multi-tab | tabs.items[].icon, tabs.items[].left/right.image.image, tabs.items[].left/right.video.preview_image.image |
| Multi-tab columns | tabs.items[].icon, tabs.items[].columns[].icon |
| Q&A section | repeatable.columns[].icon, repeatable.columns[].side_media.image.image, repeatable.columns[].side_media.video.preview_image.image |
| Side by side | left/right.image.image, left/right.video.preview_image.image |
| Header | primary_logo.image.url, secondary_logo.image.url |
| Ribbon (background) | ribbonParts[].backgroundImage |
/describe response for your specific template to see the authoritative list of editable fields.
Setting items on content sections #
You can specify which content items appear in a section at board-creation time using the item_id field type. The API accepts two value formats for every item_id field:
| Format | Example | When to use |
|---|---|---|
| Plain integer | 774620 | Item already exists on the source template board |
| Content center reference | { "content_center_item_id": 774620 } | Item exists in the org’s Content Center but is not on the template. The API auto-adds it to the new board during creation. |
content_center_item_id object form, the API automatically adds the item to the new board — no separate POST /v1/board_items step needed. The item must exist in your organization’s Content Center; create it first with POST /v1/items if needed.
Supported widgets
| Widget | Field path | Cardinality | Status |
|---|---|---|---|
single-item | sources.flz_item_id | Single item | Supported |
video-section | sources.flz_item_id | Single item | Supported |
side-by-side | left.video.flz_item_id / right.video.flz_item_id | Single item per side | Supported |
multi-tab | tabs[].left.video.flz_item_id / tabs[].right.video.flz_item_id | Single item per tab side | Supported |
qa-section | repeatable.items[].side_media.video.flz_item_id | Single item per Q&A row | Supported |
content-carousel | sources.flz_item_ids | Multiple items (array) | Bug — currently accepts only a single value; array support pending fix |
content-promoted | sources.flz_item_ids | Multiple items (array) | Bug — same as carousel |
Not yet supported
The following content widgets do not expose item sources in their API schema. Items on these sections can only be set through the Folloze UI or via POST /v1/board_items after board creation.
content-tab/content-tab-2content-spread/content-spread-2
Example: single-item widget
"widgets": {
"single-item": {
"title": "Featured case study",
"subtitle": "See how Acme Corp transformed their pipeline",
"sources": {
"flz_item_id": { "content_center_item_id": 774620 }
}
}
}
Example: side-by-side with video item
"widgets": {
"side-by-side": {
"title": "Product overview",
"left": {
"paragraph": { "title": "...", "subtitle": "..." },
"video": {
"preview_image": { "image": { "url": "https://..." } },
"flz_item_id": { "content_center_item_id": 775972 }
}
},
"right": {
"image": { "image": { "url": "https://..." } },
"paragraph": { "title": "...", "subtitle": "..." }
}
}
}
CTA: open a content item #
The open_content_item action type on any CTA opens a content item in the board’s item viewer. The flz_item_id inside item_viewer accepts the same two formats as the sources fields — plain integer or content_center_item_id object.
CTA object with open_content_item
"cta": {
"text": "View case study",
"type": "flz-primary",
"action": {
"type": "open_content_item",
"item_viewer": {
"flz_item_id": { "content_center_item_id": 775972 },
"design": "sidebar",
"allow_download": true
}
}
}
| Field | Type | Description |
|---|---|---|
item_viewer.flz_item_id | integer | object | The content item to open. Pass a plain integer (existing board item) or { "content_center_item_id": <id> } (auto-added from Content Center). |
item_viewer.design | string | Optional. Viewer layout style (e.g. "sidebar"). |
item_viewer.allow_download | boolean | Optional. Whether the visitor can download the content item. |
This action is available on every CTA across all widget types — banners, split banners, side-by-side, promo columns, Q&A, single items, etc. See Configuring CTAs for the full list of action types.
Event sections (Zoom / Simulive) #
Zoom and Simulive event sections let you embed a live or pre-recorded video experience directly into a board — with scheduling, state-specific images, and a registration CTA. Both widgets share the same schema; they differ only in the video source field.
Zoom vs. Simulive
| Widget key | Source field | Value | Use case |
|---|---|---|---|
zoom | sources.meetingUrl | Zoom meeting or webinar URL | Live meetings & webinars |
simulive-event | sources.videoUrl | Pre-recorded video URL | On-demand / simulated-live playback at a scheduled time |
Widget fields
| Field | Type | Description |
|---|---|---|
name | string | Structured event name — displayed consistently across pre/during/post views. Separate from the rich-text title. |
title | string | Event headline (Quill HTML) |
subtitle | string | Event description (Quill HTML) |
sources.meetingUrl | string | Zoom only. Full Zoom meeting/webinar URL |
sources.videoUrl | string | Simulive only. Pre-recorded video URL |
scheduling.startTime | string (ISO 8601) | Event start time, e.g. "2026-07-15T14:00:00Z" |
scheduling.endTime | string (ISO 8601) | Event end time, e.g. "2026-07-15T15:00:00Z" |
scheduling.timezone | string (IANA) | Timezone for displaying event times to the viewer. Must be one of the supported values. |
cta | object | Registration / action button — see Configuring CTAs |
views.pre.image | image object | Banner shown before the event starts |
views.during.preview_image.image | image object | Preview image shown during the event (behind the video player) |
views.post.placeholder.image | image object | Image shown after the event ends |
"2026-07-15T14:00:00Z") and converts them internally to millisecond timestamps. When you read the widget back via the Describe endpoint, you will see ISO strings. Set scheduling.timezone to control how the event time is rendered for visitors.
Supported timezones
The scheduling.timezone field accepts the following IANA timezone identifiers:
Pacific/Honolulu, America/Anchorage, America/Los_Angeles, America/Denver, America/Chicago, America/New_York, America/Halifax, Europe/London, Europe/Berlin, Europe/Athens, Europe/Moscow, Asia/Jerusalem, Australia/Perth, Australia/Adelaide, Australia/Sydney
Image objects
Each image slot (views.pre.image, views.during.preview_image.image, views.post.placeholder.image) accepts:
| Field | Type | Description |
|---|---|---|
url | string | Image URL (Folloze CDN or any public URL) |
alt | string | Alt text for accessibility |
link | string | Optional click-through URL |
Full example: Zoom event section
{
"section_id": "s_abc12345",
"widgets": {
"zoom": {
"name": "OT Security for Manufacturing",
"title": "Live webinar: OT security for manufacturing",
"subtitle": "Join our experts for a deep dive into securing operational technology",
"sources": {
"meetingUrl": "https://zoom.us/j/1234567890"
},
"scheduling": {
"startTime": "2026-07-15T14:00:00Z",
"endTime": "2026-07-15T15:00:00Z",
"timezone": "America/New_York"
},
"cta": {
"text": "Register now",
"type": "flz-primary",
"action": {
"type": "form",
"form": { "form_id": "172817" }
}
},
"views": {
"pre": {
"image": {
"url": "https://images.folloze.com/image/upload/v.../pre-event-banner.jpg",
"alt": "Webinar pre-event banner"
}
},
"during": {
"preview_image": {
"image": {
"url": "https://images.folloze.com/image/upload/v.../during-preview.jpg",
"alt": "Webinar in progress"
}
}
},
"post": {
"placeholder": {
"image": {
"url": "https://images.folloze.com/image/upload/v.../post-event-thanks.jpg",
"alt": "Thank you for attending"
}
}
}
}
}
},
"ribbonParts": []
}
Full example: Simulive event section
{
"section_id": "s_def67890",
"widgets": {
"simulive-event": {
"name": "Platform Walkthrough",
"title": "On-demand demo: Platform walkthrough",
"subtitle": "Watch our 30-minute product tour at the scheduled time",
"sources": {
"videoUrl": "https://videos.example.com/demo-recording.mp4"
},
"scheduling": {
"startTime": "2026-08-01T10:00:00Z",
"endTime": "2026-08-01T10:30:00Z",
"timezone": "Europe/London"
},
"cta": {
"text": "Reserve your spot",
"type": "flz-primary",
"action": {
"type": "form",
"form": { "form_id": "183920" }
}
},
"views": {
"pre": {
"image": { "url": "https://images.folloze.com/image/upload/v.../simulive-pre.jpg" }
},
"during": {
"preview_image": {
"image": { "url": "https://images.folloze.com/image/upload/v.../simulive-during.jpg" }
}
},
"post": {
"placeholder": {
"image": { "url": "https://images.folloze.com/image/upload/v.../simulive-post.jpg" }
}
}
}
}
},
"ribbonParts": []
}
cta on event sections is commonly set to "form" action type to capture registrations. You can also use "open_url" to link to an external registration page, or "send_email_clicked" for direct contact.
Organization media library #
Browse your organization's media library to find image URLs for board creation payloads — logos, banners, backgrounds, icons, and more. This endpoint uses the same Basic auth and base URL as the rest of the Board Creation API.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
category[] | string (repeatable) | No | Filter by one or more categories. Repeat the parameter for multiple: category[]=logos&category[]=banners |
sort_col | string | No | Column to sort by (e.g. "created_at", "name") |
sort_direction | string | No | "asc" or "desc" |
page | integer | No | Page number (default: 1) |
per_page | integer | No | Results per page (default: 20) |
Image categories
| Category value | Description |
|---|---|
banners | Banner / hero images |
mobile_banners | Mobile-optimized banner images |
thumbnails | Thumbnail images |
icons | Icons and small graphics |
logos | Company and brand logos |
videos | Video thumbnails / poster images |
images | General-purpose images |
avatars | Profile / avatar images |
Example request
curl -s "https://content-api.folloze.com/v1/organization_media?category[]=logos&category[]=banners&per_page=10&page=1&sort_col=created_at&sort_direction=desc" \
-H "Authorization: Basic $BASE64_CREDENTIALS"
Example: fetch all images (no category filter)
curl -s "https://content-api.folloze.com/v1/organization_media?per_page=100&page=1" \
-H "Authorization: Basic $BASE64_CREDENTIALS"
Example response
{
"media": [
{
"id": 170092,
"name": "Cloud Network Logo",
"url": "https://images.folloze.com/image/upload/v1770730346/kpo6sywpsdpcqwi8esdq.png",
"category": "logos",
"created_at": "2026-03-08T13:35:19.119Z",
"updated_at": "2026-03-08T13:35:19.119Z",
"uploader": null
},
{
"id": 169988,
"name": "Banner Image",
"url": "https://images.folloze.com/image/upload/v1759827103/qqtposangcjdxfymv72k.jpg",
"category": "banners",
"created_at": "2026-03-08T13:33:18.807Z",
"updated_at": "2026-03-08T13:33:18.807Z",
"uploader": null
}
],
"pagination": {
"total_items": 47,
"total_pages": 24,
"current_page": 1,
"per_page": 2
}
}
Response fields
| Field | Type | Description |
|---|---|---|
media | array | Array of image objects |
media[].id | integer | Image ID |
media[].name | string | Image name |
media[].url | string | Full CDN URL — use directly in any image field on your board payload |
media[].category | string | Image category (e.g. "logos", "banners") |
media[].created_at | string (ISO 8601) | Upload timestamp |
media[].updated_at | string (ISO 8601) | Last update timestamp |
media[].uploader | string | null | Full name of the user who uploaded the image |
pagination.total_items | integer | Total number of images matching the query |
pagination.total_pages | integer | Total number of pages |
pagination.current_page | integer | Current page number |
pagination.per_page | integer | Results per page |
List templates #
Returns every template board in your organization with its id, name, and description. Use this to discover which templates are available before fetching schemas or creating boards — especially helpful when you don't already know the template ids.
Example request
curl https://content-api.folloze.com/v1/board_templates \
-H "Authorization: Basic $FOLLOZE_API_TOKEN"const res = await fetch(
"https://content-api.folloze.com/v1/board_templates",
{ headers: { Authorization: `Basic ${FOLLOZE_API_TOKEN}` } }
);
const templates = await res.json();import requests
res = requests.get(
"https://content-api.folloze.com/v1/board_templates",
headers={"Authorization": f"Basic {FOLLOZE_API_TOKEN}"},
)
templates = res.json()Example response
[
{
"id": 178297,
"name": "CSM: Account Resource Center",
"description": "A customer-success hub for sharing onboarding guides, success plans, and key contacts with each account. Use as the home base for ongoing customer engagement. #csm #customer-success"
},
{
"id": 155459,
"name": "[Template] Digital Sales Room",
"description": "Digital Sales room for Folloze - \n\nHow to use:\n\nWithin the content section there are categories labelled \"Hidden\" where you an pull in the most used content straight into the actual categories. \n\n #Template #sales"
},
{
"id": 165922,
"name": "[Template] GeneratorAI",
"description": "Pre-wired template for boards generated by AI pipelines — every section's titles, subtitles, and CTAs are designed to be filled programmatically. Pair with a content pipeline that calls POST /v1/boards. #generative #template"
},
{
"id": 152644,
"name": "[BuyEx 3.0 Template] ABM 1:1",
"description": "1:1 personalized buyer experience for ABM plays — branded for a single target account, with sections for tailored value props, case studies, and a curated content library. #abm #buyex"
}
]
Response fields
| Field | Type | Description |
|---|---|---|
id | integer | Template id — pass this as template_board_id on POST /v1/boards, or as the :id path parameter on GET /v1/board_templates/:id/describe. |
name | string | Display name of the template as set by the template owner in Folloze. |
description | string | Template description (may be empty). Often used to capture intent, instructions, or hashtags — useful for filtering templates client-side. |
Errors
| Status | When |
|---|---|
| 401 | Authentication is missing or invalid. |
| 403 | Feature board_creation_api is disabled for the organization. |
Describe template / board #
Returns the full structure of a template or any existing board: every body section (with the API schema of its widgets pre-populated with the current content), plus the header and footer, all in the format used for creation. Useful for “round-tripping” — read it, modify it, and post it back to create a new board.
/v1/board_templates/:id/describe for template boards, or /v1/boards/:id/describe for any board (including non-templates). The response format is identical — the only difference is the source.
The response is keyed by page. Every board has a main_page. Boards that include a registration page also have a registration_page key with its own sections, header, and footer.
Example response
{
"main_page": {
"header": {
"section_id": "s_f316586e",
"type": "header",
"name": "Header - original POC",
"widgets": {
"header": {
"primary_logo": {
"image": {
"url": "https://images.folloze.com/image/upload/v1759506309/vuo2cfwtclkhewhd5ryb.png"
}
},
"secondary_logo": {
"image": {
"url": "https://images.folloze.com/image/upload/v1776690426/aqmsxd15kdpeoj3pm3vi.svg"
}
},
"cta": {
"text": "Contact Us",
"type": "flz-secondary",
"action": {
"type": "open_url",
"open_url": {
"url": ""
}
}
},
"color": {
"tag_line": "#000000"
},
"visibility": {
"primary_logo": true,
"show_logos": true,
"show_symbols": false,
"secondary_logo": true,
"tag_line": false,
"cta": false,
"share_button": false,
"navigation": false
},
"navigation": {}
},
"banner": {
"title": "<div style=\"--fz-marker-color: var(--fz-color-neutral-0); text-align: center;\" class=\"ql-style-heading2\"><span style=\"color: var(--fz-color-primary-1);\">Global. Simple. Unified. </span></div><div style=\"--fz-marker-color: var(--fz-color-neutral-0); text-align: center;\" class=\"ql-style-heading2\"><span style=\"color: var(--fz-color-primary-1);\">It’s the Power of One</span></div><div style=\"--fz-marker-color: var(--fz-color-neutral-0); text-align: center;\" class=\"ql-style-heading3\"><span style=\"text-align: left;\"><br></span></div><div style=\"--fz-marker-color: var(--fz-color-neutral-0); text-align: center;\" class=\"ql-style-heading3\"><span style=\"text-align: left; color: var(--fz-color-primary-1);\">How JLL will empower </span></div><div style=\"--fz-marker-color: var(--fz-color-neutral-0); text-align: center;\" class=\"ql-style-heading3\"><span style=\"text-align: left; color: var(--fz-color-primary-1);\">[insert target’s sector]</span><span style=\"text-align: left;\"> </span></div><div style=\"--fz-marker-color: var(--fz-color-neutral-0); text-align: center;\" class=\"ql-style-heading3\"><span style=\"color: var(--fz-color-primary-1);\"><br></span></div><div style=\"--fz-marker-color: var(--fz-color-neutral-0); text-align: center;\" class=\"ql-style-heading3\"><br></div>",
"subtitle": "<div style=\"text-align: center;\" class=\"ql-style-heading4\"><span style=\"color: var(--fz-color-primary-1);\">Enhance productivity, services and employee experiences with data center and workspace management solutions from JLL.</span></div>",
"cta": {
"text": "Read your business case now",
"type": "flz-primary",
"action": {
"type": "open_content_item",
"item_viewer": {}
}
},
"secondary_cta": {
"text": "Schedule a Demo",
"type": "flz-secondary",
"action": {
"type": "open_url",
"open_url": {
"url": "",
"open_in_new_window": true
}
}
}
}
},
"ribbonParts": []
},
"sections": [ /* array of section objects */ ],
"footer_id": 789
},
"registration_page": {
"sections": [
{
"section_id": "s_reg001",
"type": "body",
"name": "Registration",
"widgets": { /* widget schema with current content */ },
"ribbonParts": []
}
]
}
}
registration_page key only appears if the template/board has a registration page configured. Boards without one return only main_page.
widgets keys, fields, and visibility flags depend on the header type your template uses. Always read the actual /describe response for your template and round-trip what you receive (including the name, the full visibility map, and any rich-text HTML in titles/subtitles) when posting to POST /v1/boards.
Variation: header with two logos and inline navigation
Different header types return different widget keys and fields. For example, a template using the Header with 2 logos type returns a widgets.header with a populated navigation.items array (instead of an empty navigation: {}) and no banner widget at all:
{
"main_page": {
"header": {
"section_id": "s_27da2d1a",
"type": "header",
"name": "Header with 2 logos",
"widgets": {
"header": {
"primary_logo": {
"image": {
"url": "https://images.folloze.com/image/upload/v1750030731/d7ajbarxztndszv8ywi8.png"
}
},
"secondary_logo": {
"image": {
"url": "",
"link": "",
"alt": ""
}
},
"cta": {
"text": "Contact Us",
"type": "flz-secondary",
"action": {
"type": "open_url",
"open_url": {
"url": ""
}
}
},
"color": {
"tag_line": "#000000",
"symbol": "var(--fz-color-neutral-4)"
},
"visibility": {
"primary_logo": true,
"show_logos": true,
"show_symbols": true,
"secondary_logo": false,
"tag_line": false,
"cta": false,
"share_button": false,
"navigation": true
},
"navigation": {
"items": [
{ "text": "Solutions", "type": "flz-link", "action": { "type": "open_url", "open_url": {} } },
{ "text": "Why us", "type": "flz-link", "action": { "type": "open_url", "open_url": {} } },
{ "text": "Success stories", "type": "flz-link", "action": { "type": "open_url", "open_url": {} } },
{ "text": "Product tour", "type": "flz-link", "action": { "type": "open_url", "open_url": {} } },
{ "text": "Resources", "type": "flz-link", "action": { "type": "open_url", "open_url": {} } }
]
}
}
},
"ribbonParts": []
}
}
}
Search boards #
Search and list boards in your organization. Supports filtering by name, status, and owner — based on the same search functionality available in the Folloze UI.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
search | string | No | Search boards by name (partial match) |
activation_state | integer | No | Filter by board status: 1 = online, 0 = draft |
owner | integer | No | Filter by owner user id |
page | integer | No | Page number (default: 1) |
per_page | integer | No | Results per page (default: 50) |
Example request
curl -s "https://content-api.folloze.com/v1/boards?search=Acme&activation_state=1&page=1&per_page=10" \
-H "Authorization: Basic $BASE64_CREDENTIALS"
Example response
{
"boards": [
{
"id": 247054,
"name": "Personalized Hub for Acme Corp",
"description": null,
"status": "Online",
"board_url": "https://acme.boards.folloze.com/eee17a",
"image_url": "https://images.folloze.com/image/upload/v.../hero.png",
"owner": {
"id": 1999626,
"full_name": "Jane Smith",
"email": "jane@acme.com"
},
"hashtags": [],
"created_at": "2026-07-06T08:27:35.511Z",
"updated_at": "2026-07-06T08:28:12.797Z"
}
],
"pagination": {
"total_items": 1,
"total_pages": 1,
"current_page": 1,
"per_page": 10
}
}
Response fields
| Field | Type | Description |
|---|---|---|
boards | array | Array of board objects |
boards[].id | integer | Board id — use with GET /v1/boards/:id/describe or source_board_id on POST /v1/boards |
boards[].name | string | Board display name |
boards[].description | string | null | Board description |
boards[].status | string | "Online" or "Draft" |
boards[].board_url | string | Public board URL |
boards[].image_url | string | Board thumbnail / hero image URL |
boards[].owner | object | Board owner with id, full_name, and email |
boards[].hashtags | string[] | Board tags |
boards[].created_at | string (ISO 8601) | Creation timestamp |
boards[].updated_at | string (ISO 8601) | Last update timestamp |
pagination.total_items | integer | Total boards matching the query |
pagination.total_pages | integer | Total pages |
pagination.current_page | integer | Current page |
pagination.per_page | integer | Results per page |
Create a board #
Creates a new board in the organization, copying the source board's structure and applying your content. The source can be a template board (template_board_id) or any existing board (source_board_id).
Request body
| Field | Type | Description | |
|---|---|---|---|
template_board_id | number | required* | Id of the template board to use. Required unless source_board_id is provided. |
source_board_id | number | required* | Id of any existing board (non-template) to use as the source. Use GET /v1/boards/:id/describe to inspect its structure first. Required unless template_board_id is provided. |
main_page | object | required* | Main page content. Contains sections, header, and footer_id (see page object). Required unless top-level sections is provided. |
registration_page | object | optional | Registration page content. Same shape as main_page (page object). Only valid when the template/source board has a registration page — returns 422 otherwise. |
sections | array | required* | Legacy format. Array of section objects for the main page. Equivalent to main_page.sections. Required unless main_page is provided. |
board_name | string | optional | Display name. Defaults to "Board from {template name}". |
header | object | optional | Legacy format. Header overrides for the main page. Equivalent to main_page.header. The shape depends on the header type your template uses — fetch /describe to see the exact structure. |
footer_id | number | optional | Legacy format. Footer id for the main page. Equivalent to main_page.footer_id. |
owner_email | string | optional | Email of an existing Folloze user to assign as the board owner. Must match a user in the organization. If omitted, the board is owned by the first admin of the organization. |
tags | string[] | optional | Board tags to apply. New tags are created on demand; existing tags are reused. |
state | string | optional | Initial board state. Accepted values: "online" (published) or "draft". Defaults to "draft". |
channels | array | optional | Channels to add the new board to. Each entry is { "name": "..." } — matched by exact name (case-sensitive, no whitespace trimming); the board joins the existing channel if a match is found, otherwise a new channel is created with this name. See Channels. |
slug | string | optional | Vanity URL slug for the board. The board will be accessible at https://<domain>.boards.folloze.com/<slug>. Must be unique across the organization. Returns 422 if the slug is already taken. |
settings | object | optional | Board settings. Currently supports access for access control configuration. See Access settings. |
sections, header, and footer_id at the top level (legacy format) or inside a main_page object. Both are equivalent for the main page. Use main_page + registration_page when your template has a registration page and you want to customize it.
Page object
| Field | Type | Description | |
|---|---|---|---|
sections | array | required | Array of section objects. Sections in the template page but missing here are removed from the new board's page. |
header | object | optional | Header overrides for this page. Shape depends on the header type — fetch /describe to see the exact structure. |
footer_id | number | optional | Id of an existing footer CampaignElement in the organization. |
Section object
| Field | Type | Description | |
|---|---|---|---|
section_id | string | required | Id from GET /describe. Sections without a section_id are not added to the new board. |
type | string | optional | Section type, typically "body". Header / footer are managed via header and footer_id, not here. |
ribbonParts | array | optional | Ribbon overrides; usually pass through what you got from GET /describe. |
Channels
Pass each channel as { "name": "…" }. The name is matched exactly (case-sensitive, no whitespace trimming) against existing channels in the organization — the board joins the existing channel if a match is found; otherwise a new channel is created with this name.
{
"channels": [
{ "name": "Q4 launch" },
{ "name": "Acme account" }
]
}
| Field | Type | Description |
|---|---|---|
name | string | Channel name. Matched exactly against existing channels; created if no match. |
Access settings
The settings.access object controls who can view the board. Four access types are supported:
| Type | Description |
|---|---|
open_to_all | Anyone with the link can view. Optionally enable token-based access. |
password | Visitors must enter a password to view the board. |
email | Visitors must verify their email address via a one-time code. |
2fa | Two-factor authentication — same shape as email but with stricter verification. |
Open to all (default):
{
"settings": {
"access": {
"type": "open_to_all",
"token_enabled": true
}
}
}
Password-protected:
{
"settings": {
"access": {
"type": "password",
"password": "s3cret",
"re_authenticate_per_session": true
}
}
}
Email verification:
{
"settings": {
"access": {
"type": "email",
"include_recipients": true,
"domain_filter_enabled": true,
"allowed_domains": ["acme.com", "partner.co"],
"blocked_domain_filter_enabled": false,
"blocked_domains": [],
"privacy_message_id": 123,
"re_authenticate_per_session": false,
"code_expiration_seconds": 900
}
}
}
| Field | Type | Applies to | Description |
|---|---|---|---|
type | string | all | Access type: "open_to_all", "password", "email", or "2fa". |
token_enabled | boolean | open_to_all | Enable token-based access for the board. |
password | string | password | The password visitors must enter. |
re_authenticate_per_session | boolean | password, email, 2fa | If true, visitors must re-authenticate each browser session. |
re_authenticate_after_days | number | password | Alternative to re_authenticate_per_session — re-authenticate after this many days. |
include_recipients | boolean | email, 2fa | Include email recipients in board analytics. |
domain_filter_enabled | boolean | email, 2fa | Enable allowed-domain filtering. |
allowed_domains | string[] | email, 2fa | Email domains permitted to access the board (when domain_filter_enabled is true). |
blocked_domain_filter_enabled | boolean | email, 2fa | Enable blocked-domain filtering. |
blocked_domains | string[] | email, 2fa | Email domains blocked from accessing the board. |
privacy_message_id | number | email, 2fa | Id of a privacy message to display during email verification. |
code_expiration_seconds | number | email, 2fa | Verification code expiration time in seconds. |
settings.access returns 422 with "Board access management is not enabled for this organization".
Example response
{
"board_id": 12345,
"board_url": "https://acme.boards.folloze.com/my-vanity-slug",
"slug": "my-vanity-slug",
"main_page": {
"header": { "section_id": "s_f316586e", "type": "header", "name": "Header", "widgets": { ... } },
"sections": [
{ "section_id": "s_abc123", "type": "body", "name": "Single Item", "widgets": { ... }, "ribbonParts": [] }
],
"footer_id": 789
},
"registration_page": {
"sections": [
{ "section_id": "s_reg001", "type": "body", "name": "Registration", "widgets": { ... }, "ribbonParts": [] }
]
}
}
registration_page key appears in the response whenever the template/source board has a registration page — even if you did not send a registration_page in the request. It reflects the registration page as-is from the template.
Errors
| Status | When |
|---|---|
| 400 | sections is missing or not an array. |
| 401 | Authentication is missing or invalid. |
| 403 | Feature board_creation_api is disabled for the organization. |
| 404 | template_board_id not found, or footer_id does not exist in the organization. |
| 422 | registration_page was provided but the template/source board has no registration page. |
| 422 | slug is already taken: "The vanity URL \"{value}\" is occupied. Try again with a different value". |
| 422 | settings.access was provided but the board access management feature is not enabled for the organization. |
null as a leaf value in a widget field clears that field, overriding the template's value. This applies to all widget fields across create and update operations.
Update a board #
Updates an existing board's sections, widgets, slug, and settings. Accepts the same page structure as POST /v1/boards — main_page, registration_page, or legacy top-level sections. The board must have a published configuration (i.e. it must have been published at least once).
Path parameters
| Name | Type | Description | |
|---|---|---|---|
board_id | number | required | Id of the board to update. |
Request body
| Field | Type | Description | |
|---|---|---|---|
main_page | object | optional | Main page content overrides. Same shape as on create (page object). |
registration_page | object | optional | Registration page content overrides (page object). Only valid if the board has a registration page. |
sections | array | optional | Legacy format. Main page section overrides. Equivalent to main_page.sections. |
board_name | string | optional | Update the board's display name. |
header | object | optional | Legacy format. Header overrides for the main page. |
footer_id | number | optional | Legacy format. Footer id for the main page. |
owner_email | string | optional | Reassign the board to a different owner by email. |
tags | string[] | optional | Board tags to apply. |
state | string | optional | Board state: "online" or "draft". If "online", the board is published after the update. |
channels | array | optional | Channels to add the board to. See Channels. |
slug | string | optional | Update the board's vanity URL slug. |
settings | object | optional | Board settings. See Access settings. |
Example request
PATCH /v1/boards/12345
Content-Type: application/json
Authorization: Basic <your_api_token>
{
"board_name": "Updated Hub for Acme Corp",
"slug": "acme-hub-q4",
"main_page": {
"sections": [
{
"section_id": "s_abc123",
"widgets": {
"banner": {
"title": "<p>Welcome to Acme, updated!</p>",
"subtitle": "<p>Your personalized resource hub</p>"
}
}
}
]
},
"settings": {
"access": {
"type": "password",
"password": "acme2026",
"re_authenticate_per_session": true
}
},
"state": "online"
}
Example response
{
"board_id": 12345,
"board_url": "https://acme.boards.folloze.com/acme-hub-q4",
"slug": "acme-hub-q4",
"main_page": {
"header": { "section_id": "s_f316586e", "type": "header", "name": "Header", "widgets": { ... } },
"sections": [
{ "section_id": "s_abc123", "type": "body", "name": "Banner", "widgets": { ... }, "ribbonParts": [] }
],
"footer_id": 789
}
}
Errors
| Status | When |
|---|---|
| 401 | Authentication is missing or invalid. |
| 403 | Feature board_creation_api is disabled for the organization. |
| 404 | Board not found in the organization. |
| 422 | Board has no published configuration (must be published at least once before updating). |
| 422 | slug is already taken. |
| 422 | settings.access was provided but the access management feature is not enabled. |
Publish board #
Publishes a board's unpublished changes, making them live. By default the board is also set to online state; pass go_online=false to publish the configuration without going live.
Path parameters
| Name | Type | Description | |
|---|---|---|---|
id | number | required | Id of the board to publish. |
Query / body parameters
| Name | Type | Description | |
|---|---|---|---|
go_online | boolean | optional | Set the board to online after publishing. Defaults to true. Pass false to publish the configuration without making the board publicly reachable. |
Example request
POST /v1/boards/243672/publish
Authorization: Basic <your_api_token>
Example response
{
"board_id": 243672,
"board_url": "https://acme.boards.folloze.com/q3-acme",
"published_at": "2026-05-18T06:12:43Z",
"online": true
}
Errors
| Status | When |
|---|---|
| 208 | No unpublished changes to publish. Response: { "board_id": ..., "message": "No unpublished changes" }. |
| 400 | Publish failed (e.g. invalid board configuration). |
| 401 | Authentication is missing or invalid. |
| 403 | Feature board_creation_api is disabled for the organization. |
| 404 | Board not found in the organization. |
List HTML sections #
Returns every editable HTML field in a board's unpublished configuration. Use this to discover which fields can be read or updated via the HTML content endpoint.
Path parameters
| Name | Type | Description | |
|---|---|---|---|
board_id | number | required | Id of the board. |
Example request
GET /v1/boards/243672/html_sections
Authorization: Basic <your_api_token>
Example response
{
"html_sections": [
{
"section_id": "s_1b61c01c",
"widget_tag": "html-section",
"field": "content",
"url": "/v1/boards/243672/html_sections/s_1b61c01c/html-section/content"
},
{
"section_id": "s_f316586e",
"widget_tag": "html-header",
"field": "content",
"url": "/v1/boards/243672/html_sections/s_f316586e/html-header/content"
}
]
}
{ "html_sections": [] }.
Response fields
| Field | Type | Description |
|---|---|---|
section_id | string | The section containing this HTML field. |
widget_tag | string | The widget type within the section (e.g. html-section, html-header, html-footer). |
field | string | The field name (typically content). |
url | string | Convenience URL for the GET/PUT HTML content endpoint for this field. |
Errors
| Status | When |
|---|---|
| 401 | Authentication is missing or invalid. |
| 403 | Feature board_creation_api is disabled for the organization. |
| 404 | Board not found in the organization. |
| 422 | Board has no configuration. |
Get / update HTML content #
Read or replace the raw HTML content of a single field in a board's configuration. Both verbs use text/html — not JSON — for the content body.
Path parameters
| Name | Type | Description | |
|---|---|---|---|
board_id | number | required | Id of the board. |
section_id | string | required | Section id (from List HTML sections). |
widget_tag | string | required | Widget type tag (e.g. html-section). |
field | string | required | Field name (typically content). |
GET — read HTML content
Returns the raw HTML of the field. By default reads from the unpublished configuration; pass the ?published query parameter to read the published version instead.
GET /v1/boards/243672/html_sections/s_1b61c01c/html-section/content
Authorization: Basic <your_api_token>
Response: Content-Type: text/html; charset=utf-8
<div class="ql-style-heading4">
<strong>Your platform isn't truly modern</strong>
</div>
PUT — update HTML content
Replaces the HTML content in the board's unpublished configuration. The request body is raw HTML — not a JSON object.
Content-Type must be text/html. The server returns 415 if you send any other content type. Do not wrap the HTML in a JSON string.
PUT /v1/boards/243672/html_sections/s_1b61c01c/html-section/content
Authorization: Basic <your_api_token>
Content-Type: text/html
<div class="ql-style-heading4">
<strong>Updated headline copy</strong>
</div>
Response: 200 OK with Content-Type: text/html; charset=utf-8, echoing back the written HTML.
Typical workflow
- Create a board via
POST /v1/boards(the board is created as a draft with unpublished config). - Call
GET /v1/boards/:id/html_sectionsto discover which HTML fields exist. - Use
PUTon each field to inject your custom HTML content. - Call
POST /v1/boards/:id/publishto push the changes live.
Errors
| Status | When |
|---|---|
| 401 | Authentication is missing or invalid. |
| 403 | Feature board_creation_api is disabled for the organization. |
| 404 | Board, section, widget, or field not found. |
| 415 | Content-Type is not text/html (PUT only). |
| 422 | Board has no unpublished configuration to update (PUT only). |
End-to-end walkthrough #
A common workflow has four steps: pick a template, read its schema, build a payload, create the board.
Step 1 — Pick a template
GET /v1/board_templates
Authorization: Basic <your_api_token>
Returns every template board in your organization with its id, name, and description. Pick the template id you want to base the new board on (e.g. 4321). If you already know the id (e.g. it's stored in your config), you can skip this step.
Step 2 — Read the template's schema
GET /v1/board_templates/4321/describe
Authorization: Basic <your_api_token>
Save the response — it tells you which sections exist, which widgets they contain, what fields each widget exposes, and the header / footer to round-trip back to POST /v1/boards.
Step 3 — Build your payload
Take the response, replace the placeholder text with your real content, drop sections you don't want, and add your header.
{
"template_board_id": 4321,
"board_name": "Q3 Campaign — Acme Corp",
"owner_email": "rep@acme.com",
"tags": ["acme", "q3-campaign"],
"state": "online",
"channels": [
{ "name": "Q3 Campaigns" }
],
"header": {
"primary_logo": { "url": "https://cdn.example.com/acme.png", "link": "https://acme.com", "alt": "Acme" },
"tagline": "Built for modern teams",
"navigation": {
"items": [
{ "text": "Solutions", "type": "flz-link", "action": { "type": "open_url", "open_url": { "url": "https://acme.com/solutions", "open_in_new_window": true } } },
{ "text": "Pricing", "type": "flz-link", "action": { "type": "open_url", "open_url": { "url": "https://acme.com/pricing", "open_in_new_window": true } } }
]
},
"cta": {
"text": "Talk to sales",
"type": "flz-primary",
"action": { "type": "form", "form": { "form_id": "86101" } }
}
},
"footer_id": 789,
"sections": [
{
"section_id": "s_abc123",
"name": "Banner - classic",
"widgets": [
{
"widget_id": "w_111",
"title": "Acme x Folloze",
"subtitle": "Personalized buyer experiences at scale",
"cta": {
"text": "Get started",
"type": "flz-primary",
"action": { "type": "open_content_item", "item_viewer": {} }
},
"secondary_cta": {
"text": "Watch demo",
"type": "flz-secondary",
"action": { "type": "open_url", "open_url": { "url": "https://acme.com/demo", "open_in_new_window": true } }
}
}
],
"ribbonParts": []
},
{
"section_id": "s_def456",
"name": "Columns - Value proposition 4 columns",
"widgets": [
{
"widget_id": "w_222",
"title": "Why Acme",
"subtitle": "Three reasons teams choose us",
"cta": {
"text": "See all",
"type": "flz-primary",
"action": { "type": "open_url", "open_url": { "url": "", "open_in_new_window": true } }
},
"repeatable": {
"columns": [
{ "title": "Faster onboarding", "paragraph": "Go live in days, not months." },
{ "title": "Personalized at scale", "paragraph": "1:1 experiences for every account." },
{ "title": "Built-in analytics", "paragraph": "Know what works and double down." }
]
}
}
],
"ribbonParts": []
}
]
}
Step 4 — Create the board
POST /v1/boards
Authorization: Basic <your_api_token>
Content-Type: application/json
<body from step 3>
Response:
{
"board_id": 99213,
"board_url": "https://acme.boards.folloze.com/q3-acme",
"sections": [
{
"section_id": "s_abc123",
"type": "body",
"name": "Banner - classic",
"widgets": [{ "widget_id": "w_888", "title": "Acme x Folloze", "subtitle": "..." }],
"ribbonParts": []
}
]
}
You can now share board_url with your audience.
Content API #
The Content API manages the content items in your Folloze organization — the URLs, files, videos, and PDFs that appear on boards. You can ingest assets from any external source (the asset doesn't need to exist in Folloze first), update them, search them, and place them on specific boards with per-board overrides.
What it's for
- Ingest from anywhere — create items directly from any URL or downloadable asset, with no prior step in the Folloze UI.
- Sync your CMS — mirror your single source of truth into Folloze.
- Personalize per account — place the same item on many boards with a different title, description, or thumbnail on each, without duplicating the source asset.
- Search & audit — list and filter items in your organization from external systems.
Endpoints at a glance
| Endpoint | Purpose |
|---|---|
POST /v1/items | Create a content item from any external source (URL, file, video, PDF). |
GET /v1/items | List & search items in your organization (filter by type, owner, tags, free text). |
POST /v1/board_items | Place an existing item on a board. |
PUT /v1/board_items/:id | Update a board item's title, description, thumbnail, schedule, status, or categories. |
Authentication & base URL #
The Content API is served from the same host as the Board Creation API:
https://content-api.folloze.com
Authentication uses the same scheme as the Board Creation API — an API token in the Authorization header:
Authorization: Basic <your_api_token>
Tokens are issued per organization by Folloze. Contact your Folloze account representative to request a token — the same token grants access to both the Board Creation API and the Content API.
creator_email is required on every write. All POST calls require a creator_email field in the body, and the email must match a real user in your Folloze organization. Unknown emails return {"message":"user not found for creator email"}. Pass the email of a real seller / admin who should be recorded as the creator of the item.
List & search content items #
Returns a paginated list of content items in your organization, with filtering by type, owner, tags, and free text.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number for pagination. |
per_page | integer | 24 | Items per page (max 100). |
sort_by | string | — | Field to sort results by. |
sort_direction | string | desc | asc or desc. |
search | string | — | Free-text search across item content. |
type[] | string | — | Filter by content type (e.g. video, pdf). Repeatable. |
owner[] | string | — | Filter by owner user id. Repeatable. |
tags[] | string | — | Filter by tag name. Repeatable. |
[] suffix on array params. Without it, only the last value wins. ?type=video&type=pdf filters by pdf only; ?type[]=video&type[]=pdf filters by both.
Examples
# Single filter
curl "https://content-api.folloze.com/v1/items?type[]=video" \
-H "Authorization: Basic $FOLLOZE_API_TOKEN"
# Combined filters
curl "https://content-api.folloze.com/v1/items?type[]=video&type[]=pdf&owner[]=42&tags[]=webinar&search=demo&page=1&per_page=50&sort_direction=asc" \
-H "Authorization: Basic $FOLLOZE_API_TOKEN"const params = new URLSearchParams();
["video", "pdf"].forEach(t => params.append("type[]", t));
params.append("owner[]", "42");
params.append("tags[]", "webinar");
params.set("search", "demo");
params.set("per_page", "50");
const res = await fetch(
`https://content-api.folloze.com/v1/items?${params}`,
{ headers: { Authorization: `Basic ${FOLLOZE_API_TOKEN}` } }
);
const items = await res.json();import requests
res = requests.get(
"https://content-api.folloze.com/v1/items",
headers={"Authorization": f"Basic {FOLLOZE_API_TOKEN}"},
params=[
("type[]", "video"), ("type[]", "pdf"),
("owner[]", "42"),
("tags[]", "webinar"),
("search", "demo"),
("per_page", 50),
],
)
items = res.json()Create a content item #
Creates a new content item in your Folloze organization from any external source. The asset itself does not need to exist in Folloze beforehand — provide a public URL (for link items) or a downloadable URL (for file items) and Folloze ingests it. File items are ingested asynchronously — see Async file ingest.
Request body (key fields)
| Field | Type | Description | |
|---|---|---|---|
creator_email | string | required | Email of an existing user in the organization. Recorded as the item's creator. |
title | string | required | Display title of the item. |
description | string | optional | Long-form description. |
thumbnail_url | string | optional | Cover image URL. If omitted, Folloze will attempt to derive one. |
download_url | string | conditional | For file-type items: the public URL Folloze will pull the asset from. |
url | string | conditional | For URL-type items: the destination URL the item links to. |
tags | string[] | optional | Tag names to apply. New tags are created on demand. |
board_item | object | optional | If present, also places the newly created item on a board in the same call. Saves a round-trip when you're ingesting and placing in one shot. See Categories for the nested categories shape. |
Response
Successful create returns 202 Accepted with a tracking payload:
{
"folloze_id": 770029,
"provider_asset_id": "abc123",
"provider_name": "folloze"
}
Attach an item to a board #
Places an existing content item on a board without creating a duplicate. Per-board presentation fields (title, description, thumbnail, tags, schedule, categories) override the source item's defaults only on this board — the source item is never mutated.
POST /v1/items first to create the item from its source URL, then call this endpoint with the returned id. Or do both in a single call by including a board_item object on the create request.
Request body
| Field | Type | Description | |
|---|---|---|---|
board_id | integer | required | Target board id. |
content_center_item_id | integer | required | Id of the existing content item to place on the board (returned from POST /v1/items or GET /v1/items). |
creator_email | string | required | Email of an existing user; recorded as the board-item creator. |
title | string | optional | Per-board title override. |
description | string | optional | Per-board description override. |
thumbnail_url | string | optional | Per-board thumbnail override. |
tags | string[] | optional | Per-board tags. |
status | string | optional | online or offline. |
is_gated | boolean | optional | Whether viewing the item requires a form fill. |
open_in_new_tab | boolean | optional | Open destination in a new tab. |
schedule | object | false | optional | Visibility window. Pass false to clear any schedule. Object shape: { start_unix, end_unix, timezone }. |
categories | array | optional | Categories to place this item under on the board. See Categories. |
Example request
POST https://content-api.folloze.com/v1/board_items
Authorization: Basic <your_api_token>
Content-Type: application/json
{
"board_id": 241976,
"content_center_item_id": 509798,
"creator_email": "admin@follozeqa.com",
"title": "Shaping Tomorrow's Cities — for JLL",
"description": "Human-centric development insights tailored for this account.",
"thumbnail_url": "https://images.folloze.com/image/upload/hsvolbwawc9krdqmybvs.png",
"tags": ["JLL", "Creating a Space"],
"status": "online",
"is_gated": false,
"open_in_new_tab": true,
"schedule": {
"start_unix": 1714500000,
"end_unix": 1717092000,
"timezone": "America/New_York"
},
"categories": [
{ "name": "Product Updates" },
{ "name": "Customer Stories" }
]
}
Update a board item #
Updates the per-board overrides for an existing board item: title, description, thumbnail, schedule, status, and categories. The underlying source content item is never modified.
Path parameters
| Name | Type | Description | |
|---|---|---|---|
id | integer | required | Id of the board item (returned when you created it via POST /v1/board_items). |
Request body
Same fields as attach to board, except board_id and content_center_item_id are not changeable. Only fields you include are updated — omit a field to leave it as-is.
Example request
PUT https://content-api.folloze.com/v1/board_items/2207832
Authorization: Basic <your_api_token>
Content-Type: application/json
{
"title": "Test of update",
"description": "Updated walkthrough of the new platform capabilities.",
"thumbnail_url": "https://images.folloze.com/image/upload/hsvolbwawc9krdqmybvs.png",
"status": "online",
"is_gated": true,
"open_in_new_tab": true,
"schedule": {
"start_unix": 1714500000,
"end_unix": 1717092000,
"timezone": "America/New_York"
},
"categories": [
{ "name": "Product Updates" },
{ "name": "Customer Stories" }
]
}
Categories & subcategories #
The categories field on board-item endpoints lets you place an item under one or more categories on the board. Each entry can be specified by name (created on demand if missing) or by id (must already exist; otherwise the call fails).
Shape
{
"categories": [
{
"name": "Marketing",
"sub_categories": [
{ "name": "Email Campaigns" },
{ "id": 456 }
]
},
{ "id": 789 }
]
}
| Field | Type | Description |
|---|---|---|
name | string | Category name. Found on the board if it exists; created if it doesn't. |
id | integer | Existing category id. Errors if not found on this board. |
sub_categories | array | Same name/id rules; nested under the parent category. |
name for cross-board sync. If your CMS has stable category names but the board ids differ across organizations, name-based addressing lets the same payload work everywhere. Use id only when you've cached ids from a previous response.
board_item.category_ids (a flat integer array). It still works for backwards compatibility, but new integrations should use categories — it supports name-based lookup and nested subcategories.
Async file ingest #
When you create a file-type item with a download_url, Folloze responds immediately with 202 Accepted and ingests the file asynchronously by pulling it from the URL you provided.
What this means for your client
- The
folloze_idin the response is valid right away — you can attach the item to boards immediately. - The actual file content (and any derived previews / thumbnails) becomes available once ingest completes — usually seconds to minutes depending on size.
- Make sure
download_urlis publicly reachable (or signed with a long enough TTL) until ingest finishes. - If ingest fails, the item is created but its file payload is empty. Re-create or update the item with a fresh
download_url.
Folloze 101 — Key concepts
New to Folloze? This is a quick tour of the things the API talks about — boards, sections, content items, and the rest. Anywhere a term is marked with a ? in the API docs above, this is the page that's being linked to.
The big picture
A board is a personalized web page you share with buyers. Boards are built from sections (banner, content block, form, etc.), and each section is populated with content items from your Folloze organization. Marketers usually start by designing a template board in the Folloze UI, then the API takes that template and stamps out personalized copies of it — one per account, campaign, or event.
Boards
| Term | What it means |
|---|---|
| Board | A published web page in Folloze — the thing buyers actually visit. It has a unique board_url (e.g. acme.boards.folloze.com/q3-acme) and is made up of a header, a stack of body sections, and a footer. Help Center → |
| Template board | A board that's been marked as a template inside Folloze. Templates are the starting point for new boards — you take a template, fill in account-specific copy and content, and the API produces a new board based on it. Designers create templates in the Folloze UI. Help Center → |
| Header | The bar at the top of a board: logo(s), navigation links, an optional top-bar CTA, and a tagline. Folloze ships several pre-designed header types (e.g. Header with navigation, Header with 2 logos) — the template's designer picks which one to use. Help Center → |
| Footer | The bottom area of a board (links, legal, social). Footers are reusable across boards in your organization; you reference one by id when creating a board. Help Center → |
| Board tag | A free-form label applied to a board (distinct from content-item tags). Used to organize, filter, and report on boards inside Folloze. Pass tags: ["..."] on POST /v1/boards — new tags are created on demand; existing ones are reused. Help Center → |
| Channel | A grouping of boards used for navigation, sharing, and audience targeting inside Folloze. One board can belong to many channels. On POST /v1/boards reference channels by name — matched exactly (case-sensitive, no trim); created if no match exists. Help Center → |
| Board state | Publish state of a board. "online" means the board is published and reachable at its URL; "draft" means it's saved but not yet published. New boards default to "draft" — pass state: "online" on POST /v1/boards to publish immediately. Help Center → |
CTAs & actions
| Term | What it means |
|---|---|
| CTA (call to action) | A clickable button or link on a board section. Every CTA has a label (text), a visual style (type: flz-primary, flz-secondary, or flz-link), and an action that defines what happens on click. Sections can have a primary cta and/or a secondary_cta. CTA configuration reference → Help Center → |
| CTA action | The behaviour triggered when a visitor clicks a CTA. Nine action types are supported: open_url, form, registration, anchor, contact, message, share, send_email_clicked, and open_content_item. Each type carries its own configuration — see the action types table. Help Center → |
| Privacy message | A consent / privacy notice shown alongside contact, message, and share actions. Referenced by privacy_message_id in the action configuration. Privacy messages are managed in the Folloze UI. Help Center → |
Sections & ribbons
| Term | What it means |
|---|---|
| Section | A horizontal block of a board — for example, a hero banner, a 4-column value-prop block, a content carousel, or a contact form. The body of a board is just an ordered list of sections. Help Center → |
| Ribbon (API term) | Optional background or decorative layer behind a section — e.g. a coloured shape, a gradient, an image. Ribbons are part of the template's visual design and aren't surfaced as a distinct concept in the Folloze UI; in API calls you usually pass them through unchanged from /describe. |
| HTML section | A section (or header/footer) whose widget exposes raw HTML content via a dedicated html field type. HTML content cannot be set through the normal POST /v1/boards payload — use the HTML content endpoint to read and write it after the board is created, then publish to push changes live. |
| Image field (API term) | An editable image slot on a widget, identified by the image_url schema type. Accepts a public URL, a base64 data URI, or an existing Folloze CDN URL. The API automatically uploads external images to the Folloze CDN (Cloudinary) during board creation — no separate upload step is needed. On output, the field includes a read-only optimized_url and image_config with the designer's transformation settings. Image configuration reference → |
Content items
| Term | What it means |
|---|---|
| Content item | A single asset in your Folloze organization. There are two main types: links (URLs — web pages, articles, third-party content) and files (PDFs, slide decks, videos, images, etc.). Content items live independently of any board; one item can appear on many boards. Help Center → |
| Content Center | The place inside Folloze where all of your organization's content assets are hosted — PDFs, slides, videos, links, images, and so on. It's the central library every board pulls content from, and it's the same library the API exposes via GET /v1/items. Help Center → |
| Board item | A content item that's been placed onto a specific board. You can override the title, description, thumbnail, tags, and visibility schedule per board — without changing the underlying content item. So one canonical case study can appear on twenty different boards with twenty different account-specific titles. Help Center → |
| Category | A grouping bucket for board items on a section — e.g. “Product Updates”, “Customer Stories”. Categories are defined per board and used to organize content into tabs/columns/etc. Help Center → |
| Subcategory | A category nested under another category. Same rules — you can address them by name or by id. Help Center → |
| Tag | A free-form label applied to a content item. Used for searching/filtering items via GET /v1/items?tags[]=…. New tags are created on demand when you reference them. Help Center → |
How they fit together
From the API's perspective, generating a personalized board for an account looks like this:
- Start with a template board the marketing team designed in the Folloze UI.
- Read the template's sections and widgets to see which fields you can fill.
- Send a
POST /v1/boardswith account-specific copy — you get back a new board with its own URL. - Optionally add or update content items in your library, then place them on the new board as board items with per-board titles, thumbnails, and categories.
- Share the board URL with your buyer.
Changelog
Audit log of changes to the public APIs. Newest first.
| Date | Area | Change |
|---|---|---|
| 2026-07-16 | Board Creation API |
Update board endpoint. New PATCH/PUT /v1/boards/:board_id endpoint to update an existing board's sections, widgets, slug, and settings. Accepts the same page structure as create. See Update board.
|
| 2026-07-16 | Board Creation API |
Board settings: slug and access control. POST /v1/boards and PATCH /v1/boards/:board_id now accept a slug field for vanity URLs and a settings.access object for access control (open, password, email, or 2FA). Null leaf values now override template values instead of being rejected. See Access settings.
|
| 2026-07-09 | Board Creation API |
Event name and timezone fields. Zoom and Simulive event sections now expose a structured name field and scheduling.timezone (IANA). Scheduling fields renamed from unixStartTime/unixEndTime to startTime/endTime. See Event sections.
|
| 2026-07-09 | Board Creation API |
Registration page support. POST /v1/boards now accepts main_page and registration_page objects for multi-page board creation. GET /describe response is now keyed by page (main_page, registration_page). Top-level sections format still supported for backward compatibility. See Create board and Describe template / board.
|
| 2026-07-06 | Board Creation API |
Search boards. New GET /v1/boards endpoint to search and list boards by name, status, and owner. See Search boards.
|
| 2026-07-06 | Board Creation API |
Describe & create from any board. GET /v1/boards/:id/describe now works on any board (not just templates). POST /v1/boards accepts source_board_id as an alternative to template_board_id. See Describe template / board and Create board.
|
| 2026-06-24 | Board Creation API |
Event sections expanded. Added full field reference table, image object schema (url, alt, link), and a dedicated Simulive example. See Event sections (Zoom / Simulive).
|
| 2026-06-24 | Board Creation API |
Media library rewritten. Corrected endpoint to GET /v1/organization_media on content-api.folloze.com with Basic auth. Added multi-category filtering (category[]), real response format (media array + pagination object), all 8 categories, and full field reference. See Organization media library.
|
| 2026-06-15 | Board Creation API |
CTA open_content_item action. Documented the open_content_item CTA action type with item_viewer.flz_item_id support for both integer and content_center_item_id formats. See CTA: open a content item.
|
| 2026-06-15 | Board Creation API |
Organization media library endpoint. Added documentation for GET /v1/organization_media to browse your org’s media library for image URLs. See Organization media library.
|
| 2026-06-14 | Board Creation API |
Event sections (Zoom / Simulive). Event sections expose sources.meetingUrl (Zoom) or sources.videoUrl (Simulive), scheduling.unixStartTime/unixEndTime (ISO 8601), view images, and CTA fields. See Event sections (Zoom / Simulive).
|
| 2026-06-08 | Board Creation API |
Set content items on sections during board creation. Content sections now accept a sources field on widgets, letting you specify which content item to display via sources.flz_item_id. This bridges the Board Creation API and Content API in a single call — no separate POST /v1/board_items step needed. See Setting items on sections.
|
| 2026-05-30 | Board Creation API |
Image transformations via image_config. Added support for optimized_url and writable image_config fields (maxWidth, maxHeight, flipX/Y, tint, crop, shape, artisticFilter, sharpness) on image fields. Enables callers to apply Cloudinary-style transformations through the API.
|
| 2026-05-20 | Board Creation API |
Image support across all widget types. Widgets that display images now expose editable image fields in their API schemas. Pass any public URL or base64 data URI and the API auto-uploads to the Folloze CDN. Response includes optimized_url and image_config with the designer's transformation settings. Supported on image sections, single items, video sections, simulive/zoom, carousels, promo columns, multi-tab, Q&A, side-by-side, and headers. See Configuring images.
|
| 2026-05-20 | Board Creation API |
Three new endpoints for HTML widget editing and board publishing:
|
| 2026-05-13 | Board Creation API |
CTA configuration across all sections. CTAs now support a full action object with nine action types (open_url, form, registration, anchor, contact, message, share, send_email_clicked, open_content_item) and three button styles (flz-primary, flz-secondary, flz-link). Available on headers, banners, navigation items, and all body sections. See Configuring CTAs.
|
| 2026-05-11 | Board Creation API |
Added four optional fields to POST /v1/boards:
|