Skip to main content

Overview

Skayle CMS allows Skayle to act as your primary headless content management system, exposing content through a JSON:API-compliant REST API. This is ideal when you:
  • Don’t have an existing CMS
  • Want to build a custom frontend
  • Need a simple, fast content API
  • Want full control over your content structure

How It Works

In Skayle CMS mode:
  1. Content is created and managed in Skayle
  2. Skayle exposes a REST API for your content
  3. Your frontend fetches content directly from Skayle
  4. No external CMS synchronization needed
┌─────────────┐     REST API     ┌─────────────┐
│   Skayle    │ ───────────────► │  Frontend   │
│   (CMS)     │  JSON responses  │  (Next.js,  │
│             │                  │   Astro,    │
└─────────────┘                  │   etc.)     │
                                 └─────────────┘

Setup Guide

Step 1: Enable Skayle CMS

  1. Go to Settings → Connectors in Skayle
  2. Select Skayle CMS as your connector type
  3. Configure your API settings

Step 2: Generate API Key

  1. Go to Settings → API Keys
  2. Click Generate New Key
  3. Set permissions (read-only or read-write)
  4. Copy the generated key
API keys provide access to your content. Use read-only keys for public frontends and keep write keys secure.

Step 3: Configure Your Frontend

Use the Skayle API endpoint in your frontend:
const SKAYLE_API = "https://api.skayle.ai/v1";
const API_KEY = process.env.SKAYLE_API_KEY;

async function getPosts() {
  const response = await fetch(`${SKAYLE_API}/posts`, {
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    }
  });
  return response.json();
}

API Endpoints

Posts

MethodEndpointDescription
GET/postsList all posts
GET/posts/:idGet single post
GET/posts/slug/:slugGet post by slug

Categories

MethodEndpointDescription
GET/categoriesList all categories
GET/categories/:idGet single category
GET/categories/:id/postsGet posts in category

Tags

MethodEndpointDescription
GET/tagsList all tags
GET/tags/:idGet single tag
GET/tags/:id/postsGet posts with tag

Authors

MethodEndpointDescription
GET/authorsList all authors
GET/authors/:idGet single author
GET/authors/:id/postsGet posts by author

Media

MethodEndpointDescription
GET/mediaList all media
GET/media/:idGet single media item

Response Format

All responses follow JSON:API specification:

Single Resource

{
  "data": {
    "id": "post-123",
    "type": "post",
    "attributes": {
      "title": "Getting Started with Skayle",
      "slug": "getting-started-with-skayle",
      "excerpt": "Learn how to use Skayle...",
      "content": "<p>Full HTML content...</p>",
      "publishedAt": "2024-01-15T10:00:00Z",
      "createdAt": "2024-01-14T08:30:00Z",
      "updatedAt": "2024-01-15T09:45:00Z"
    },
    "relationships": {
      "author": {
        "data": { "id": "author-1", "type": "author" }
      },
      "categories": {
        "data": [
          { "id": "cat-1", "type": "category" }
        ]
      },
      "tags": {
        "data": [
          { "id": "tag-1", "type": "tag" },
          { "id": "tag-2", "type": "tag" }
        ]
      }
    }
  },
  "included": [
    {
      "id": "author-1",
      "type": "author",
      "attributes": {
        "name": "John Doe",
        "slug": "john-doe"
      }
    }
  ]
}

Collection

{
  "data": [
    { "id": "post-1", "type": "post", "attributes": {...} },
    { "id": "post-2", "type": "post", "attributes": {...} }
  ],
  "meta": {
    "total": 42,
    "page": 1,
    "perPage": 10,
    "totalPages": 5
  },
  "links": {
    "self": "/posts?page=1",
    "first": "/posts?page=1",
    "last": "/posts?page=5",
    "next": "/posts?page=2"
  }
}

Query Parameters

Pagination

GET /posts?page=2&perPage=20
ParameterDefaultMaxDescription
page1-Page number
perPage10100Items per page

Filtering

GET /posts?filter[status]=published
GET /posts?filter[category]=technology
GET /posts?filter[author]=john-doe

Sorting

GET /posts?sort=-publishedAt    # Newest first
GET /posts?sort=title           # Alphabetical
GET /posts?sort=-updatedAt      # Recently updated

Including Relations

GET /posts?include=author,categories,tags
GET /posts/123?include=author

Sparse Fieldsets

GET /posts?fields[post]=title,slug,excerpt
GET /posts?fields[author]=name

Content Formats

HTML Content

By default, content is returned as HTML:
{
  "content": "<h2>Introduction</h2><p>Welcome to...</p>"
}

Raw BlockNote JSON

Request raw editor format:
GET /posts/123?format=raw
{
  "content": {
    "type": "doc",
    "content": [
      {
        "type": "heading",
        "attrs": { "level": 2 },
        "content": [{ "type": "text", "text": "Introduction" }]
      }
    ]
  }
}

Framework Examples

// lib/skayle.ts
const API_URL = process.env.SKAYLE_API_URL;
const API_KEY = process.env.SKAYLE_API_KEY;

export async function getPosts() {
  const res = await fetch(`${API_URL}/posts?include=author,categories`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
    next: { revalidate: 60 }
  });
  return res.json();
}

export async function getPost(slug: string) {
  const res = await fetch(`${API_URL}/posts/slug/${slug}?include=author`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
    next: { revalidate: 60 }
  });
  return res.json();
}

// app/blog/page.tsx
export default async function BlogPage() {
  const { data: posts } = await getPosts();
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.attributes.title}</li>
      ))}
    </ul>
  );
}

Webhooks

Configure webhooks to receive notifications when content changes:

Available Events

EventDescription
post.createdNew post created
post.updatedPost updated
post.publishedPost published
post.unpublishedPost unpublished
post.deletedPost deleted

Webhook Payload

{
  "event": "post.published",
  "timestamp": "2024-01-15T10:00:00Z",
  "data": {
    "id": "post-123",
    "type": "post",
    "attributes": {
      "title": "New Post",
      "slug": "new-post"
    }
  }
}

Setting Up Webhooks

  1. Go to Settings → Webhooks
  2. Click Add Webhook
  3. Enter your endpoint URL
  4. Select events to subscribe to
  5. Save and test
Use webhooks to trigger rebuilds of static sites or invalidate caches when content changes.

Caching Strategies

CDN Caching

Skayle sets appropriate cache headers:
Cache-Control: public, max-age=60, stale-while-revalidate=300

ISR (Incremental Static Regeneration)

For Next.js or similar frameworks:
export const revalidate = 60; // Revalidate every 60 seconds

On-Demand Revalidation

Use webhooks to trigger revalidation:
// pages/api/revalidate.ts
export default async function handler(req, res) {
  const { slug } = req.body.data.attributes;
  await res.revalidate(`/blog/${slug}`);
  return res.json({ revalidated: true });
}

Rate Limits

PlanRequests/MinuteRequests/Day
Free6010,000
Pro300100,000
EnterpriseCustomCustom

Best Practices

  1. Use caching: Implement proper caching to reduce API calls
  2. Include relations: Fetch related data in single requests
  3. Use sparse fieldsets: Only request fields you need
  4. Set up webhooks: Automate cache invalidation
  5. Monitor usage: Track API usage to stay within limits

Next Steps