CMS

The CMS module manages sites, collections, entries, revisions, marketing campaigns, and marketing posts for content and go-to-market workflows.

What it does

CMS is an app-plane module for structured content operations. It supports headless CMS records and marketing planning records inside the same workspace boundary as CRM, Tasks, Assets, Activity Log, and Data & Insights.

Public capabilities:

  • Create and list CMS sites.
  • Create and manage content collections.
  • Create, search, and update richer entries.
  • Move entries through draft, review, published, and archived states.
  • Create and list marketing campaigns.
  • Create and list marketing posts for campaign channels.
  • Keep all CMS operations tenant-scoped, audited, and available through REST and MCP.

Resource model

ResourcePurpose
sitesWorkspace site records for docs, marketing, help center, or customer education surfaces.
collectionsContent schemas such as case studies, posts, docs, landing pages, or changelog items.
entriesStructured content records with title, status, fields, and queryable custom_fields.
revisionsVersion history for entry changes.
marketing_campaignsCampaign containers for launches, webinars, lifecycle programs, or customer outreach.
marketing_postsChannel-specific posts linked to campaigns, sites, entries, assets, or CRM context.

CMS entries use fields for content schema data and custom_fields for workspace-specific query metadata, such as persona, product area, lifecycle stage, or experiment key.

Production website delivery

Slab5 CMS REST reads are metered API reads. They are intended for builders, server-side applications, previews, content tooling, agents, and controlled publish flows.

Production websites should not fetch published CMS entries from Slab5 on every pageview. Use build-time reads, server-side caching, route-level revalidation, or a publish pipeline that writes a cached website artifact. This keeps websites fast, protects workspace usage, and avoids turning every visitor request into a CMS API request.

Recommended launch patterns:

PatternUse it whenBilling behavior
Build-time CMS readContent changes are deployed with the website or through a rebuild.Slab5 meters the CMS reads during build, not each visitor pageview.
Server-side cached readThe site needs fresh content without rebuilding for every change.Slab5 meters cache misses and revalidation reads. Visitor hits served from cache do not call Slab5.
On-demand revalidationA publish action or webhook can revalidate specific pages.Slab5 meters the publish/revalidation reads instead of every pageview.
Direct per-request CMS readInternal tools, previews, or low-traffic admin surfaces only.Every request can become a metered CMS API read. Avoid this for public websites.

For public marketing sites, use build-time or server-side cached reads before launch. Publish-to-CDN delivery can be added later as a separate publishing layer.

Next.js caching examples

Use a server-only CMS client and set an explicit cache policy for published content reads.

// lib/slab5-cms.ts
import 'server-only'

const SLAB5_API_URL = process.env.SLAB5_API_URL ?? 'https://api.slab5.com/v1'
const SLAB5_WORKSPACE_ID = process.env.SLAB5_WORKSPACE_ID
const SLAB5_API_KEY = process.env.SLAB5_API_KEY

export async function readPublishedEntries(query: string) {
  if (!SLAB5_WORKSPACE_ID || !SLAB5_API_KEY) {
    throw new Error('Slab5 CMS is not configured')
  }

  const url = new URL(`${SLAB5_API_URL}/cms/entries`)
  url.searchParams.set('status', 'published')
  url.searchParams.set('query', query)
  url.searchParams.set('limit', '10')

  const response = await fetch(url, {
    headers: {
      Authorization: `Bearer ${SLAB5_API_KEY}`,
      'x-slab5-workspace-id': SLAB5_WORKSPACE_ID,
    },
    next: {
      revalidate: 300,
      tags: [`slab5-cms:${query}`],
    },
  })

  if (!response.ok) {
    throw new Error(`Unable to load published CMS content: ${response.status}`)
  }

  return response.json()
}

For pages that can tolerate a fixed refresh interval, call the helper from a server component:

// app/products/page.tsx
import { readPublishedEntries } from '@/lib/slab5-cms'

export const revalidate = 300

export default async function ProductsPage() {
  const entries = await readPublishedEntries('products')
  const products = entries.data?.[0]?.fields?.products

  return <ProductsView products={products} />
}

For content that should refresh immediately after publish, add a protected revalidation route in the website and call it from the publish workflow:

// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache'
import { NextResponse } from 'next/server'

