REST Endpoints

Base URL#

All API endpoints are served from your CMS Worker URL:

https://your-cms.your-account.workers.dev/api

During local development:

http://localhost:8787/api

Response Envelope#

Every API response follows the same envelope format:

{
  "data": [],
  "meta": {
    "count": 10,
    "timestamp": "2026-03-08T12:00:00.000Z",
    "cache": {
      "hit": false,
      "source": "database"
    },
    "timing": {
      "total": 45,
      "execution": 12,
      "unit": "ms"
    }
  }
}

The meta object always includes timing information so you can monitor performance. When the cache plugin is active, you'll also see cache.hit and cache.source fields.

Authentication Methods#

Endpoints use three auth levels:

LevelHowWhen
NoneNo auth neededPublic read endpoints
API KeyX-API-Key headerHeadless frontend reads
JWTAuthorization: Bearer <token> or auth_token cookieAdmin write operations

See Authentication for the full JWT flow and API Tokens for key-based access.

Content API#

These are the endpoints you'll use most often to read and write content.

MethodPathAuthDescription
GET/api/contentNoneList all content with filtering
GET/api/content/check-slugNoneCheck if a slug is available
GET/api/content/:idNoneGet a single content item by ID
POST/api/contentJWTCreate new content
PUT/api/content/:idJWTUpdate existing content
DELETE/api/content/:idJWTDelete content

Collection API#

MethodPathAuthDescription
GET/api/collectionsNoneList all active collections
GET/api/collections/:name/contentNoneGet content from a specific collection

Media API#

All media endpoints require JWT authentication.

MethodPathAuthDescription
GET/api/mediaJWTList all media files
POST/api/media/uploadJWTUpload a file to R2 storage
DELETE/api/media/:idJWTDelete a media file

System API#

MethodPathAuthDescription
GET/api/NoneOpenAPI 3.0 specification
GET/api/healthNoneBasic health check
GET/api/system/healthNoneDetailed health with DB/KV latency

Auth Endpoints#

MethodPathAuthDescription
POST/auth/loginNoneLogin with JSON body
POST/auth/login/formNoneLogin with form-encoded body
POST/auth/registerNoneRegister a new user (JSON)
POST/auth/logoutNoneClear auth cookie
GET/auth/logoutNoneClear auth cookie and redirect

Detailed Examples#

List content from a collection#

The most common operation -- fetching published content for your frontend.

curl:

curl "http://localhost:8787/api/collections/blog-posts/content?status=published&limit=10"

TypeScript:

const response = await fetch(
  'http://localhost:8787/api/collections/blog-posts/content?status=published&limit=10'
)
const { data, meta } = await response.json()
 
console.log(`Found ${meta.count} posts (${meta.timing.total}ms)`)
data.forEach(post => {
  console.log(post.title, post.slug)
})

Response:

{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "title": "Getting Started with Flare CMS",
      "slug": "getting-started-with-flare-cms",
      "status": "published",
      "collectionId": "coll-uuid-here",
      "data": {
        "content": "<p>Welcome to Flare CMS...</p>",
        "featuredImage": "https://images.flarecms.dev/uploads/hero.jpg"
      },
      "created_at": 1709856000000,
      "updated_at": 1709942400000
    }
  ],
  "meta": {
    "count": 1,
    "timestamp": "2026-03-08T12:00:00.000Z",
    "cache": { "hit": false, "source": "database" },
    "timing": { "total": 23, "execution": 8, "unit": "ms" }
  }
}

Get a single content item#

curl:

curl "http://localhost:8787/api/content/a1b2c3d4-e5f6-7890-abcd-ef1234567890"

TypeScript:

const response = await fetch(
  `http://localhost:8787/api/content/${contentId}`
)
const { data } = await response.json()
 
console.log(data.title)
console.log(data.data.content)

Response:

{
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "title": "Getting Started with Flare CMS",
    "slug": "getting-started-with-flare-cms",
    "status": "published",
    "collectionId": "coll-uuid-here",
    "data": {
      "content": "<p>Welcome to Flare CMS...</p>"
    },
    "created_at": 1709856000000,
    "updated_at": 1709942400000
  }
}

Create content#

Requires JWT authentication. Pass the token as a Bearer header or rely on the auth_token cookie.

curl:

curl -X POST "http://localhost:8787/api/content" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "collectionId": "coll-uuid-here",
    "title": "My New Post",
    "slug": "my-new-post",
    "status": "draft",
    "data": {
      "content": "<p>Hello world</p>",
      "excerpt": "A short summary"
    }
  }'

TypeScript:

const response = await fetch('http://localhost:8787/api/content', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`,
  },
  body: JSON.stringify({
    collectionId: 'coll-uuid-here',
    title: 'My New Post',
    slug: 'my-new-post',
    status: 'draft',
    data: {
      content: '<p>Hello world</p>',
      excerpt: 'A short summary',
    },
  }),
})
 
const { data } = await response.json()
console.log('Created:', data.id)

Response (201):

{
  "data": {
    "id": "new-uuid-here",
    "title": "My New Post",
    "slug": "my-new-post",
    "status": "draft",
    "collectionId": "coll-uuid-here",
    "data": {
      "content": "<p>Hello world</p>",
      "excerpt": "A short summary"
    },
    "created_at": 1709942400000,
    "updated_at": 1709942400000
  }
}

Upload media#

Files are uploaded via multipart/form-data and stored in Cloudflare R2.

curl:

curl -X POST "http://localhost:8787/api/media/upload" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -F "file=@./photo.jpg" \
  -F "folder=blog-images"

TypeScript:

const formData = new FormData()
formData.append('file', fileBlob, 'photo.jpg')
formData.append('folder', 'blog-images')
 
const response = await fetch('http://localhost:8787/api/media/upload', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
  },
  body: formData,
})
 
const result = await response.json()
console.log('Uploaded to:', result.data.url)

Error Responses#

All errors follow the same shape:

{
  "error": "Content not found"
}

Some errors include additional detail:

{
  "error": "Failed to create content",
  "details": "UNIQUE constraint failed: content.slug"
}

Common HTTP Status Codes#

CodeMeaning
200Success
201Created
400Bad request (invalid body or params)
401Not authenticated
403Insufficient permissions or blocked by hook
404Resource not found
409Conflict (duplicate slug, invalid status transition)
500Server error

CORS#

Cross-origin requests are controlled by the CORS_ORIGINS environment variable. Set it to a comma-separated list of allowed origins:

# wrangler.toml
[vars]
CORS_ORIGINS = "https://your-site.com,http://localhost:4321"

If CORS_ORIGINS is not set, all cross-origin requests are rejected (secure default).

Allowed methods: GET, POST, PUT, DELETE, OPTIONS Allowed headers: Content-Type, Authorization, X-API-Key

Rate Limiting#

Authentication endpoints are rate-limited:

  • Login: 5 attempts per minute per IP
  • Registration: 3 attempts per minute per IP

API content endpoints are not rate-limited by default but can be throttled via the cache plugin.

Cache Headers#

When the cache plugin is active, API responses include cache headers:

HeaderValueMeaning
X-Cache-StatusHIT or MISSWhether response came from cache
X-Cache-Sourcememory, kv, or databaseWhere the data came from
X-Cache-TTL300Remaining cache lifetime in seconds
X-Response-Time23msTotal response time