Skip to main content

Pages & Documents API

Overview

The Pages & Documents API provides a comprehensive headless Content Management System (CMS) that enables multi-channel content delivery, flexible content modeling, and sophisticated content workflows. Built for enterprise-scale content operations, this API powers dynamic websites, mobile applications, and omnichannel digital experiences through a powerful, API-first architecture.

Resource Description

The Pages & Documents system is organized around these core concepts:

Content Architecture

  • Projects: Top-level content containers that organize and isolate content for different brands, websites, or applications
  • Document Models: Content type definitions that define the structure and fields for documents
  • Slice Models: Reusable content components that can be embedded within documents
  • Documents: Actual content instances based on document models
  • Project Metadata: Configuration and settings for each project
  • Attachments: Media assets and files associated with content

Content Hierarchy

Project (Brand/Site)
├── Project Metadata (Settings & Configuration)
├── Document Models (Content Types)
│ ├── Page Model
│ ├── Article Model
│ ├── Product Model
│ └── Custom Models
├── Slice Models (Components)
│ ├── Hero Banner
│ ├── Content Grid
│ ├── Call-to-Action
│ └── Custom Slices
└── Documents (Content Instances)
├── Homepage
├── About Page
├── Blog Articles
└── Product Pages

Key Features

Content Management

  • Structured Content: Define custom content types with flexible field configurations
  • Component-Based Architecture: Build pages using reusable slice components
  • Multi-Project Support: Manage content for multiple brands or sites from a single platform
  • Version Control: Track content changes and maintain revision history
  • Draft/Publish Workflow: Separate draft and published content states

Content Modeling

  • Flexible Field Types: Text, rich text, numbers, dates, media, references, and custom fields
  • Nested Structures: Support for complex, nested content structures
  • Content Relationships: Link documents and create content hierarchies
  • Validation Rules: Enforce content quality with field-level validation
  • Default Values: Streamline content creation with predefined defaults

Content Delivery

  • RESTful API: Simple, predictable API for content retrieval
  • Query Flexibility: Filter, sort, and paginate content results
  • Multi-Channel Support: Deliver content to web, mobile, IoT, and other channels
  • SEO Optimization: Built-in support for SEO metadata and structured data
  • Performance Optimized: Efficient content delivery with caching strategies

Media Management

  • Secure Uploads: Presigned URLs for direct, secure file uploads
  • Asset Organization: Organize media by project and content type
  • Multiple Formats: Support for images, videos, documents, and other file types
  • CDN Integration: Automatic CDN distribution for optimal performance
  • Transformation Support: On-the-fly image resizing and optimization

Authentication

All endpoints require API key authentication via the X-API-Key header:

X-API-Key: your-api-key-here

Project Metadata Management

Project metadata defines the configuration and settings for each content project, including localization, publishing rules, and custom configurations.

List Project Metadata

Retrieve all project metadata configurations across your organization.

GET /pages/project_metadata/list

Parameters

ParameterTypeRequiredLocationDescription
namestringNoQueryFilter by project name
searchstringNoQuerySearch projects by keyword

Example Request

curl -X GET \
'https://apis.app.stack9.co/pages/project_metadata/list?name=corporate-site' \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"data": [
{
"id": "proj_meta_123",
"name": "corporate-site",
"displayName": "Corporate Website",
"description": "Main corporate website content",
"defaultLocale": "en-US",
"supportedLocales": ["en-US", "es-ES", "fr-FR"],
"settings": {
"enableVersioning": true,
"autoPublish": false,
"requireApproval": true,
"cdnEnabled": true
},
"customFields": {
"brand": "MainBrand",
"region": "North America",
"gaTrackingId": "UA-123456-1"
},
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-11-20T14:30:00Z"
},
{
"id": "proj_meta_456",
"name": "mobile-app",
"displayName": "Mobile App Content",
"description": "Content for iOS and Android apps",
"defaultLocale": "en-US",
"supportedLocales": ["en-US", "es-ES"],
"settings": {
"enableVersioning": true,
"autoPublish": true,
"requireApproval": false,
"cdnEnabled": true
}
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 2,
"totalPages": 1
}
}

Create/Update Project Metadata

Create a new project metadata configuration or update an existing one.

PUT /pages/project_metadata/{id}

Parameters

ParameterTypeRequiredLocationDescription
idstringYesPathUnique project identifier

Request Body

{
"name": "ecommerce-site",
"displayName": "E-commerce Platform",
"description": "Product catalog and content for e-commerce site",
"defaultLocale": "en-US",
"supportedLocales": ["en-US", "es-ES", "pt-BR"],
"settings": {
"enableVersioning": true,
"autoPublish": false,
"requireApproval": true,
"cdnEnabled": true,
"maxRevisions": 10,
"defaultStatus": "draft"
},
"customFields": {
"storefront": "main",
"currency": "USD",
"timezone": "America/New_York"
},
"seoDefaults": {
"titleTemplate": "{title} | E-commerce Store",
"defaultMetaTags": [
{"name": "viewport", "content": "width=device-width, initial-scale=1"},
{"property": "og:site_name", "content": "E-commerce Store"}
]
}
}

