# 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.

- Skill: `coldbox/contentbox-boxlang-api-headless` (Agent Skill)
- Install (CLI): `npx skillmds@latest add coldbox/contentbox-boxlang-api-headless`
- Raw SKILL.md: https://api.skillmd.com/api/skills/coldbox/contentbox-boxlang-api-headless/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: ColdBox (https://skillmd.com/u/coldbox)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/coldbox/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

| Handler | Resource | Description |
|---------|----------|-------------|
| `auth.bx` | Authentication | JWT token generation and validation |
| `authors.bx` | Authors | Author CRUD operations |
| `categories.bx` | Categories | Category CRUD operations |
| `comments.bx` | Comments | Comment management |
| `contentStore.bx` | ContentStore | Key-value content blocks |
| `contentTemplates.bx` | Templates | Content template management |
| `entries.bx` | Entries | Blog entry CRUD operations |
| `menus.bx` | Menus | Menu management |
| `pages.bx` | Pages | Page CRUD operations |
| `relocations.bx` | Relocations | URL redirect management |
| `settings.bx` | Settings | Global settings API |
| `siteSettings.bx` | Site Settings | Site-specific settings |
| `sites.bx` | Sites | Multi-site management |
| `versions.bx` | Versions | Content versioning |
| `echo.bx` | Health Check | API health check / echo |

### Base Handler Pattern

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

```boxlang
// 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

| Parameter | Description |
|-----------|-------------|
| `page` | Page number (default: 1) |
| `maxRows` | Results per page |
| `sortOrder` | Sort field and direction |
| `isDeleted` | Include soft-deleted entries |
| `includes` | Related entities to include |
| `excludes` | Fields 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
```

### Menus

```
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

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

### Single Resource Response

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

### Error Response

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

## Creating Custom API Endpoints

### Custom API Handler

```boxlang
// 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`:

```boxlang
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:

```javascript
// 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:

```boxlang
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