export async function POST(request: Request) {
  const secret = request.headers.get('x-revalidate-secret')
  if (secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ error: 'unauthorized' }, { status: 401 })
  }

  const { tag } = await request.json()
  revalidateTag(tag)

  return NextResponse.json({ revalidated: true })
}

Set downstream response caching for server-rendered routes where appropriate:

export async function GET() {
  const body = await renderCachedContent()

  return new Response(body, {
    headers: {
      'content-type': 'text/html; charset=utf-8',
      'cache-control': 'public, s-maxage=300, stale-while-revalidate=3600',
    },
  })
}

Keep the Slab5 API key on the server. Do not expose workspace API keys in browser JavaScript.

Marketing workflow

A marketing/content agent can run a launch workflow:

  1. create_marketing_campaign for the launch or customer cohort.
  2. create_cms_site if the site does not exist yet.
  3. create_collection for the content type.
  4. create_entry for the draft page, post, or case study.
  5. create_asset_upload_intent for images, PDFs, or video attachments.
  6. create_marketing_post for LinkedIn, email, blog, or changelog distribution.
  7. log_activity to preserve planning and publish activity.
  8. create_bi_dashboard or run_bi_query for campaign tracking when the Data & Insights module is enabled.

Permissions

Use cms:read to list sites, campaigns, posts, and search entries. Use cms:write to create or update CMS resources.

Recommended scoped agent credentials:

cms:read
cms:write
assets:read
assets:write
activity:write
bi:read
bi:write

MCP tools

create_collection

Create a CMS collection that defines a content type for entries.

MVP
Idempotent writesNo dry runStrict schema
Required scopes
cms:write
API equivalent
POST /v1/cms/collections

Example prompt

Create a blog posts collection with title, slug, and body fields.

Input schema

PropertyTypeRequiredDescription
namestringYesHuman-readable collection name.
keystringYesStable collection key used by API and MCP clients.
descriptionstringNoOptional description of the collection purpose.
schemaobjectNoJSON schema-like field definition for entries in this collection.
idempotency_keystringNoStable client-generated key that makes retries safe for this write.

Additional properties are rejected.

Example input

{
  "name": "Blog Posts",
  "key": "blog_posts",
  "schema": {
    "fields": [
      {
        "key": "title",
        "type": "string",
        "required": true
      },
      {
        "key": "slug",
        "type": "string",
        "required": true
      },
      {
        "key": "body",
        "type": "markdown",
        "required": true
      }
    ]
  }
}

Example response