Example Request

curl -X PUT \
https://apis.app.stack9.co/pages/project_metadata/ecommerce-site \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "ecommerce-site",
"displayName": "E-commerce Platform",
"defaultLocale": "en-US",
"supportedLocales": ["en-US", "es-ES"]
}'

Example Response

{
"id": "ecommerce-site",
"name": "ecommerce-site",
"displayName": "E-commerce Platform",
"status": "created",
"createdAt": "2024-11-26T10:00:00Z"
}

Delete Project Metadata

Remove a project metadata configuration.

DELETE /pages/project_metadata/{id}

Parameters

ParameterTypeRequiredLocationDescription
idstringYesPathProject metadata identifier

Example Request

curl -X DELETE \
https://apis.app.stack9.co/pages/project_metadata/old-project \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"status": "deleted",
"id": "old-project",
"message": "Project metadata successfully deleted"
}

Document Models

Document models define the structure and schema for content types within a project. They serve as templates for creating documents.

List Document Models

Retrieve all document models for a specific project.

GET /pages/{project_id}/document-model

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
pagenumberNoQueryPage number (default: 1)
limitnumberNoQueryItems per page (default: 20, max: 100)
searchTextstringNoQuerySearch models by name or description

Example Request

curl -X GET \
'https://apis.app.stack9.co/pages/corporate-site/document-model?limit=10' \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"data": [
{
"id": "doc_model_page",
"name": "page",
"displayName": "Page",
"description": "Standard web page template",
"uid": "page",
"fields": [
{
"key": "title",
"type": "text",
"label": "Page Title",
"required": true,
"validation": {
"maxLength": 60
}
},
{
"key": "slug",
"type": "uid",
"label": "URL Slug",
"required": true,
"targetField": "title"
},
{
"key": "content",
"type": "richtext",
"label": "Page Content",
"required": false,
"config": {
"toolbar": ["bold", "italic", "link", "heading", "list"]
}
},
{
"key": "slices",
"type": "sliceZone",
"label": "Content Sections",
"config": {
"choices": ["hero_banner", "content_grid", "testimonial"]
}
},
{
"key": "seo",
"type": "group",
"label": "SEO Settings",
"fields": [
{
"key": "metaTitle",
"type": "text",
"label": "Meta Title",
"validation": {
"maxLength": 70
}
},
{
"key": "metaDescription",
"type": "textarea",
"label": "Meta Description",
"validation": {
"maxLength": 160
}
}
]
}
],
"status": "active",
"version": 2,
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-11-20T14:30:00Z"
},
{
"id": "doc_model_article",
"name": "article",
"displayName": "Blog Article",
"description": "Blog post template with author and categories",
"uid": "article",
"fields": [
{
"key": "title",
"type": "text",
"label": "Article Title",
"required": true
},
{
"key": "author",
"type": "reference",
"label": "Author",
"config": {
"documentType": "author"
}
},
{
"key": "publishDate",
"type": "date",
"label": "Publish Date",
"required": true
},
{
"key": "categories",
"type": "multiselect",
"label": "Categories",
"options": [
{"value": "tech", "label": "Technology"},
{"value": "business", "label": "Business"},
{"value": "design", "label": "Design"}
]
}
]
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 2,
"totalPages": 1
}
}

Create/Update Document Model

Create a new document model or update an existing one.

PUT /pages/{project_id}/document-model

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier

Request Body

{
"id": "product",
"name": "product",
"displayName": "Product",
"description": "E-commerce product template",
"uid": "product",
"fields": [
{
"key": "name",
"type": "text",
"label": "Product Name",
"required": true,
"validation": {
"minLength": 3,
"maxLength": 100
}
},
{
"key": "sku",
"type": "text",
"label": "SKU",
"required": true,
"unique": true,
"validation": {
"pattern": "^[A-Z0-9-]+$"
}
},
{
"key": "price",
"type": "number",
"label": "Price",
"required": true,
"validation": {
"min": 0,
"precision": 2
}
},
{
"key": "description",
"type": "richtext",
"label": "Product Description"
},
{
"key": "images",
"type": "media",
"label": "Product Images",
"multiple": true,
"config": {
"accept": "image/*",
"maxSize": 5242880
}
},
{
"key": "specifications",
"type": "json",
"label": "Technical Specifications"
},
{
"key": "category",
"type": "reference",
"label": "Product Category",
"required": true,
"config": {
"documentType": "category"
}
},
{
"key": "relatedProducts",
"type": "reference",
"label": "Related Products",
"multiple": true,
"config": {
"documentType": "product",
"maxItems": 4
}
},
{
"key": "status",
"type": "select",
"label": "Product Status",
"required": true,
"default": "draft",
"options": [
{"value": "draft", "label": "Draft"},
{"value": "active", "label": "Active"},
{"value": "discontinued", "label": "Discontinued"}
]
}
],
"settings": {
"singleton": false,
"sortable": true,
"searchable": ["name", "sku", "description"],
"slugField": "name"
}
}

