CMS
Content nodes and blocks management
The CMS module provides flexible content management through nodes. Nodes hold structured content made of blocks and can be classified with taxonomies.
Email templates, forms, and taxonomies are now first-class entities with their own APIs. See Email Templates, Forms, and Taxonomies.
Content Nodes
Nodes are the building blocks of your content. Each node has a key identifier and contains structured content via blocks.
Create Node
/v1/businesses/{businessId}/nodes sdk.cms.createNode() Create a new content node.
const result = await sdk.cms.createNode({
key: 'getting-started-with-arky',
parentId: 'node_blog_root',
slug: {
en: 'getting-started-with-arky',
es: 'comenzando-con-arky'
},
blocks: [
{
key: 'title',
type: 'LOCALIZED_TEXT',
properties: { label: { en: 'Title' } },
value: [{ en: 'Welcome to Arky', es: 'Bienvenido a Arky' }]
},
{
key: 'body',
type: 'MARKDOWN',
properties: {},
value: [{ en: 'This is the introduction paragraph...', es: 'Este es el párrafo de introducción...' }]
},
{
key: 'featured',
type: 'BOOLEAN',
properties: {},
value: [true]
}
],
taxonomies: [
{
taxonomyId: 'tax_blog_categories',
fields: [
{ type: 'text', id: 'fld_category', key: 'category', value: ['tutorials'] }
]
}
]
});Parameters
| Name | Type | Description |
|---|---|---|
key required | string | Unique node key identifier |
parentId optional | string | null | Parent node ID for hierarchy |
blocks optional | Block[] | Content blocks |
slug optional | Record<string, string> | Localized URL-friendly slugs (e.g., { en: 'my-page', es: 'mi-pagina' }) |
taxonomies optional | TaxonomyEntry[] | Taxonomy classifications with filterable field values |
Get Node
/v1/businesses/{businessId}/nodes/{id} sdk.cms.getNode() Retrieve a node by ID, slug, or key.
// By ID
const result = await sdk.cms.getNode({
id: 'node_xyz789'
});
// By slug (locale-aware)
const result = await sdk.cms.getNode({
slug: 'getting-started-with-arky'
});
// By key (unique lookup)
const result = await sdk.cms.getNode({
key: 'homepage'
});
// Access blocks with helper methods
const heroBlock = result.getBlock('hero');
const heroValues = result.getBlockValues('hero');
const imageUrl = result.getImage('hero-image');Parameters
| Name | Type | Description |
|---|---|---|
id optional | string | Node ID (use this OR slug OR key) |
slug optional | string | Node slug (locale-aware lookup) |
key optional | string | Node key (unique lookup) |
List Nodes
/v1/businesses/{businessId}/nodes sdk.cms.getNodes() List nodes with filtering and pagination.
const result = await sdk.cms.getNodes({
parentId: 'node_blog_root',
type: 'BLOG_POST',
key: 'featured-post',
statuses: ['ACTIVE'],
query: 'arky',
sortField: 'createdAt',
sortDirection: 'desc',
cursor: null,
limit: 10,
createdAtFrom: '2024-01-01',
createdAtTo: '2024-12-31'
});
result.items.forEach(node => {
console.log(node.key, node.slug);
});Parameters
| Name | Type | Description |
|---|---|---|
parentId optional | string | Filter by parent node |
type optional | string | Filter by node type |
key optional | string | Filter by key |
ids optional | string[] | Filter by specific node IDs |
statuses optional | string[] | Filter by statuses (ACTIVE, ARCHIVED) |
query optional | string | Search query in content |
sortField optional | string | Sort field (createdAt, updatedAt, etc.) |
sortDirection optional | asc | desc | Sort direction |
cursor optional | string | Pagination cursor |
limit optional | number | Items per page |
createdAtFrom optional | string | Filter by creation date (start) |
createdAtTo optional | string | Filter by creation date (end) |
Get Node Children
/v1/businesses/{businessId}/nodes/{id}/children sdk.cms.getNodeChildren() Get child nodes of a parent node.
const result = await sdk.cms.getNodeChildren({
id: 'node_parent123',
cursor: null,
limit: 20
});
result.items.forEach(child => {
console.log(child.key);
});
Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Parent node ID |
cursor optional | string | Pagination cursor |
limit optional | number | Items per page |
Update Node
/v1/businesses/{businessId}/nodes/{id} sdk.cms.updateNode() Update an existing node.
const result = await sdk.cms.updateNode({
id: 'node_xyz789',
key: 'updated-post-key',
parentId: 'node_new_parent',
slug: {
en: 'updated-slug',
es: 'slug-actualizado'
},
status: 'ACTIVE',
blocks: [
// Updated blocks
],
taxonomies: [
{
taxonomyId: 'tax_blog_categories',
fields: [
{ type: 'text', id: 'fld_category', key: 'category', value: ['news'] }
]
}
]
});Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Node ID to update |
key optional | string | Updated node key |
parentId optional | string | null | Updated parent node ID |
blocks optional | Block[] | Updated content blocks |
slug optional | Record<string, string> | Updated localized slugs |
taxonomies optional | TaxonomyEntry[] | Updated taxonomy classifications |
status optional | ACTIVE | ARCHIVED | Updated publication status |
Delete Node
/v1/businesses/{businessId}/nodes/{id} sdk.cms.deleteNode() Delete a node.
await sdk.cms.deleteNode({
id: 'node_xyz789'
});
Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Node ID to delete |
Content Blocks
Blocks are reusable content components within nodes.
Block Types
| Type | Description |
|---|---|
TEXT | Plain text - stores simple strings (names, emails, IDs, URLs) |
LOCALIZED_TEXT | Localized text - stores translations {en: "...", es: "..."} |
NUMBER | Numeric values |
BOOLEAN | Boolean values |
GEO_LOCATION | Geographic location with address, coordinates |
BLOCK | Nested blocks container |
RELATIONSHIP_ENTRY | Reference to other nodes |
RELATIONSHIP_MEDIA | Reference to media files |
MARKDOWN | Localized markdown content |
EMAIL | Email address |
PHONE | Phone number |
ADDRESS | Physical address (shipping or billing) |
Complete Blog Example
// Create a blog post
const post = await sdk.cms.createNode({
key: '10-tips-for-better-code',
parentId: 'node_blog_root',
slug: {
en: '10-tips-for-better-code',
es: '10-consejos-para-mejor-codigo'
},
blocks: [
{
key: 'title',
type: 'LOCALIZED_TEXT',
properties: {},
value: [{ en: '10 Tips for Better Code', es: '10 Consejos para Mejor Código' }]
},
{
key: 'subtitle',
type: 'LOCALIZED_TEXT',
properties: {},
value: [{ en: 'Improve your code quality today' }]
},
{
key: 'hero-image',
type: 'RELATIONSHIP_MEDIA',
properties: {},
value: ['media:media_hero123']
},
{
key: 'content',
type: 'MARKDOWN',
properties: {},
value: [{
en: `## Introduction
Writing clean code is essential for maintainability...
## Tip 1: Use Descriptive Names
Variable names should describe their purpose...`
}]
},
{
key: 'author-email',
type: 'EMAIL',
properties: {},
value: ['author@example.com']
}
]
});
// Archive the post later
await sdk.cms.updateNode({
id: post.id,
status: 'ARCHIVED'
});
Use the key field for unique content like homepages or settings pages that should only exist once.