{
  "collection": {
    "id": "col_123",
    "key": "blog_posts",
    "name": "Blog Posts"
  },
  "request_id": "req_135"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

The idempotency key was reused with a different request body.

create_cms_site

Create a website or marketing site managed by the CMS.

MVP
Idempotent writesNo dry runStrict schema
Required scopes
cms:write
API equivalent
POST /v1/cms/sites

Example prompt

Create a CMS site for slab5.com.

Input schema

PropertyTypeRequiredDescription
keystringYesNo description yet.
namestringYesNo description yet.
primary_domainstringNoNo description yet.
descriptionstringNoNo description yet.
settingsobjectNoNo description yet.
custom_fieldsobjectNoNo description yet.
idempotency_keystringNoNo description yet.

Additional properties are rejected.

Example input

{
  "key": "slab5_com",
  "name": "slab5.com",
  "primary_domain": "slab5.com"
}

Example response

{
  "site": {
    "id": "site_123",
    "key": "slab5_com"
  },
  "request_id": "req_212"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

The idempotency key was reused with a different request body.

list_cms_sites

List CMS-managed websites.

MVP
Read operationNo dry runStrict schema
Required scopes
cms:read
API equivalent
GET /v1/cms/sites

Example prompt

List CMS sites.

Input schema

PropertyTypeRequiredDescription
querystringNoNo description yet.
limitinteger · min 1 · max 100NoNo description yet.
cursorstringNoNo description yet.

Additional properties are rejected.

Example input

{
  "limit": 10
}

Example response

{
  "sites": [
    {
      "id": "site_123",
      "name": "slab5.com"
    }
  ],
  "request_id": "req_213"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

create_entry

Create a CMS entry in a collection.

MVP
Idempotent writesNo dry runStrict schema
Required scopes
cms:write
API equivalent
POST /v1/cms/entries

Example prompt

Create a draft blog post about AI follow-up workflows.

Input schema

PropertyTypeRequiredDescription
collection_idstringYesCollection ID where the entry should be created.
site_idstringNoOptional CMS site this content belongs to.
campaign_idstringNoOptional marketing campaign this content supports.
content_typestring · website_page | landing_page | blog_post | article | social_post | email | case_study | asset_brief | otherNoNo description yet.
slugstringNoNo description yet.
titlestringNoOptional display title for the entry.
excerptstringNoNo description yet.
fieldsobjectYesEntry field values keyed by collection schema field key.
statusstring · draft | review | published | archivedNoInitial publishing status.
seoobjectNoNo description yet.
custom_fieldsobjectNoUser-defined queryable JSON fields.
idempotency_keystringNoStable client-generated key that makes retries safe for this write.

Additional properties are rejected.

Example input

{
  "collection_id": "col_123",
  "title": "AI follow-up workflows",
  "status": "draft",
  "fields": {
    "slug": "ai-follow-up-workflows",
    "body": "Draft content..."
  }
}

Example response

{
  "entry": {
    "id": "ent_123",
    "collection_id": "col_123",
    "status": "draft"
  },
  "request_id": "req_136"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

The requested resource does not exist in this workspace.

search_entries

Search CMS entries by collection, status, title, or free-text query.

MVP
Read operationNo dry runStrict schema
Required scopes
cms:read
API equivalent
GET /v1/cms/entries

Example prompt

Find draft blog posts about follow-up.

Input schema

PropertyTypeRequiredDescription
collection_idstringNoRestrict results to one collection.
site_idstringNoNo description yet.
campaign_idstringNoNo description yet.
content_typestring · website_page | landing_page | blog_post | article | social_post | email | case_study | asset_brief | otherNoNo description yet.
statusstring · draft | review | published | archivedNoRestrict results by publishing status.
querystringNoFree-text search across title and fields.
custom_field_keystringNoNo description yet.
custom_field_valuestringNoNo description yet.
limitinteger · min 1 · max 100NoMaximum number of entries to return.
cursorstringNoOpaque cursor from a previous response's next_cursor.

Additional properties are rejected.

Example input

{
  "query": "follow-up",
  "status": "draft",
  "limit": 10
}

Example response

{
  "entries": [
    {
      "id": "ent_123",
      "title": "AI follow-up workflows",
      "status": "draft"
    }
  ],
  "request_id": "req_137"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

update_entry_status

Move a CMS entry between draft, review, published, and archived states. Deprecated in favor of update_entry.

Deprecated
Idempotent writesSupports dry runStrict schema
Use update_entry for status, title, and field changes through one revision-writing tool. Use update_entry for new integrations.
Required scopes
cms:write
API equivalent
PATCH /v1/cms/entries/{entry_id}

Example prompt

Move ent_123 to review.

Input schema

PropertyTypeRequiredDescription
entry_idstringYesEntry ID to update.
statusstring · draft | review | published | archivedYesTarget publishing status.
dry_runboolean · default falseNoPreview validation and audit effects without changing the entry.
idempotency_keystringNoStable client-generated key that makes retries safe for this write.

Additional properties are rejected.

Example input

{
  "entry_id": "ent_123",
  "status": "review"
}

Example response

{
  "entry": {
    "id": "ent_123",
    "status": "review"
  },
  "request_id": "req_138"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

The requested resource does not exist in this workspace.

update_entry

Update a CMS entry title, fields, or publishing status and create a revision.

MVP
Idempotent writesNo dry runStrict schema
Required scopes
cms:write
API equivalent
PATCH /v1/cms/entries/{entry_id}

Example prompt

Update ent_123 with a revised title, fields, and review status.

Input schema

PropertyTypeRequiredDescription
entry_idstringYesEntry ID to update.
slugstringNoNo description yet.
excerptstringNoNo description yet.
content_typestring · website_page | landing_page | blog_post | article | social_post | email | case_study | asset_brief | otherNoNo description yet.
titlestringNoOptional display title for the entry.
fieldsobjectNoOptional replacement field values keyed by collection schema field key.
statusstring · draft | review | published | archivedNoOptional target publishing status.
seoobjectNoNo description yet.
custom_fieldsobjectNoNo description yet.
idempotency_keystringNoStable client-generated key that makes retries safe for this write.

Additional properties are rejected.

Example input

{
  "entry_id": "ent_123",
  "title": "AI follow-up workflows",
  "fields": {
    "slug": "ai-follow-up-workflows",
    "body": "Updated draft content..."
  },
  "status": "review"
}

Example response

{
  "entry": {
    "id": "ent_123",
    "title": "AI follow-up workflows",
    "status": "review"
  },
  "request_id": "req_138"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

The requested resource does not exist in this workspace.

create_marketing_campaign

Create a marketing campaign for website, blog, email, social, or paid channels.

MVP
Idempotent writesNo dry runStrict schema
Required scopes
cms:write
API equivalent
POST /v1/marketing/campaigns

Example prompt

Create the slab5.com beta launch campaign.

Input schema

PropertyTypeRequiredDescription
keystringYesNo description yet.
namestringYesNo description yet.
statusstring · planned | active | paused | completed | archivedNoNo description yet.
channelstring · website | blog | email | linkedin | x | youtube | paid_ads | partner | otherNoNo description yet.
objectivestringNoNo description yet.
budget_centsinteger · min 0NoNo description yet.
custom_fieldsobjectNoNo description yet.
idempotency_keystringNoNo description yet.

Additional properties are rejected.

Example input

{
  "key": "slab5_beta_launch",
  "name": "Slab5 beta launch",
  "channel": "website"
}

Example response

{
  "campaign": {
    "id": "camp_123",
    "key": "slab5_beta_launch"
  },
  "request_id": "req_214"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

The idempotency key was reused with a different request body.

list_marketing_campaigns

List marketing campaigns by status, channel, or custom field.

MVP
Read operationNo dry runStrict schema
Required scopes
cms:read
API equivalent
GET /v1/marketing/campaigns

Example prompt

List active marketing campaigns.

Input schema

PropertyTypeRequiredDescription
statusstringNoNo description yet.
channelstringNoNo description yet.
querystringNoNo description yet.
limitinteger · min 1 · max 100NoNo description yet.
cursorstringNoNo description yet.

Additional properties are rejected.

Example input

{
  "status": "active"
}

Example response

{
  "campaigns": [
    {
      "id": "camp_123",
      "name": "Slab5 beta launch"
    }
  ],
  "request_id": "req_215"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

create_marketing_post

Create a social, blog, email, or website marketing post linked to an optional campaign or CMS entry.

MVP
Idempotent writesNo dry runStrict schema
Required scopes
cms:write
API equivalent
POST /v1/marketing/posts

Example prompt

Draft a LinkedIn post for the beta launch.

Input schema

PropertyTypeRequiredDescription
campaign_idstringNoNo description yet.
entry_idstringNoNo description yet.
channelstring · website | blog | email | linkedin | x | youtube | paid_ads | partner | otherYesNo description yet.
statusstring · draft | review | published | archivedNoNo description yet.
titlestringNoNo description yet.
bodystringYesNo description yet.
scheduled_atstring · date-timeNoNo description yet.
external_urlstringNoNo description yet.
custom_fieldsobjectNoNo description yet.
idempotency_keystringNoNo description yet.

Additional properties are rejected.

Example input

{
  "channel": "linkedin",
  "title": "Beta launch",
  "body": "We are opening Slab5 private beta."
}

Example response

{
  "post": {
    "id": "post_123",
    "channel": "linkedin"
  },
  "request_id": "req_216"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

The requested resource does not exist in this workspace.

list_marketing_posts

List marketing posts by channel, status, campaign, or custom field.

MVP
Read operationNo dry runStrict schema
Required scopes
cms:read
API equivalent
GET /v1/marketing/posts

Example prompt

List draft LinkedIn posts.

Input schema

PropertyTypeRequiredDescription
campaign_idstringNoNo description yet.
channelstringNoNo description yet.
statusstringNoNo description yet.
querystringNoNo description yet.
limitinteger · min 1 · max 100NoNo description yet.
cursorstringNoNo description yet.

Additional properties are rejected.

Example input

{
  "channel": "linkedin",
  "status": "draft"
}

Example response

{
  "posts": [
    {
      "id": "post_123",
      "channel": "linkedin"
    }
  ],
  "request_id": "req_217"
}

Common errors

The token does not include the scope required for this operation.

The request payload failed schema validation.

Was this page helpful?