Example Request

curl -X PUT \
https://apis.app.stack9.co/pages/ecommerce-site/document-model \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"id": "product",
"name": "product",
"displayName": "Product",
"fields": [...]
}'

Example Response

{
"id": "product",
"name": "product",
"displayName": "Product",
"status": "created",
"version": 1,
"createdAt": "2024-11-26T10:00:00Z"
}

Get Document Model

Retrieve a specific document model by ID.

GET /pages/{project_id}/document-model/{id}

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
idstringYesPathDocument model identifier

Example Request

curl -X GET \
https://apis.app.stack9.co/pages/corporate-site/document-model/page \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"id": "page",
"name": "page",
"displayName": "Page",
"description": "Standard web page template",
"uid": "page",
"fields": [
{
"key": "title",
"type": "text",
"label": "Page Title",
"required": true
}
],
"status": "active",
"version": 2,
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-11-20T14:30:00Z"
}

Delete Document Model

Remove a document model from a project.

DELETE /pages/{project_id}/document-model/{id}

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
idstringYesPathDocument model identifier

Example Request

curl -X DELETE \
https://apis.app.stack9.co/pages/corporate-site/document-model/old-template \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"status": "deleted",
"id": "old-template",
"message": "Document model successfully deleted"
}

Slice Models

Slice models define reusable content components that can be embedded within documents, enabling modular content composition.

List Slice Models

Retrieve all slice models for a specific project.

GET /pages/{project_id}/slice-model

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
pagenumberNoQueryPage number (default: 1)
limitnumberNoQueryItems per page (default: 20, max: 100)

Example Request

curl -X GET \
'https://apis.app.stack9.co/pages/corporate-site/slice-model?limit=10' \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"data": [
{
"id": "hero_banner",
"name": "hero_banner",
"displayName": "Hero Banner",
"description": "Full-width hero section with image and CTA",
"fields": [
{
"key": "heading",
"type": "text",
"label": "Heading",
"required": true,
"validation": {
"maxLength": 100
}
},
{
"key": "subheading",
"type": "text",
"label": "Subheading",
"validation": {
"maxLength": 200
}
},
{
"key": "backgroundImage",
"type": "media",
"label": "Background Image",
"required": true,
"config": {
"accept": "image/*"
}
},
{
"key": "ctaButton",
"type": "group",
"label": "Call to Action",
"fields": [
{
"key": "text",
"type": "text",
"label": "Button Text"
},
{
"key": "link",
"type": "link",
"label": "Button Link"
},
{
"key": "style",
"type": "select",
"label": "Button Style",
"options": [
{"value": "primary", "label": "Primary"},
{"value": "secondary", "label": "Secondary"}
]
}
]
}
],
"preview": {
"thumbnail": "/previews/hero_banner.png"
},
"status": "active",
"createdAt": "2024-01-15T10:00:00Z"
},
{
"id": "content_grid",
"name": "content_grid",
"displayName": "Content Grid",
"description": "Grid layout for displaying content cards",
"fields": [
{
"key": "title",
"type": "text",
"label": "Section Title"
},
{
"key": "columns",
"type": "select",
"label": "Number of Columns",
"default": "3",
"options": [
{"value": "2", "label": "2 Columns"},
{"value": "3", "label": "3 Columns"},
{"value": "4", "label": "4 Columns"}
]
},
{
"key": "items",
"type": "repeater",
"label": "Grid Items",
"fields": [
{
"key": "image",
"type": "media",
"label": "Card Image"
},
{
"key": "title",
"type": "text",
"label": "Card Title"
},
{
"key": "description",
"type": "textarea",
"label": "Card Description"
},
{
"key": "link",
"type": "link",
"label": "Card Link"
}
]
}
]
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 2,
"totalPages": 1
}
}

Create/Update Slice Model

Create a new slice model or update an existing one.

PUT /pages/{project_id}/slice-model

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier

Request Body

{
"id": "testimonial",
"name": "testimonial",
"displayName": "Customer Testimonial",
"description": "Display customer testimonials with ratings",
"fields": [
{
"key": "quote",
"type": "textarea",
"label": "Testimonial Quote",
"required": true,
"validation": {
"minLength": 20,
"maxLength": 500
}
},
{
"key": "author",
"type": "group",
"label": "Author Information",
"fields": [
{
"key": "name",
"type": "text",
"label": "Author Name",
"required": true
},
{
"key": "title",
"type": "text",
"label": "Job Title"
},
{
"key": "company",
"type": "text",
"label": "Company"
},
{
"key": "photo",
"type": "media",
"label": "Author Photo",
"config": {
"accept": "image/*"
}
}
]
},
{
"key": "rating",
"type": "number",
"label": "Rating",
"validation": {
"min": 1,
"max": 5
}
},
{
"key": "featured",
"type": "boolean",
"label": "Featured Testimonial",
"default": false
}
],
"settings": {
"icon": "quote",
"color": "#4A90E2"
}
}

