CMS

Content nodes and blocks management

The CMS module provides a flexible content management system with nodes and blocks.

Content Nodes

Nodes are the building blocks of your content. Each node has a key identifier and contains structured content via blocks.

Create Node

POST /v1/businesses/{businessId}/nodes
SDK: 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'
},
access: 'PUBLIC',
writeAccess: 'AUTHENTICATED',
status: 'ACTIVE',
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]
  }
],
emailSubject: {
  en: 'Welcome to Arky!',
  es: '¡Bienvenido a Arky!'
}
});

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' })
access optional PUBLIC | AUTHENTICATED | PRIVATE Read access level
writeAccess optional PUBLIC | AUTHENTICATED | PRIVATE Write access level
status optional DRAFT | ACTIVE | ARCHIVED Publication status
emailSubject optional Record<string, string> Localized email subject (for email template nodes)

Get Node

GET /v1/businesses/{businessId}/nodes/{id}
SDK: 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

GET /v1/businesses/{businessId}/nodes
SDK: 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: ['PUBLISHED'],
query: 'arky',
sortField: 'createdAt',
sortDirection: 'desc',
cursor: null,
limit: 10,
includeChildren: false,
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
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
includeChildren optional boolean Include child nodes in response
createdAtFrom optional string Filter by creation date (start)
createdAtTo optional string Filter by creation date (end)

Get Node Children

GET /v1/businesses/{businessId}/nodes/{id}/children
SDK: 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

PUT /v1/businesses/{businessId}/nodes/{id}
SDK: 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'
},
access: 'AUTHENTICATED',
writeAccess: 'PRIVATE',
status: 'PUBLISHED',
blocks: [
  // Updated blocks
],
emailSubject: {
  en: 'Updated Subject'
}
});

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
access optional PUBLIC | AUTHENTICATED | PRIVATE Updated read access level
writeAccess optional PUBLIC | AUTHENTICATED | PRIVATE Updated write access level
status optional DRAFT | ACTIVE | ARCHIVED Updated publication status
emailSubject optional Record<string, string> Updated localized email subject

Delete Node

DELETE /v1/businesses/{businessId}/nodes/{id}
SDK: 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

TypeDescription
TEXTPlain text - stores simple strings (names, emails, IDs, URLs)
LOCALIZED_TEXTLocalized text - stores translations {en: "...", es: "..."}
NUMBERNumeric values
BOOLEANBoolean values
GEO_LOCATIONGeographic location with address, coordinates
BLOCKNested blocks container
RELATIONSHIP_ENTRYReference to other nodes
RELATIONSHIP_MEDIAReference to media files
MARKDOWNLocalized markdown content
EMAILEmail address
PHONEPhone number
ADDRESSPhysical address (shipping or billing)

Generate Blocks with AI

POST /v1/businesses/{businessId}/nodes/blocks/generate
SDK: sdk.cms.generateBlocks()

Generate content blocks using AI.

const result = await sdk.cms.generateBlocks({
  prompt: 'Create a landing page for a fitness app',
  blockTypes: ['HERO', 'TEXT', 'CTA'],
  tone: 'professional',
  length: 'medium'
});

// Use generated blocks
const blocks = result.blocks;

Get Variable Metadata

GET /v1/businesses/{businessId}/nodes/types/{nodeType}/variables
SDK: sdk.cms.getVariableMetadata()

Get available template variables for a node type.

const result = await sdk.cms.getVariableMetadata({
  nodeType: 'BLOG_POST'
});

// Returns available variables like {{title}}, {{author}}

Parameters

Name Type Description
nodeType required string Node type to get variables for

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'
  },
  access: 'PUBLIC',
  status: 'DRAFT',
  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: ['[email protected]']
    }
  ]
});

// Publish the post
await sdk.cms.updateNode({
  id: post.id,
  key: post.key,
  blocks: post.blocks,
  slug: post.slug,
  access: post.access,
  writeAccess: post.writeAccess,
  status: 'ACTIVE'
});
Tip

Use the key field for unique content like homepages or settings pages that should only exist once.