SonicJS AI provides a comprehensive REST API for managing content, collections, media, and authentication programmatically. The API is built on Cloudflare Workers and features a three-tiered caching system for optimal performance.
https://your-domain.com/api
For local development:
http://localhost:8787/api
Most API endpoints require authentication. SonicJS AI uses JWT (JSON Web Tokens) for authentication with HTTP-only cookies for web clients and Bearer tokens for API clients.
Endpoint: POST /auth/login
curl -X POST "http://localhost:8787/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@sonicjs.com",
"password": "sonicjs!"
}'Request Body:
{
"email": "user@example.com",
"password": "your-password"
}Response (200 OK):
{
"user": {
"id": "admin-user-id",
"email": "admin@sonicjs.com",
"username": "admin",
"firstName": "Admin",
"lastName": "User",
"role": "admin"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJhZG1pbi11c2VyLWlkIiwiZW1haWwiOiJhZG1pbkBzb25pY2pzLmNvbSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTczMDk0MDAwMCwiaWF0IjoxNzMwODUzNjAwfQ.xyz"
}Error Response (401 Unauthorized):
{
"error": "Invalid email or password"
}Include the token in the Authorization header for all authenticated requests:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...For browser-based applications, the token is automatically stored as an HTTP-only cookie named auth_token.
Endpoint: POST /auth/refresh
Requires existing valid authentication.
curl -X POST "http://localhost:8787/auth/refresh" \
-H "Authorization: Bearer {token}"Response (200 OK):
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Endpoint: POST /auth/register
curl -X POST "http://localhost:8787/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "newuser@example.com",
"password": "securepassword123",
"username": "newuser",
"firstName": "John",
"lastName": "Doe"
}'Request Body:
{
"email": "newuser@example.com",
"password": "securepassword123",
"username": "newuser",
"firstName": "John",
"lastName": "Doe"
}Response (201 Created):
{
"user": {
"id": "uuid-generated-id",
"email": "newuser@example.com",
"username": "newuser",
"firstName": "John",
"lastName": "Doe",
"role": "viewer"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Endpoint: GET /auth/me
Requires authentication.
curl -X GET "http://localhost:8787/auth/me" \
-H "Authorization: Bearer {token}"Response (200 OK):
{
"user": {
"id": "admin-user-id",
"email": "admin@sonicjs.com",
"username": "admin",
"first_name": "Admin",
"last_name": "User",
"role": "admin",
"created_at": 1730000000
}
}Endpoint: POST /auth/logout or GET /auth/logout
curl -X POST "http://localhost:8787/auth/logout"Response (200 OK):
{
"message": "Logged out successfully"
}Endpoint: GET /api/health
Check API health and view available schemas.
Authentication: None required
curl -X GET "http://localhost:8787/api/health"Response (200 OK):
{
"status": "healthy",
"timestamp": "2025-10-06T12:34:56.789Z",
"schemas": [
"users",
"blog-posts",
"pages",
"products"
]
}Endpoint: GET /api/
Get the complete OpenAPI 3.0 specification.
Authentication: None required
curl -X GET "http://localhost:8787/api/"Response (200 OK):
{
"openapi": "3.0.0",
"info": {
"title": "SonicJS AI API",
"version": "0.1.0",
"description": "RESTful API for SonicJS AI - A modern headless CMS built for Cloudflare's edge platform"
},
"servers": [
{
"url": "http://localhost:8787",
"description": "Current server"
}
],
"paths": {
"/api/health": {...},
"/api/collections": {...},
"/api/content": {...}
}
}Endpoint: GET /api/collections
Retrieve all active collections with their schemas.
Authentication: None required
Cache Headers: Cached for 5 minutes (API cache tier)
curl -X GET "http://localhost:8787/api/collections"Response (200 OK):
{
"data": [
{
"id": "blog-posts-collection-id",
"name": "blog-posts",
"display_name": "Blog Posts",
"description": "Blog posts collection",
"schema": {
"name": "blog-posts",
"displayName": "Blog Posts",
"fields": [
{
"name": "title",
"type": "text",
"label": "Title",
"required": true
},
{
"name": "content",
"type": "richtext",
"label": "Content"
},
{
"name": "excerpt",
"type": "textarea",
"label": "Excerpt"
},
{
"name": "featuredImage",
"type": "media",
"label": "Featured Image"
}
]
},
"is_active": 1,
"created_at": 1730000000,
"updated_at": 1730000000
}
],
"meta": {
"count": 1,
"timestamp": "2025-10-06T12:34:56.789Z",
"cache": {
"hit": false,
"source": "database"
},
"timing": {
"total": 45,
"execution": 12,
"unit": "ms"
}
}
}Response Headers:
X-Cache-Status: MISS
X-Cache-Source: database
X-Response-Time: 45ms
Cached Response Headers:
X-Cache-Status: HIT
X-Cache-Source: kv
X-Cache-TTL: 234
X-Response-Time: 8ms
Endpoint: GET /api/content
Retrieve all content items with pagination.
Authentication: None required
Anonymous, viewer, and author requests return published content only. Admin and editor requests may filter by other statuses.
Cache Headers: Cached for 5 minutes (API cache tier)
Query Parameters:
limit(optional): Maximum number of items to return (default: 50, max: 100)status(optional): Content status filter for admin/editor requests. Anonymous, viewer, and author requests are limited to published results.
curl -X GET "http://localhost:8787/api/content?limit=10"Response (200 OK):
{
"data": [
{
"id": "content-uuid-1",
"title": "Welcome to SonicJS AI",
"slug": "welcome-to-sonicjs-ai",
"status": "published",
"collectionId": "blog-posts-collection-id",
"data": {
"title": "Welcome to SonicJS AI",
"content": "<p>This is the content of my first blog post...</p>",
"excerpt": "Getting started with SonicJS AI",
"featuredImage": {
"id": "media-id-1",
"url": "https://pub-sonicjs-media-dev.r2.dev/uploads/image.jpg"
}
},
"created_at": "2025-10-05T10:30:00.000Z",
"updated_at": "2025-10-05T10:30:00.000Z"
}
],
"meta": {
"count": 1,
"timestamp": "2025-10-06T12:34:56.789Z",
"cache": {
"hit": true,
"source": "memory",
"ttl": 278
},
"timing": {
"total": 3,
"execution": 1,
"unit": "ms"
}
}
}Response Headers:
X-Cache-Status: HIT
X-Cache-Source: memory
X-Cache-TTL: 278
X-Response-Time: 3ms
Endpoint: GET /api/collections/{collection}/content
Retrieve content for a specific collection.
Authentication: None required
Anonymous, viewer, and author requests return published content only. Admin and editor requests may filter by other statuses.
Cache Headers: Cached for 5 minutes (API cache tier)
Path Parameters:
collection(required): Collection name (e.g., "blog-posts")
Query Parameters:
limit(optional): Maximum number of items to return (default: 50, max: 100)status(optional): Content status filter for admin/editor requests. Anonymous, viewer, and author requests are limited to published results.
curl -X GET "http://localhost:8787/api/collections/blog-posts/content?limit=25"Response (200 OK):
{
"data": [
{
"id": "content-uuid-1",
"title": "Welcome to SonicJS AI",
"slug": "welcome-to-sonicjs-ai",
"status": "published",
"collectionId": "blog-posts-collection-id",
"data": {
"title": "Welcome to SonicJS AI",
"content": "<p>This is my blog post content...</p>"
},
"created_at": "2025-10-05T10:30:00.000Z",
"updated_at": "2025-10-05T10:30:00.000Z"
}
],
"meta": {
"collection": {
"id": "blog-posts-collection-id",
"name": "blog-posts",
"display_name": "Blog Posts",
"schema": {...}
},
"count": 1,
"timestamp": "2025-10-06T12:34:56.789Z",
"cache": {
"hit": false,
"source": "database"
},
"timing": {
"total": 28,
"execution": 15,
"unit": "ms"
}
}
}Error Response (404 Not Found):
{
"error": "Collection not found"
}All media endpoints require authentication.
Endpoint: POST /api/media/upload
Authentication: Required (Bearer token or auth cookie)
Content-Type: multipart/form-data
Supported File Types:
- Images: JPEG, PNG, GIF, WebP, SVG
- Documents: PDF, TXT, DOC, DOCX
- Videos: MP4, WebM, OGG, AVI, MOV
- Audio: MP3, WAV, OGG, M4A
Max File Size: 50MB
curl -X POST "http://localhost:8787/api/media/upload" \
-H "Authorization: Bearer {token}" \
-F "file=@/path/to/image.jpg" \
-F "folder=blog-images"Request Body (multipart/form-data):
file(required): The file to uploadfolder(optional): Folder path (default: "uploads")
Response (200 OK):
{
"success": true,
"file": {
"id": "V1StGXR8_Z5jdHi6B",
"filename": "V1StGXR8_Z5jdHi6B.jpg",
"originalName": "my-image.jpg",
"mimeType": "image/jpeg",
"size": 245678,
"width": 1920,
"height": 1080,
"publicUrl": "https://pub-sonicjs-media-dev.r2.dev/blog-images/V1StGXR8_Z5jdHi6B.jpg",
"thumbnailUrl": "https://imagedelivery.net/account-id/blog-images/V1StGXR8_Z5jdHi6B.jpg/thumbnail",
"uploadedAt": "2025-10-06T12:34:56.789Z"
}
}Error Response (400 Bad Request):
{
"error": "File validation failed",
"details": [
{
"code": "too_big",
"message": "File size exceeds 50MB limit",
"path": ["size"]
}
]
}Endpoint: POST /api/media/upload-multiple
Authentication: Required
curl -X POST "http://localhost:8787/api/media/upload-multiple" \
-H "Authorization: Bearer {token}" \
-F "files=@/path/to/image1.jpg" \
-F "files=@/path/to/image2.png" \
-F "folder=gallery"Request Body (multipart/form-data):
files(required): Multiple files to uploadfolder(optional): Folder path (default: "uploads")
Response (200 OK):
{
"success": true,
"uploaded": [
{
"id": "file-id-1",
"filename": "file-id-1.jpg",
"originalName": "image1.jpg",
"mimeType": "image/jpeg",
"size": 123456,
"publicUrl": "https://pub-sonicjs-media-dev.r2.dev/gallery/file-id-1.jpg",
"uploadedAt": "2025-10-06T12:34:56.789Z"
},
{
"id": "file-id-2",
"filename": "file-id-2.png",
"originalName": "image2.png",
"mimeType": "image/png",
"size": 234567,
"publicUrl": "https://pub-sonicjs-media-dev.r2.dev/gallery/file-id-2.png",
"uploadedAt": "2025-10-06T12:34:56.789Z"
}
],
"errors": [],
"summary": {
"total": 2,
"successful": 2,
"failed": 0
}
}Endpoint: DELETE /api/media/{id}
Authentication: Required (uploader or admin role)
Path Parameters:
id(required): File ID
curl -X DELETE "http://localhost:8787/api/media/V1StGXR8_Z5jdHi6B" \
-H "Authorization: Bearer {token}"Response (200 OK):
{
"success": true,
"message": "File deleted successfully"
}Error Response (403 Forbidden):
{
"error": "Permission denied"
}Error Response (404 Not Found):
{
"error": "File not found"
}Endpoint: POST /api/media/bulk-delete
Authentication: Required (uploader or admin role)
Max Files Per Operation: 50
curl -X POST "http://localhost:8787/api/media/bulk-delete" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"fileIds": ["file-id-1", "file-id-2", "file-id-3"]
}'Request Body:
{
"fileIds": ["file-id-1", "file-id-2"]
}Response (200 OK):
{
"success": true,
"deleted": [
{
"fileId": "file-id-1",
"filename": "image1.jpg",
"success": true
}
],
"errors": [
{
"fileId": "file-id-2",
"error": "Permission denied"
}
],
"summary": {
"total": 2,
"successful": 1,
"failed": 1
}
}Endpoint: PATCH /api/media/{id}
Authentication: Required (uploader or admin role)
Path Parameters:
id(required): File ID
Allowed Fields:
alt: Alt text for imagescaption: Image captiontags: Array of tagsfolder: Move file to different folder
curl -X PATCH "http://localhost:8787/api/media/V1StGXR8_Z5jdHi6B" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"alt": "Beautiful sunset over the ocean",
"caption": "Sunset at Malibu Beach",
"tags": ["nature", "sunset", "ocean"]
}'Request Body:
{
"alt": "Beautiful sunset over the ocean",
"caption": "Sunset at Malibu Beach",
"tags": ["nature", "sunset", "ocean"],
"folder": "featured-images"
}Response (200 OK):
{
"success": true,
"message": "File updated successfully"
}SonicJS AI features a three-tiered caching system:
-
Tier 1: In-Memory Cache (fastest, region-specific)
- Sub-millisecond access
- Regional edge location only
- Used for hot data
-
Tier 2: Cloudflare KV (fast, global)
- 1-10ms access globally
- Distributed across all edge locations
- Used for frequently accessed data
-
Tier 3: Database (source of truth)
- Direct database queries
- Used when cache misses occur
All cached responses include the following headers:
X-Cache-Status:HITorMISSX-Cache-Source:memory,kv, ordatabaseX-Cache-TTL: Time remaining in cache (seconds) - only on HITX-Response-Time: Total response time in milliseconds
| Entity Type | TTL | Memory Cache | KV Cache |
|---|---|---|---|
| API responses | 5 minutes | ✓ | ✓ |
| Collections | 2 hours | ✓ | ✓ |
| Content | 1 hour | ✓ | ✓ |
| User data | 15 minutes | ✓ | ✓ |
| Media metadata | 1 hour | ✓ | ✓ |
| Configuration | 2 hours | ✓ | ✓ |
| Session data | 30 minutes | ✓ | ✗ |
Cache is automatically invalidated on:
- Content updates:
content.update,content.delete,content.publish - User updates:
user.update,user.delete,auth.login - Media updates:
media.upload,media.delete,media.update - Collection updates:
collection.update,collection.delete
The API uses standard HTTP status codes to indicate success or failure:
200 OK- Request succeeded201 Created- Resource created successfully204 No Content- Request succeeded with no content to return
400 Bad Request- Invalid request parameters or body401 Unauthorized- Authentication required or token invalid403 Forbidden- Insufficient permissions404 Not Found- Resource not found422 Unprocessable Entity- Validation failed
500 Internal Server Error- Server error occurred503 Service Unavailable- Service temporarily unavailable
All error responses follow this format:
{
"error": "Human-readable error message",
"details": {
"field": "fieldName",
"code": "ERROR_CODE",
"message": "Detailed error description"
}
}Examples:
Validation Error (400):
{
"error": "File validation failed",
"details": [
{
"code": "invalid_type",
"message": "Unsupported file type",
"path": ["type"]
}
]
}Authentication Error (401):
{
"error": "Invalid or expired token"
}Permission Error (403):
{
"error": "Permission denied"
}Not Found Error (404):
{
"error": "Collection not found"
}Server Error (500):
{
"error": "Failed to fetch collections"
}Note: Rate limiting is planned but not currently implemented. Future versions will include:
- Authenticated requests: 1000 requests per hour per user
- Unauthenticated requests: 100 requests per hour per IP
Rate limit headers (planned):
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1730000000Most list endpoints support pagination through query parameters.
limit: Number of items per page (default: 50, max: 100)offset: Number of items to skip (default: 0)page: Page number (alternative to offset, 1-based)
Example with limit:
curl -X GET "http://localhost:8787/api/content?limit=10"Example with offset:
curl -X GET "http://localhost:8787/api/content?limit=10&offset=20"Example with page:
curl -X GET "http://localhost:8787/api/content?limit=10&page=3"{
"data": [...],
"meta": {
"count": 10,
"limit": 10,
"offset": 20,
"page": 3,
"totalPages": 10,
"totalItems": 95
}
}SonicJS AI provides OpenAPI 3.0 specifications for all endpoints.
-
JSON Specification
curl -X GET "http://localhost:8787/api/" -
Scalar UI (Modern interactive documentation)
- Visit:
http://localhost:8787/docs - Built-in API playground
- Real-time testing
- Visit:
-
Swagger UI (Traditional interface)
- Visit:
http://localhost:8787/docs/swagger - Classic OpenAPI experience
- Visit:
-
Raw OpenAPI JSON
- Visit:
http://localhost:8787/docs/openapi.json - Download specification file
- Visit:
Import the specification into tools like:
- Postman: Import → Link →
http://localhost:8787/api/ - Insomnia: Import → URL →
http://localhost:8787/api/ - OpenAPI Generator: Generate client SDKs in any language
- Stoplight Studio: API design and documentation
Get Collections:
const response = await fetch('http://localhost:8787/api/collections', {
headers: {
'Content-Type': 'application/json'
}
})
const { data, meta } = await response.json()
console.log(`Retrieved ${meta.count} collections`)
console.log('Cache status:', meta.cache.hit ? 'HIT' : 'MISS')Authenticated Request:
const token = 'your-jwt-token'
const response = await fetch('http://localhost:8787/api/media/upload', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
},
body: formData
})
const result = await response.json()
if (result.success) {
console.log('File uploaded:', result.file.publicUrl)
}Login and Get Content:
// Login
const loginResponse = await fetch('http://localhost:8787/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'admin@sonicjs.com',
password: 'sonicjs!'
})
})
const { token } = await loginResponse.json()
// Get content
const contentResponse = await fetch('http://localhost:8787/api/content?limit=25', {
headers: {
'Authorization': `Bearer ${token}`
}
})
const { data } = await contentResponse.json()
console.log(`Retrieved ${data.length} content items`)Install:
pip install requestsGet Collections:
import requests
response = requests.get('http://localhost:8787/api/collections')
data = response.json()
print(f"Retrieved {data['meta']['count']} collections")
print(f"Cache status: {data['meta']['cache']['hit']}")
print(f"Response time: {data['meta']['timing']['total']}ms")
for collection in data['data']:
print(f"- {collection['display_name']} ({collection['name']})")Upload File:
import requests
# Login first
login_response = requests.post(
'http://localhost:8787/auth/login',
json={
'email': 'admin@sonicjs.com',
'password': 'sonicjs!'
}
)
token = login_response.json()['token']
# Upload file
files = {'file': open('image.jpg', 'rb')}
headers = {'Authorization': f'Bearer {token}'}
response = requests.post(
'http://localhost:8787/api/media/upload',
headers=headers,
files=files,
data={'folder': 'uploads'}
)
result = response.json()
if result['success']:
print(f"File uploaded: {result['file']['publicUrl']}")
print(f"File ID: {result['file']['id']}")Paginated Content Fetch:
import requests
def get_all_content(base_url, token=None, limit=50):
all_items = []
offset = 0
headers = {}
if token:
headers['Authorization'] = f'Bearer {token}'
while True:
response = requests.get(
f'{base_url}/api/content',
params={'limit': limit, 'offset': offset},
headers=headers
)
data = response.json()
items = data['data']
all_items.extend(items)
if len(items) < limit:
break
offset += limit
return all_items
content = get_all_content('http://localhost:8787')
print(f"Total content items: {len(content)}")Get Collections:
curl -X GET "http://localhost:8787/api/collections" \
-H "Content-Type: application/json"Login:
curl -X POST "http://localhost:8787/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@sonicjs.com",
"password": "sonicjs!"
}'Get Content with Authentication:
TOKEN="your-jwt-token"
curl -X GET "http://localhost:8787/api/content?limit=25" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json"Upload File:
TOKEN="your-jwt-token"
curl -X POST "http://localhost:8787/api/media/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@/path/to/image.jpg" \
-F "folder=blog-images"Get Collection Content:
curl -X GET "http://localhost:8787/api/collections/blog-posts/content?limit=10" \
-H "Content-Type: application/json"Delete Media File:
TOKEN="your-jwt-token"
curl -X DELETE "http://localhost:8787/api/media/V1StGXR8_Z5jdHi6B" \
-H "Authorization: Bearer $TOKEN"Bulk Delete Media:
TOKEN="your-jwt-token"
curl -X POST "http://localhost:8787/api/media/bulk-delete" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fileIds": ["file-id-1", "file-id-2", "file-id-3"]
}'View Response Headers:
curl -X GET "http://localhost:8787/api/collections" \
-H "Content-Type: application/json" \
-iExample with Cache Headers:
# First request (cache MISS)
curl -X GET "http://localhost:8787/api/content?limit=10" -i
# Response headers:
# X-Cache-Status: MISS
# X-Cache-Source: database
# X-Response-Time: 45ms
# Second request (cache HIT)
curl -X GET "http://localhost:8787/api/content?limit=10" -i
# Response headers:
# X-Cache-Status: HIT
# X-Cache-Source: memory
# X-Cache-TTL: 294
# X-Response-Time: 2ms- Store tokens securely: Never expose tokens in client-side code or version control
- Use HTTPS in production: Always use secure connections for token transmission
- Refresh tokens regularly: Implement token refresh before expiration
- Handle 401 errors: Redirect to login or refresh token when authentication fails
- Leverage cache headers: Check
X-Cache-Statusto monitor cache effectiveness - Use appropriate limits: Request only the data you need with
limitparameter - Cache on client side: Store frequently accessed data in client cache
- Monitor cache TTL: Be aware of cache expiration times
- Batch operations: Use bulk endpoints for multiple operations
- Optimize queries: Use filters and limits to reduce data transfer
- Monitor response times: Track
X-Response-Timeheader for performance insights - Use collections endpoint: Fetch specific collection content rather than all content
- Handle all status codes: Implement proper error handling for 4xx and 5xx responses
- Parse error details: Use error.details for specific validation errors
- Implement retry logic: Retry failed requests with exponential backoff
- Log errors: Track API errors for debugging and monitoring
- Documentation: http://localhost:8787/docs
- OpenAPI Playground: http://localhost:8787/docs
- GitHub Repository: https://github.com/lane711/sonicjs-ai
- Community Discussions: https://github.com/lane711/sonicjs-ai/discussions
- Report Issues: https://github.com/lane711/sonicjs-ai/issues
- Initial API implementation
- JWT authentication
- Collections and content endpoints
- Media upload and management
- Three-tiered caching system
- OpenAPI 3.0 specification
- Cache headers and metadata