Example Request

curl -X PUT \
https://apis.app.stack9.co/pages/corporate-site/slice-model \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"id": "testimonial",
"name": "testimonial",
"displayName": "Customer Testimonial",
"fields": [...]
}'

Example Response

{
"id": "testimonial",
"name": "testimonial",
"displayName": "Customer Testimonial",
"status": "created",
"createdAt": "2024-11-26T10:00:00Z"
}

Delete Slice Model

Remove a slice model from a project.

DELETE /pages/{project_id}/slice-model/{id}

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
idstringYesPathSlice model identifier

Example Request

curl -X DELETE \
https://apis.app.stack9.co/pages/corporate-site/slice-model/old-component \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"status": "deleted",
"id": "old-component",
"message": "Slice model successfully deleted"
}

Documents

Documents are the actual content instances created based on document models. They represent pages, articles, products, or any other content type.

Get Document

Retrieve a specific document by various identifiers.

GET /pages/{project_id}/document

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
idstringNo*QueryDocument ID
document_model_idstringNo*QueryFilter by document model
uidstringNo*QueryDocument UID/slug

*At least one query parameter is required to identify the document.

Example Request

curl -X GET \
'https://apis.app.stack9.co/pages/corporate-site/document?uid=homepage' \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"id": "doc_001",
"uid": "homepage",
"document_model_id": "page",
"document_model": {
"id": "page",
"name": "page",
"displayName": "Page"
},
"data": {
"title": "Welcome to Our Company",
"slug": "homepage",
"content": "<p>Welcome to our corporate website...</p>",
"slices": [
{
"slice_type": "hero_banner",
"slice_id": "slice_001",
"primary": {
"heading": "Transform Your Business",
"subheading": "Innovative solutions for modern challenges",
"backgroundImage": {
"url": "https://cdn.stack9.io/images/hero-bg.jpg",
"alt": "Hero background",
"dimensions": {
"width": 1920,
"height": 1080
}
},
"ctaButton": {
"text": "Get Started",
"link": "/contact",
"style": "primary"
}
}
},
{
"slice_type": "content_grid",
"slice_id": "slice_002",
"primary": {
"title": "Our Services",
"columns": "3",
"items": [
{
"title": "Consulting",
"description": "Expert guidance for your business",
"image": {
"url": "https://cdn.stack9.io/images/consulting.jpg"
},
"link": "/services/consulting"
},
{
"title": "Development",
"description": "Custom software solutions",
"image": {
"url": "https://cdn.stack9.io/images/development.jpg"
},
"link": "/services/development"
},
{
"title": "Support",
"description": "24/7 technical support",
"image": {
"url": "https://cdn.stack9.io/images/support.jpg"
},
"link": "/services/support"
}
]
}
}
],
"seo": {
"metaTitle": "Welcome to Our Company - Innovative Business Solutions",
"metaDescription": "Transform your business with our innovative solutions. Expert consulting, custom development, and 24/7 support."
}
},
"status": "published",
"publishedAt": "2024-11-20T10:00:00Z",
"version": 3,
"locale": "en-US",
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-11-20T10:00:00Z",
"createdBy": "user_123",
"updatedBy": "user_456"
}

List Documents

Retrieve a paginated list of documents with filtering options.

POST /pages/{project_id}/document/list

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier

Request Body

{
"filters": {
"document_model_id": "article",
"status": "published",
"locale": "en-US",
"createdAfter": "2024-01-01T00:00:00Z",
"tags": ["technology", "innovation"]
},
"sort": {
"field": "publishedAt",
"order": "desc"
},
"pagination": {
"page": 1,
"limit": 20
},
"fields": ["id", "uid", "title", "publishedAt", "author"]
}

Example Request

curl -X POST \
https://apis.app.stack9.co/pages/corporate-site/document/list \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"filters": {
"document_model_id": "article",
"status": "published"
},
"sort": {
"field": "publishedAt",
"order": "desc"
},
"pagination": {
"page": 1,
"limit": 10
}
}'

Example Response

