๐Ÿ”ท Core

contentbox-boxlang-api-headless

Use this skill when implementing headless ContentBox APIs, including REST endpoint design, JWT authentication flows, content CRUD, custom API handlers, and integration patterns for decoupled frontends.

$ npx skills add coldbox/skills/contentbox-boxlang/api-headless
$ coldbox ai skills install coldbox/skills/contentbox-boxlang/api-headless
๐Ÿ”— https://skills.boxlang.io/skills/raw/coldbox/skills/contentbox-boxlang~api-headless

ContentBox API & Headless Development (BoxLang)

Build headless and REST API integrations with ContentBox CMS using BoxLang. ContentBox provides a full REST API (v1) for managing all content types, enabling headless CMS architectures.

API Architecture

The API module lives at modules/contentbox/modules/contentbox-api/ with a nested v1 module at modules/contentbox/modules/contentbox-api/modules/contentbox-api-v1/.

API v1 Handlers

HandlerResourceDescription
auth.bxAuthenticationJWT token generation and validation
authors.bxAuthorsAuthor CRUD operations
categories.bxCategoriesCategory CRUD operations
comments.bxCommentsComment management
contentStore.bxContentStoreKey-value content blocks
contentTemplates.bxTemplatesContent template management
entries.bxEntriesBlog entry CRUD operations
menus.bxMenusMenu management
pages.bxPagesPage CRUD operations
relocations.bxRelocationsURL redirect management
settings.bxSettingsGlobal settings API
siteSettings.bxSite SettingsSite-specific settings
sites.bxSitesMulti-site management
versions.bxVersionsContent versioning
echo.bxHealth CheckAPI health check / echo

Base Handler Pattern

API handlers extend BaseHandler (which extends cborm.models.resources.BaseHandler):

// handlers/api/v1/MyResource.bx
component extends="contentbox.modules.contentbox-api.modules.contentbox-api-v1.handlers.baseHandler" singleton {

	// Inject the virtual entity service
	property name="ormService" inject="MyEntityService@contentbox"

	// Entity name (singular)
	variables.entity = "MyEntity"

	// Default sort order
	variables.sortOrder = "createdDate DESC"

	// Use native getOrFail() or getByIdOrSlugOrFail()
	variables.useGetOrFail = true

}

This automatically provides: index, create, show, update, delete methods.

Authentication

JWT Authentication

The API uses JWT tokens for authentication:

POST /api/v1/auth
Body: { "username": "admin", "password": "secret" }

Response: { "token": "eyJhbGciOiJIUzI1NiIs...", "expires": 3600 }

Using Tokens

Include the token in the Authorization header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

API Endpoints

Entries

GET    /api/v1/entries              โ†’ List entries (paginated)
GET    /api/v1/entries/:id          โ†’ Get single entry
POST   /api/v1/entries              โ†’ Create entry
PUT    /api/v1/entries/:id          โ†’ Update entry
DELETE /api/v1/entries/:id          โ†’ Delete entry

Query Parameters

ParameterDescription
pagePage number (default: 1)
maxRowsResults per page
sortOrderSort field and direction
isDeletedInclude soft-deleted entries
includesRelated entities to include
excludesFields to exclude from response

Pages

GET    /api/v1/pages                โ†’ List pages
GET    /api/v1/pages/:id            โ†’ Get single page (by ID or slug)
POST   /api/v1/pages                โ†’ Create page
PUT    /api/v1/pages/:id            โ†’ Update page
DELETE /api/v1/pages/:id            โ†’ Delete page

Categories

GET    /api/v1/categories           โ†’ List categories
GET    /api/v1/categories/:id       โ†’ Get single category
POST   /api/v1/categories           โ†’ Create category
PUT    /api/v1/categories/:id       โ†’ Update category
DELETE /api/v1/categories/:id       โ†’ Delete category

Authors

GET    /api/v1/authors              โ†’ List authors
GET    /api/v1/authors/:id          โ†’ Get single author
POST   /api/v1/authors              โ†’ Create author
PUT    /api/v1/authors/:id          โ†’ Update author
DELETE /api/v1/authors/:id          โ†’ Delete author

ContentStore

GET    /api/v1/contentstore         โ†’ List all content store items
GET    /api/v1/contentstore/:key    โ†’ Get item by key
POST   /api/v1/contentstore         โ†’ Create item
PUT    /api/v1/contentstore/:key    โ†’ Update item
DELETE /api/v1/contentstore/:key    โ†’ Delete item
GET    /api/v1/menus                โ†’ List menus
GET    /api/v1/menus/:slug          โ†’ Get menu by slug
POST   /api/v1/menus                โ†’ Create menu
PUT    /api/v1/menus/:slug          โ†’ Update menu
DELETE /api/v1/menus/:slug          โ†’ Delete menu

