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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| name | string | No | Query | Filter by project name |
| search | string | No | Query | Search 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| id | string | Yes | Path | Unique 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| id | string | Yes | Path | Project 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| page | number | No | Query | Page number (default: 1) |
| limit | number | No | Query | Items per page (default: 20, max: 100) |
| searchText | string | No | Query | Search 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| id | string | Yes | Path | Document 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| id | string | Yes | Path | Document 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| page | number | No | Query | Page number (default: 1) |
| limit | number | No | Query | Items 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| id | string | Yes | Path | Slice 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| id | string | No* | Query | Document ID |
| document_model_id | string | No* | Query | Filter by document model |
| uid | string | No* | Query | Document 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| id | string | Yes | Path | Document 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| id | string | Yes | Path | Document 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
| Parameter | Type | Required | Location | Description |
|---|---|---|---|---|
| project_id | string | Yes | Path | Project identifier |
| object_ref | string | Yes | Query | Object reference/key for the attachment |
| intent | string | Yes | Query | Intent: "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.