{
"data": [
{
"id": "doc_article_001",
"uid": "introducing-new-features",
"document_model_id": "article",
"data": {
"title": "Introducing New Features for 2024",
"author": {
"id": "author_001",
"name": "Jane Smith"
},
"publishDate": "2024-11-25T09:00:00Z",
"categories": ["tech", "innovation"],
"excerpt": "Discover the latest features we're launching..."
},
"status": "published",
"publishedAt": "2024-11-25T09:00:00Z"
},
{
"id": "doc_article_002",
"uid": "best-practices-guide",
"document_model_id": "article",
"data": {
"title": "Best Practices for Content Management",
"author": {
"id": "author_002",
"name": "John Doe"
},
"publishDate": "2024-11-20T14:00:00Z",
"categories": ["business", "guide"],
"excerpt": "Learn the best practices for managing content..."
},
"status": "published",
"publishedAt": "2024-11-20T14:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 42,
"totalPages": 5
},
"aggregations": {
"byCategory": {
"tech": 15,
"business": 12,
"innovation": 8,
"guide": 7
},
"byStatus": {
"published": 35,
"draft": 7
}
}
}

Create/Update Document

Create a new document or update an existing one.

PUT /pages/{project_id}/document/{id}

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
idstringYesPathDocument identifier

Request Body

{
"document_model_id": "product",
"uid": "premium-laptop-x1",
"data": {
"name": "Premium Laptop X1",
"sku": "LAPTOP-X1-2024",
"price": 1299.99,
"description": "<p>Experience unparalleled performance with our Premium Laptop X1...</p>",
"images": [
{
"url": "https://cdn.stack9.io/products/laptop-x1-front.jpg",
"alt": "Laptop X1 Front View",
"dimensions": {
"width": 1000,
"height": 1000
}
},
{
"url": "https://cdn.stack9.io/products/laptop-x1-side.jpg",
"alt": "Laptop X1 Side View",
"dimensions": {
"width": 1000,
"height": 1000
}
}
],
"specifications": {
"processor": "Intel Core i7-13700H",
"memory": "32GB DDR5",
"storage": "1TB NVMe SSD",
"display": "15.6\" 4K OLED",
"graphics": "NVIDIA RTX 4060",
"battery": "90Wh, up to 12 hours",
"weight": "1.8kg"
},
"category": {
"id": "cat_laptops",
"name": "Laptops"
},
"relatedProducts": [
{"id": "doc_product_002"},
{"id": "doc_product_003"}
],
"status": "active"
},
"locale": "en-US",
"status": "draft",
"metadata": {
"tags": ["premium", "laptop", "business"],
"customFields": {
"featured": true,
"promotionEligible": true
}
}
}

Example Request

curl -X PUT \
https://apis.app.stack9.co/pages/ecommerce-site/document/prod_001 \
-H 'X-API-Key: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"document_model_id": "product",
"uid": "premium-laptop-x1",
"data": {
"name": "Premium Laptop X1",
"sku": "LAPTOP-X1-2024",
"price": 1299.99
},
"status": "draft"
}'

Example Response

{
"id": "prod_001",
"uid": "premium-laptop-x1",
"document_model_id": "product",
"status": "created",
"version": 1,
"createdAt": "2024-11-26T10:00:00Z"
}

Delete Document

Remove a document from a project.

DELETE /pages/{project_id}/document/{id}

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
idstringYesPathDocument identifier

Example Request

curl -X DELETE \
https://apis.app.stack9.co/pages/corporate-site/document/doc_old_001 \
-H 'X-API-Key: your-api-key-here'

Example Response

{
"status": "deleted",
"id": "doc_old_001",
"message": "Document successfully deleted"
}

Attachment Management

Manage media assets and file attachments for content with secure, presigned URLs.

Get Presigned URL

Generate a presigned URL for uploading or downloading attachments.

GET /pages/{project_id}/attachment/signed-url

Parameters

ParameterTypeRequiredLocationDescription
project_idstringYesPathProject identifier
object_refstringYesQueryObject reference/key for the attachment
intentstringYesQueryIntent: "upload" or "download"

Example Request (Upload)

curl -X GET \
'https://apis.app.stack9.co/pages/corporate-site/attachment/signed-url?object_ref=images/hero-banner.jpg&intent=upload' \
-H 'X-API-Key: your-api-key-here'

Example Response (Upload)

{
"url": "https://storage.stack9.io/corporate-site/images/hero-banner.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"method": "PUT",
"headers": {
"Content-Type": "image/jpeg"
},
"expiresAt": "2024-11-26T11:00:00Z",
"maxSize": 10485760,
"instructions": "Use PUT method with the provided URL to upload your file"
}

Example Upload Implementation

# Get presigned URL
RESPONSE=$(curl -X GET \
'https://apis.app.stack9.co/pages/corporate-site/attachment/signed-url?object_ref=images/hero.jpg&intent=upload' \
-H 'X-API-Key: your-api-key-here')

# Extract URL from response
UPLOAD_URL=$(echo $RESPONSE | jq -r '.url')

# Upload file
curl -X PUT \
"$UPLOAD_URL" \
-H 'Content-Type: image/jpeg' \
--data-binary '@/path/to/hero.jpg'

Example Request (Download)

curl -X GET \
'https://apis.app.stack9.co/pages/corporate-site/attachment/signed-url?object_ref=documents/whitepaper.pdf&intent=download' \
-H 'X-API-Key: your-api-key-here'

Example Response (Download)