Sites

GET    /api/v1/sites                โ†’ List sites
GET    /api/v1/sites/:id            โ†’ Get single site
POST   /api/v1/sites                โ†’ Create site
PUT    /api/v1/sites/:id            โ†’ Update site
DELETE /api/v1/sites/:id            โ†’ Delete site

Response Format

List Response

{
	"data": [
		{ "id": "...", "title": "...", "slug": "...", ... }
	],
	"total": 100,
	"page": 1,
	"maxRows": 25
}

Single Resource Response

{
	"data": {
		"id": "...",
		"title": "...",
		"slug": "...",
		"content": "...",
		"author": { ... },
		"categories": [ ... ],
		...
	}
}

Error Response

{
	"error": true,
	"message": "Resource not found",
	"details": "..."
}

Creating Custom API Endpoints

Custom API Handler

// handlers/api/v1/CustomResource.bx
component extends="contentbox.modules.contentbox-api.modules.contentbox-api-v1.handlers.baseHandler" singleton {

	property name="ormService" inject="CustomEntityService@contentbox"
	variables.entity = "CustomEntity"
	variables.sortOrder = "createdDate DESC"

	// Override index for custom filtering
	function index( event, rc, prc, criteria, results ){
		// Custom filtering logic
		prc.criteria = ormService.newCriteria()
		if( structKeyExists( rc, "status" ) ){
			prc.criteria.isEq( "status", rc.status )
		}

		// Delegate to parent
		super.index( event, rc, prc, prc.criteria )
	}

	// Add custom action
	function publish( event, rc, prc ){
		entity = ormService.get( rc.id )
		entity.setStatus( "published" )
		ormService.save( entity )

		renderData(
			type       : "json",
			data       : { success : true, entity : entity.getMemento() },
			statusCode : 200
		)
	}

}

Custom API Routes

Register routes in your module's ModuleConfig.bx:

routes = [
	// RESTful resources
	{ pattern : "/api/v1/custom", handler : "api/v1/customResource" },
	{ pattern : "/api/v1/custom/:id", handler : "api/v1/customResource" },
	// Custom actions
	{ pattern : "/api/v1/custom/:id/publish", handler : "api/v1/customResource", action : "publish" }
]

Headless CMS Usage

Frontend Integration

Use the API to build headless frontends:

// Fetch entries
const response = await fetch('/api/v1/entries?page=1&maxRows=10', {
  headers: { 'Authorization': `Bearer ${token}` }
});
const { data, total, page } = await response.json();

// Fetch single page by slug
const pageResponse = await fetch('/api/v1/pages/my-page-slug', {
  headers: { 'Authorization': `Bearer ${token}` }
});
const { data: page } = await pageResponse.json();

Content Rendering

The API returns content with all fields, including:

  • title, slug, content (HTML)
  • publishedDate, createdDate, modifiedDate
  • author (nested object)
  • categories (array)
  • customFields (if configured)
  • featuredImage (media reference)

API Security

Firewall Rules

API routes are protected by cbSecurity rules. Configure in settings:

settings.cbsecurity = {
	firewall : {
		invalidAuthenticationEvent : "cbapi/auth/unauthorized",
		defaultAuthenticationAction : "redirect",
		invalidAuthorizationEvent : "cbapi/auth/forbidden",
		defaultAuthorizationAction : "redirect"
	}
}

Rate Limiting

The core RateLimiter@contentbox interceptor protects against brute-force attacks.

Best Practices

  1. Extend baseHandler โ€” get CRUD operations for free
  2. Inject ormService โ€” use the correct virtual entity service
  3. Use variables.entity โ€” set the singular entity name
  4. Set variables.sortOrder โ€” define default sorting
  5. Use param for defaults โ€” set safe defaults for query parameters
  6. Override methods as needed โ€” customize index, show, etc.
  7. Use renderData() โ€” for consistent JSON responses
  8. Announce interception points โ€” for extensibility
  9. Include related entities โ€” use includes parameter for nested data
  10. Handle errors gracefully โ€” return proper error responses

Engine Compatibility

This skill targets BoxLang engine. For CFML-specific syntax (Lucee 5+, Adobe ColdFusion 2018+), see the CFML variant of this skill.

Key BoxLang advantages:

  • Cleaner script syntax without <cfcomponent> / <cffunction> tags
  • No parentheses needed for zero-argument function calls
  • #{...}# for inline expression output in .bx templates
  • Modern syntax: ?: null coalescing, ?. safe navigation
  • Native support for modern data structures