{
"url": "https://cdn.stack9.io/corporate-site/documents/whitepaper.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"method": "GET",
"expiresAt": "2024-11-26T11:00:00Z",
"contentType": "application/pdf",
"contentLength": 2456789
}

Advanced Features

Content Versioning

Track and manage content versions for audit trails and rollback capabilities.

{
"document": {
"id": "doc_001",
"version": 3,
"versions": [
{
"version": 1,
"createdAt": "2024-01-15T10:00:00Z",
"createdBy": "user_123",
"changes": "Initial version"
},
{
"version": 2,
"createdAt": "2024-06-20T14:30:00Z",
"createdBy": "user_456",
"changes": "Updated content and added new sections"
},
{
"version": 3,
"createdAt": "2024-11-20T09:00:00Z",
"createdBy": "user_789",
"changes": "SEO optimization and image updates"
}
]
}
}

Multi-Language Support

Manage content in multiple languages with locale-specific versions.

{
"document": {
"id": "doc_001",
"defaultLocale": "en-US",
"locales": {
"en-US": {
"title": "Welcome to Our Company",
"content": "English content...",
"status": "published"
},
"es-ES": {
"title": "Bienvenido a Nuestra Empresa",
"content": "Contenido en español...",
"status": "published"
},
"fr-FR": {
"title": "Bienvenue dans Notre Entreprise",
"content": "Contenu français...",
"status": "draft"
}
}
}
}

Content Relationships

Define relationships between documents for complex content structures.

{
"document": {
"id": "article_001",
"relationships": {
"author": {
"type": "one-to-one",
"target": "author_001",
"model": "author"
},
"categories": {
"type": "many-to-many",
"targets": ["cat_001", "cat_002"],
"model": "category"
},
"relatedArticles": {
"type": "one-to-many",
"targets": ["article_002", "article_003"],
"model": "article"
}
}
}
}

Content Search and Filtering

Advanced search capabilities across content fields.

{
"search": {
"query": "innovation technology",
"filters": {
"document_model_id": ["article", "page"],
"status": "published",
"dateRange": {
"field": "publishedAt",
"from": "2024-01-01",
"to": "2024-12-31"
},
"fields": {
"categories": {
"operator": "IN",
"values": ["tech", "innovation"]
},
"price": {
"operator": "BETWEEN",
"min": 100,
"max": 1000
}
}
},
"facets": ["categories", "author", "status"],
"highlight": {
"fields": ["title", "content"],
"preTag": "<em>",
"postTag": "</em>"
}
}
}

Integration Examples

Headless CMS Implementation

Frontend Integration (React)

// pages-api.js
class PagesAPI {
constructor(apiKey, projectId) {
this.apiKey = apiKey;
this.projectId = projectId;
this.baseUrl = 'https://apis.app.stack9.co/pages';
}

async getDocument(uid) {
const response = await fetch(
`${this.baseUrl}/${this.projectId}/document?uid=${uid}`,
{
headers: {
'X-API-Key': this.apiKey
}
}
);
return response.json();
}

async listDocuments(modelId, options = {}) {
const response = await fetch(
`${this.baseUrl}/${this.projectId}/document/list`,
{
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
filters: {
document_model_id: modelId,
status: 'published',
...options.filters
},
sort: options.sort || { field: 'publishedAt', order: 'desc' },
pagination: options.pagination || { page: 1, limit: 20 }
})
}
);
return response.json();
}
}

// React Component
import React, { useState, useEffect } from 'react';

function BlogList() {
const [articles, setArticles] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
const api = new PagesAPI('your-api-key', 'corporate-site');

api.listDocuments('article', {
filters: { categories: ['tech'] },
pagination: { page: 1, limit: 10 }
})
.then(response => {
setArticles(response.data);
setLoading(false);
});
}, []);

if (loading) return <div>Loading...</div>;

return (
<div className="blog-list">
{articles.map(article => (
<article key={article.id}>
<h2>{article.data.title}</h2>
<p>{article.data.excerpt}</p>
<a href={`/blog/${article.uid}`}>Read More</a>
</article>
))}
</div>
);
}

Next.js Static Generation

// pages/[slug].js
import { PagesAPI } from '../lib/pages-api';

const api = new PagesAPI(process.env.STACK9_API_KEY, 'corporate-site');

export async function getStaticPaths() {
const response = await api.listDocuments('page');

const paths = response.data.map(doc => ({
params: { slug: doc.uid }
}));

return { paths, fallback: 'blocking' };
}

export async function getStaticProps({ params }) {
const document = await api.getDocument(params.slug);

if (!document) {
return { notFound: true };
}

return {
props: {
document
},
revalidate: 60 // ISR: regenerate page every 60 seconds
};
}

export default function Page({ document }) {
return (
<div>
<h1>{document.data.title}</h1>
<div dangerouslySetInnerHTML={{ __html: document.data.content }} />

{/* Render slices */}
{document.data.slices?.map((slice, index) => (
<SliceComponent key={index} slice={slice} />
))}
</div>
);
}

Multi-Brand Content Management

// multi-brand-cms.js
class MultiBrandCMS {
constructor(apiKey) {
this.apiKey = apiKey;
this.brands = {
'brand-a': 'project-brand-a',
'brand-b': 'project-brand-b',
'brand-c': 'project-brand-c'
};
}

async getContentForBrand(brand, contentType, uid) {
const projectId = this.brands[brand];
if (!projectId) throw new Error(`Unknown brand: ${brand}`);

const api = new PagesAPI(this.apiKey, projectId);
return api.getDocument(uid);
}

async getGlobalContent(contentType) {
const results = {};

for (const [brand, projectId] of Object.entries(this.brands)) {
const api = new PagesAPI(this.apiKey, projectId);
results[brand] = await api.listDocuments(contentType);
}

return results;
}
}

// Usage
const cms = new MultiBrandCMS('your-api-key');

// Get brand-specific content
const brandAHomepage = await cms.getContentForBrand('brand-a', 'page', 'homepage');

// Get content across all brands
const allProducts = await cms.getGlobalContent('product');

Content Workflow Automation

// content-workflow.js
class ContentWorkflow {
constructor(apiKey, projectId) {
this.api = new PagesAPI(apiKey, projectId);
}

async createDraftContent(modelId, data) {
// Create initial draft
const document = await this.api.createDocument({
document_model_id: modelId,
data: data,
status: 'draft',
metadata: {
workflow: 'pending_review',
createdAt: new Date().toISOString()
}
});

// Trigger review notification
await this.notifyReviewers(document.id);

return document;
}

async reviewContent(documentId, approved, comments) {
const update = {
metadata: {
workflow: approved ? 'approved' : 'needs_revision',
reviewedAt: new Date().toISOString(),
reviewComments: comments
}
};

if (approved) {
update.status = 'ready_to_publish';
}

return this.api.updateDocument(documentId, update);
}

async publishContent(documentId, scheduledDate = null) {
const update = {
status: 'published',
publishedAt: scheduledDate || new Date().toISOString(),
metadata: {
workflow: 'published'
}
};

const document = await this.api.updateDocument(documentId, update);

// Clear CDN cache
await this.invalidateCache(document.uid);

// Trigger published webhook
await this.triggerWebhook('content.published', document);

return document;
}

async schedulePublication(documentId, publishDate) {
// Schedule future publication
return this.api.updateDocument(documentId, {
metadata: {
workflow: 'scheduled',
scheduledPublishDate: publishDate
}
});
}
}

Best Practices

Content Modeling

1. Design Flexible Content Types

{
"document_model": {
"id": "flexible_page",
"name": "flexible_page",
"fields": [
{
"key": "title",
"type": "text",
"required": true
},
{
"key": "content_sections",
"type": "sliceZone",
"config": {
"choices": [
"hero_banner",
"text_block",
"image_gallery",
"video_embed",
"form_embed",
"product_grid"
]
}
}
]
}
}

2. Use Semantic Field Names

{
"fields": [
{
"key": "headline", // ✅ Clear purpose
"key": "field1" // ❌ Unclear
},
{
"key": "publishDate", // ✅ Descriptive
"key": "date" // ❌ Ambiguous
},
{
"key": "featuredImage", // ✅ Specific
"key": "image" // ❌ Generic
}
]
}

3. Implement Proper Validation

{
"fields": [
{
"key": "email",
"type": "text",
"validation": {
"pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"message": "Please enter a valid email address"
}
},
{
"key": "price",
"type": "number",
"validation": {
"min": 0,
"max": 999999.99,
"precision": 2,
"required": true
}
}
]
}

Performance Optimization

1. Implement Caching Strategies

class CachedPagesAPI extends PagesAPI {
constructor(apiKey, projectId, cacheConfig = {}) {
super(apiKey, projectId);
this.cache = new Map();
this.cacheTTL = cacheConfig.ttl || 300000; // 5 minutes default
}

async getDocument(uid, options = {}) {
const cacheKey = `doc_${uid}`;

// Check cache
if (!options.skipCache && this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey);
if (Date.now() - cached.timestamp < this.cacheTTL) {
return cached.data;
}
}

// Fetch fresh data
const document = await super.getDocument(uid);

// Update cache
this.cache.set(cacheKey, {
data: document,
timestamp: Date.now()
});

return document;
}

invalidate(pattern = null) {
if (pattern) {
// Invalidate matching keys
for (const key of this.cache.keys()) {
if (key.includes(pattern)) {
this.cache.delete(key);
}
}
} else {
// Clear all cache
this.cache.clear();
}
}
}

2. Optimize Content Queries

// ❌ Inefficient: Multiple API calls
const articles = await api.listDocuments('article');
for (const article of articles.data) {
const author = await api.getDocument(article.data.authorId);
article.author = author;
}

// ✅ Efficient: Include related data in query
const articles = await api.listDocuments('article', {
include: ['author', 'categories'],
fields: ['title', 'excerpt', 'publishDate', 'author.name']
});

3. Use Field Selection

// ❌ Fetching all fields when only few needed
const documents = await api.listDocuments('product');

// ✅ Select only required fields
const documents = await api.listDocuments('product', {
fields: ['id', 'name', 'price', 'image.url'],
pagination: { limit: 50 }
});

Security Considerations

1. Implement Access Control

class SecurePagesAPI extends PagesAPI {
async createDocument(data) {
// Validate permissions
if (!this.hasPermission('content.create')) {
throw new Error('Insufficient permissions');
}

// Sanitize input
const sanitized = this.sanitizeInput(data);

// Add audit trail
sanitized.metadata = {
...sanitized.metadata,
createdBy: this.currentUser.id,
createdAt: new Date().toISOString(),
ip: this.requestIp
};

return super.createDocument(sanitized);
}

sanitizeInput(data) {
// Remove potential XSS
if (data.content) {
data.content = DOMPurify.sanitize(data.content);
}

// Validate file uploads
if (data.images) {
data.images = data.images.filter(img =>
this.isValidImageType(img.type) &&
img.size < 5242880 // 5MB limit
);
}

return data;
}
}

2. Rate Limiting

const rateLimit = require('express-rate-limit');

const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP',
standardHeaders: true,
legacyHeaders: false,
});

app.use('/api/pages', apiLimiter);

Architecture Patterns

1. Repository Pattern

class DocumentRepository {
constructor(api) {
this.api = api;
}

async findById(id) {
return this.api.getDocument(id);
}

async findBySlug(slug) {
return this.api.getDocument(slug);
}

async findAll(criteria = {}) {
return this.api.listDocuments('page', criteria);
}

async create(data) {
return this.api.createDocument(data);
}

async update(id, data) {
return this.api.updateDocument(id, data);
}

async delete(id) {
return this.api.deleteDocument(id);
}

async publish(id) {
return this.update(id, { status: 'published' });
}

async unpublish(id) {
return this.update(id, { status: 'draft' });
}
}

2. Service Layer Pattern

class ContentService {
constructor(repository, cache, eventBus) {
this.repository = repository;
this.cache = cache;
this.eventBus = eventBus;
}

async getPublishedContent(slug) {
// Check cache first
const cached = await this.cache.get(`content:${slug}`);
if (cached) return cached;

// Fetch from repository
const content = await this.repository.findBySlug(slug);

if (content && content.status === 'published') {
// Cache for future requests
await this.cache.set(`content:${slug}`, content, 300);

// Track analytics
this.eventBus.emit('content.viewed', {
contentId: content.id,
slug: slug
});

return content;
}

return null;
}

async publishContent(id) {
const content = await this.repository.publish(id);

// Clear relevant caches
await this.cache.delete(`content:${content.slug}`);
await this.cache.delete('content:list');

// Notify subscribers
this.eventBus.emit('content.published', content);

// Trigger CDN purge
await this.purgeCDN(content.slug);

return content;
}
}

Error Responses

All API endpoints return consistent error responses:

400 Bad Request

{
"error": {
"code": "INVALID_REQUEST",
"message": "Invalid request parameters",
"details": {
"field": "document_model_id",
"reason": "Document model 'invalid_model' does not exist"
}
}
}

401 Unauthorized

{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
}

403 Forbidden

{
"error": {
"code": "FORBIDDEN",
"message": "You don't have permission to access this resource"
}
}

404 Not Found

{
"error": {
"code": "NOT_FOUND",
"message": "Document not found",
"details": {
"id": "doc_999",
"project_id": "corporate-site"
}
}
}

409 Conflict

{
"error": {
"code": "CONFLICT",
"message": "Document with UID 'homepage' already exists"
}
}

422 Unprocessable Entity

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"errors": [
{
"field": "title",
"message": "Title is required"
},
{
"field": "price",
"message": "Price must be a positive number"
}
]
}
}
}

429 Too Many Requests

{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"details": {
"limit": 100,
"window": "15 minutes",
"retryAfter": 900
}
}
}

500 Internal Server Error

{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred",
"requestId": "req_abc123"
}
}

Summary

The Pages & Documents API provides a complete headless CMS solution for managing multi-channel content delivery. With its flexible content modeling, component-based architecture, and powerful API, you can build sophisticated content experiences across web, mobile, and emerging digital channels.

Key capabilities include:

  • Flexible Content Architecture: Projects, models, slices, and documents
  • Multi-Project Support: Manage content for multiple brands/sites
  • Component-Based Design: Reusable content slices for modular composition
  • Advanced Content Features: Versioning, localization, and relationships
  • Secure Media Management: Presigned URLs for direct uploads
  • Performance Optimized: Built for scale with caching and CDN support

Whether you're building a simple website, a complex multi-brand platform, or a content-driven application, the Pages & Documents API provides the tools and flexibility needed for modern content management.