REST API Documentation
Total CMS provides a RESTful API for accessing content and schemas. This documentation covers all available endpoints with examples and response formats.
API Overview
Section titled “API Overview”- Base URL: Your site’s root URL (e.g.,
https://yoursite.com) - Content Type:
application/json - Authentication: API keys (Pro edition) or session-based
Authentication
Section titled “Authentication”Total CMS supports two authentication methods for API access: API Keys (recommended for external applications, Pro edition required) and Session Authentication (for same-origin admin panel requests).
📖 For comprehensive API key documentation, see API Keys Guide
API Key Authentication (Pro Edition)
Section titled “API Key Authentication (Pro Edition)”API keys provide secure, token-based authentication ideal for headless CMS implementations, mobile apps, and third-party integrations.
Using the X-API-Key header (recommended):
curl -H "X-API-Key: tcms_1234567890abcdef1234567890abcdef" \ -H "Content-Type: application/json" \ https://yoursite.com/collections/blogUsing query parameter:
curl "https://yoursite.com/collections/blog?api_key=tcms_1234567890abcdef1234567890abcdef"Key Features:
- Scope-based permissions - Control HTTP methods (GET, POST, PUT, DELETE, PATCH)
- Path restrictions - Limit access to specific collections or endpoints
- Usage tracking - Monitor last used timestamps
- Easy revocation - Delete keys to immediately revoke access
Creating API Keys:
Navigate to Utilities → API Keys in the admin interface, or visit /admin/utils/api-keys.
For detailed information on scopes, permissions, and best practices, see the API Keys documentation.
Session Authentication
Section titled “Session Authentication”For admin panel and same-origin requests using cookies:
// Include CSRF token for session-based requestsfetch('/collections/blog', { headers: { 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content, 'Content-Type': 'application/json' }});When to use session authentication:
- Admin panel JavaScript
- Same-origin web applications
- Browser-based tools running on the same domain
When to use API keys:
- Mobile applications
- Third-party integrations
- Headless CMS implementations
- Automated scripts and workflows
Collections API
Section titled “Collections API”Get All Collections
Section titled “Get All Collections”GET /collectionsResponse:
{ "collections": [ { "name": "blog", "title": "Blog Posts", "count": 25, "schema": "/schemas/blog" }, { "name": "products", "title": "Products", "count": 150, "schema": "/schemas/products" } ]}Get Collection Objects
Section titled “Get Collection Objects”GET /collections/{collection}Query Parameters:
limit- Number of results (default: 50, max: 100)offset- Starting position (default: 0)sort- Sort field (default: created)order- Sort direction:ascordesc(default: desc)filter- Filter by field valuessearch- Full-text search
Examples:
# Get all blog postscurl https://yoursite.com/collections/blog
# Get published posts onlycurl "https://yoursite.com/collections/blog?filter[status]=published"
# Get latest 10 postscurl "https://yoursite.com/collections/blog?limit=10&sort=date&order=desc"
# Search postscurl "https://yoursite.com/collections/blog?search=tutorial"
# Paginationcurl "https://yoursite.com/collections/blog?limit=20&offset=40"Response:
{ "collection": "blog", "total": 25, "count": 10, "limit": 10, "offset": 0, "objects": [ { "id": "my-first-post", "title": "My First Post", "content": "Post content here...", "author": "john-doe", "status": "published", "date": "2024-01-15", "created": "2024-01-15T09:00:00Z", "modified": "2024-01-16T10:30:00Z" } ]}Get Single Object
Section titled “Get Single Object”GET /collections/{collection}/{id}Example:
curl https://yoursite.com/collections/blog/my-first-postResponse:
{ "id": "my-first-post", "title": "My First Post", "content": "Post content here...", "author": "john-doe", "status": "published", "date": "2024-01-15", "tags": ["tutorial", "beginner"], "image": { "url": "/media/images/post-image.jpg", "alt": "Post featured image", "width": 1200, "height": 630 }, "created": "2024-01-15T09:00:00Z", "modified": "2024-01-16T10:30:00Z"}Create Object
Section titled “Create Object”POST /collections/{collection}Request Body:
{ "title": "New Blog Post", "content": "This is the content of my new blog post.", "author": "jane-doe", "status": "draft", "tags": ["announcement", "news"]}Response (201 Created):
{ "id": "new-blog-post", "title": "New Blog Post", "content": "This is the content of my new blog post.", "author": "jane-doe", "status": "draft", "tags": ["announcement", "news"], "created": "2024-01-20T14:30:00Z", "modified": "2024-01-20T14:30:00Z"}Update Object
Section titled “Update Object”PUT /collections/{collection}/{id}Request Body:
{ "title": "Updated Blog Post Title", "status": "published"}Response (200 OK):
{ "id": "new-blog-post", "title": "Updated Blog Post Title", "content": "This is the content of my new blog post.", "author": "jane-doe", "status": "published", "tags": ["announcement", "news"], "created": "2024-01-20T14:30:00Z", "modified": "2024-01-20T15:45:00Z"}Partial Update
Section titled “Partial Update”PATCH /collections/{collection}/{id}Updates only specified fields:
{ "status": "published"}Delete Object
Section titled “Delete Object”DELETE /collections/{collection}/{id}Response (204 No Content)
Clone Object
Section titled “Clone Object”POST /collections/{collection}/{id}/cloneCreates a copy of an existing object. Optionally clone to a different ID or collection.
Request Body (optional):
{ "id": "new-object-id", "collection": "other-collection"}If no body is provided, the clone uses the same ID and collection as the source.
Response (200 OK):
{ "id": "new-object-id", "title": "My First Post", "content": "Post content here...", "created": "2024-01-20T14:30:00Z", "modified": "2024-01-20T14:30:00Z"}Check Collection Exists
Section titled “Check Collection Exists”HEAD /collections/{collection}Returns 200 OK if the collection exists, 404 Not Found if it does not. No response body.
Check Object Exists
Section titled “Check Object Exists”HEAD /collections/{collection}/{id}Returns 200 OK if the object exists, 404 Not Found if it does not. No response body.
Get Collection Schema
Section titled “Get Collection Schema”GET /collections/{collection}/schemaReturns the schema definition for a specific collection.
Query Parameters:
raw- Set totrueto return the raw (non-flattened) schema. Default returns the flattened schema.
Response (200 OK):
{ "name": "blog", "title": "Blog Posts", "properties": { "title": { "type": "string", "required": true, "maxLength": 255 } }}Query API
Section titled “Query API”The query endpoint provides paginated access to collection objects with filtering, sorting, and multiple output formats.
Query Objects
Section titled “Query Objects”GET /collections/{collection}/queryQuery Parameters:
limit- Items per pageoffset- Pagination offsetsort- Property name to sort bysearch- Full-text search terminclude- Comma-separated property names to include in resultsexclude- Comma-separated property names to exclude from resultsformat- Output format:json(default),csv,html
Example:
# Paginated query with sortingcurl "https://yoursite.com/collections/blog/query?limit=10&offset=0&sort=date"
# Search with field filteringcurl "https://yoursite.com/collections/blog/query?search=tutorial&include=title,date,status"
# Export as CSVcurl "https://yoursite.com/collections/blog/query?format=csv&include=title,date"Response (JSON, default):
{ "data": [ { "id": "my-post", "title": "My Post", "date": "2024-01-15" } ], "pagination": { "total": 25, "count": 10, "per_page": 10, "current_page": 1, "total_pages": 3 }}Get Collection Index
Section titled “Get Collection Index”GET /collections/{collection}/indexReturns the full collection index. Supports filtering and sorting.
Query Parameters:
include- Comma-separated property names to includeexclude- Comma-separated property names to excludesort- Property name to sort by
Rebuild Collection Index
Section titled “Rebuild Collection Index”PUT /collections/{collection}/indexRebuilds the collection index from scratch. No request body required. Returns the rebuilt index.
Property Operations
Section titled “Property Operations”Update, patch, or delete individual properties on an object without sending the entire object.
Update Property
Section titled “Update Property”PUT /collections/{collection}/{id}/{property}Replaces the entire property value (PUT semantics).
Request Body: The new value for the property.
"New title value"Response (200 OK): The updated object.
Patch Property
Section titled “Patch Property”PATCH /collections/{collection}/{id}/{property}Merges into the existing property value (PATCH semantics). Useful for updating individual fields within a complex property.
Request Body: Fields to merge.
{ "alt": "Updated alt text"}Response (200 OK): The updated object.
Delete Property
Section titled “Delete Property”DELETE /collections/{collection}/{id}/{property}Removes the property from the object entirely.
Response (200 OK): The updated object with the property removed.
Increment Property
Section titled “Increment Property”POST /collections/{collection}/{id}/{property}/incrementPOST /collections/{collection}/{id}/{property}/increment/{amount}Increments a numeric property value. Defaults to incrementing by 1.
Examples:
# Increment view count by 1curl -X POST https://yoursite.com/collections/blog/my-post/views/increment
# Increment by 5curl -X POST https://yoursite.com/collections/blog/my-post/views/increment/5Response (200 OK): The updated property value.
Response (400 Bad Request): If the property is not numeric.
Decrement Property
Section titled “Decrement Property”POST /collections/{collection}/{id}/{property}/decrementPOST /collections/{collection}/{id}/{property}/decrement/{amount}Decrements a numeric property value. Defaults to decrementing by 1.
Property Metadata
Section titled “Property Metadata”Update metadata associated with individual files or items within a property (e.g., alt text on an image, captions on gallery items).
Update Property Metadata
Section titled “Update Property Metadata”PUT /collections/{collection}/{id}/{property}/{name}Replaces the metadata for a specific item within a property.
Query Parameters:
path- Subfolder path for depot files
Request Body: The metadata to set.
{ "alt": "Photo of sunset", "caption": "Taken in Hawaii"}Patch Property Metadata
Section titled “Patch Property Metadata”PATCH /collections/{collection}/{id}/{property}/{name}Merges into existing metadata for a specific item within a property.
Query Parameters:
path- Subfolder path for depot files
Request Body: Fields to merge into existing metadata.
Deck Items API
Section titled “Deck Items API”CRUD operations for items within a deck (repeatable sections) property. Deck is a Pro edition feature.
Create Deck Item
Section titled “Create Deck Item”POST /collections/{collection}/{id}/{property}/deckRequest Body:
{ "id": "item-1", "title": "Slide 1", "image": "/media/slide1.jpg", "caption": "Welcome slide"}The id field is optional if the schema supports auto-generation.
Response (201 Created): The updated parent object including the new deck item.
Get Deck Item
Section titled “Get Deck Item”GET /collections/{collection}/{id}/{property}/deck/{itemId}Response (200 OK):
{ "id": "item-1", "title": "Slide 1", "image": "/media/slide1.jpg", "caption": "Welcome slide"}Response (404 Not Found): If the deck item does not exist.
Update Deck Item
Section titled “Update Deck Item”PUT /collections/{collection}/{id}/{property}/deck/{itemId}Replaces the deck item entirely.
Request Body:
{ "title": "Updated Slide 1", "image": "/media/slide1-new.jpg", "caption": "Updated caption"}Response (200 OK): The updated parent object.
Delete Deck Item
Section titled “Delete Deck Item”DELETE /collections/{collection}/{id}/{property}/deck/{itemId}Response (200 OK): The updated parent object with the deck item removed.
Reports API
Section titled “Reports API”Export collection data as CSV or JSON with field selection.
Get Report Fields
Section titled “Get Report Fields”GET /report/collections/{collection}/fieldsReturns the list of fields available for export.
Query Parameters:
format-jsonorhtml(default)
Response (JSON):
[ { "name": "title", "label": "Title", "type": "string" }, { "name": "status", "label": "Status", "type": "string" }]Export CSV Report
Section titled “Export CSV Report”GET /report/collections/{collection}/csvQuery Parameters:
fields(required) - Comma-separated field names to export. Use dot notation for deck fields:name,items.value,items.statusinclude- Filter objects by property valuesexclude- Exclude objects by property values
Example:
curl "https://yoursite.com/report/collections/blog/csv?fields=title,date,status"Response: CSV file download with Content-Disposition: attachment.
Deck fields expand into multiple rows per object — one row per deck item, with scalar fields repeated on each row.
Export JSON Report
Section titled “Export JSON Report”GET /report/collections/{collection}/jsonQuery Parameters:
fields(required) - Comma-separated field names to export. Supports dot notation for deck fields.include- Filter objects by property valuesexclude- Exclude objects by property values
Example:
curl "https://yoursite.com/report/collections/blog/json?fields=title,date,status"Response: JSON file download. Deck fields remain nested (not expanded like CSV).
[ { "title": "My Post", "date": "2024-01-15", "status": "published" }]If any objects are skipped due to data errors, the response wraps the data:
{ "data": [...], "errors": ["object-id-1", "object-id-2"]}Schemas API
Section titled “Schemas API”Get All Schemas
Section titled “Get All Schemas”GET /schemasResponse:
{ "schemas": [ { "name": "blog", "title": "Blog Posts", "description": "Blog post collection", "url": "/schemas/blog" } ]}Get Schema Definition
Section titled “Get Schema Definition”GET /schemas/{collection}Response:
{ "name": "blog", "title": "Blog Posts", "description": "Blog post collection", "properties": { "title": { "type": "string", "required": true, "maxLength": 255 }, "content": { "type": "string", "format": "html" }, "author": { "type": "string", "reference": "users" }, "status": { "type": "string", "enum": ["draft", "published", "archived"], "default": "draft" }, "tags": { "type": "array", "items": { "type": "string" } }, "date": { "type": "string", "format": "date" } }}File Downloads & Streaming
Section titled “File Downloads & Streaming”Download File (Forces Download)
Section titled “Download File (Forces Download)”Download a file from a specific collection with Content-Disposition: attachment.
GET /download/{collection}/{id}/{property}POST /download/{collection}/{id}/{property}Path Parameters:
collection- Collection name (e.g., ‘files’, ‘documents’)id- Object IDproperty- Property name containing the file
Query Parameters:
pwd- Encrypted password for protected files
Examples:
# Basic file downloadcurl -O https://yoursite.com/download/files/manual/file
# Download with custom collection/propertycurl -O https://yoursite.com/download/documents/guide/pdf
# Password-protected file (password must be encrypted)curl -O "https://yoursite.com/download/private/secret/file?pwd=ENCRYPTED_PASSWORD"Download Depot File
Section titled “Download Depot File”Download a specific file from a depot (multi-file) property.
GET /download/{collection}/{id}/{property}/{filename}POST /download/{collection}/{id}/{property}/{filename}Path Parameters:
collection- Collection nameid- Object IDproperty- Depot property namefilename- Specific file to download
Query Parameters:
path- Subfolder path within depotpwd- Encrypted password for protected files
Examples:
# Download specific depot filecurl -O https://yoursite.com/download/depot/assets/files/document.pdf
# Download from subfoldercurl -O "https://yoursite.com/download/depot/assets/files/image.jpg?path=photos/vacation"
# Password-protected depot filecurl -O "https://yoursite.com/download/depot/private/files/secret.zip?pwd=ENCRYPTED_PASSWORD"Stream File (Plays in Browser)
Section titled “Stream File (Plays in Browser)”Stream a file with Content-Disposition: inline and HTTP range request support. Ideal for video/audio files.
GET /stream/{collection}/{id}/{property}Path Parameters:
collection- Collection nameid- Object IDproperty- Property name containing the file
Query Parameters:
pwd- Encrypted password for protected files
Headers:
Range- HTTP range request (e.g., “bytes=0-1023”)
Response Headers:
Accept-Ranges: bytesContent-Range- For partial content responses (206)Content-Length- File or range size
Examples:
# Stream video filecurl https://yoursite.com/stream/videos/movie/video
# Range request for video seekingcurl -H "Range: bytes=0-1023" https://yoursite.com/stream/videos/movie/video
# Password-protected streamingcurl "https://yoursite.com/stream/private/secret/video?pwd=ENCRYPTED_PASSWORD"Stream Depot File
Section titled “Stream Depot File”Stream a specific file from a depot property.
GET /stream/{collection}/{id}/{property}/{filename}Path Parameters:
collection- Collection nameid- Object IDproperty- Depot property namefilename- Specific file to stream
Query Parameters:
path- Subfolder path within depotpwd- Encrypted password for protected files
Examples:
# Stream depot videocurl https://yoursite.com/stream/media/playlist/videos/movie.mp4
# Stream with subfolder pathcurl "https://yoursite.com/stream/media/playlist/videos/song.mp3?path=albums/rock"HTML5 Media Integration
Section titled “HTML5 Media Integration”Video Streaming:
<video controls> <source src="/stream/videos/movie/video" type="video/mp4"></video>Audio Streaming:
<audio controls> <source src="/stream/audio/song/file" type="audio/mpeg"></audio>Download vs Stream Comparison
Section titled “Download vs Stream Comparison”| Feature | Download | Stream |
|---|---|---|
| Content-Disposition | attachment | inline |
| Browser Behavior | Forces download dialog | Plays/displays in browser |
| Range Requests | No | Yes (HTTP 206) |
| Video/Audio Support | Basic | Full seeking/scrubbing |
| Safari Compatibility | Standard | Enhanced for media |
| Use Cases | Documents, archives | Video, audio, PDFs |
Password Protection
Section titled “Password Protection”Both download and stream endpoints support password protection:
- Frontend: Use Twig functions that auto-encrypt passwords
- API: Passwords must be encrypted using the Cipher class
- URLs: Encrypted passwords are URL-encoded in query parameters
Twig Examples:
{# Auto-encrypts plain password #}{{ cms.download('id', {pwd: 'plaintext'}) }}{{ cms.stream('id', {pwd: 'plaintext'}) }}
{# Already encrypted passwords work too #}{{ cms.download('id', {pwd: encrypted_pwd}) }}Image Processing (ImageWorks)
Section titled “Image Processing (ImageWorks)”Basic Image Manipulation
Section titled “Basic Image Manipulation”GET /imageworks/{collection}/{id}/{property}.{format}Parameters:
w- Width in pixelsh- Height in pixelsfit- Resize mode:crop,contain,cover,fill,inside,outsideformat- Output format:jpg,png,webp,avifquality- JPEG quality (1-100)blur- Blur amount (1-100)brightness- Brightness (-100 to 100)contrast- Contrast (-100 to 100)gamma- Gamma correction (0.1 to 3.0)sharpen- Sharpen amount (1-100)grayscale- Convert to grayscale (true/false)sepia- Apply sepia effect (true/false)
Examples:
# Resize to 800x600curl "https://yoursite.com/imageworks/gallery/hero/image.jpg?w=800&h=600"
# Crop to square thumbnailcurl "https://yoursite.com/imageworks/products/laptop/image.jpg?w=300&h=300&fit=crop"
# Convert to WebP with qualitycurl "https://yoursite.com/imageworks/blog/featured/image.webp?quality=80"
# Apply filterscurl "https://yoursite.com/imageworks/portfolio/photo/image.jpg?grayscale=true&contrast=20"
# Responsive image with blurcurl "https://yoursite.com/imageworks/hero/banner/image.jpg?w=1200&blur=5"Gallery Images
Section titled “Gallery Images”GET /imageworks/{collection}/{id}/{property}/{name}.{format}Fetch a specific image from a gallery property.
Dynamic Gallery Images
Section titled “Dynamic Gallery Images”GET /imageworks/{collection}/{id}/{property}/{action}Actions:
first- Get the first imagelast- Get the last imagerandom- Get a random imagefeatured- Get the featured image
Error Handling
Section titled “Error Handling”Error Response Format
Section titled “Error Response Format”{ "error": { "code": 400, "message": "Validation failed", "details": { "title": ["Title is required"], "email": ["Invalid email format"] } }}HTTP Status Codes
Section titled “HTTP Status Codes”200 OK- Successful request201 Created- Resource created successfully204 No Content- Successful request with no response body400 Bad Request- Invalid request data401 Unauthorized- Authentication required403 Forbidden- Insufficient permissions404 Not Found- Resource not found422 Unprocessable Entity- Validation errors429 Too Many Requests- Rate limit exceeded500 Internal Server Error- Server error
Rate Limiting
Section titled “Rate Limiting”Headers:
X-RateLimit-Limit- Request limit per windowX-RateLimit-Remaining- Remaining requestsX-RateLimit-Reset- Time when limit resets
Example Response Headers:
X-RateLimit-Limit: 60X-RateLimit-Remaining: 45X-RateLimit-Reset: 1705751400Pagination
Section titled “Pagination”For endpoints that return multiple items:
Headers:
X-Total-Count- Total number of itemsLink- Pagination links (next, prev, first, last)
Example:
X-Total-Count: 150Link: </collections/blog?offset=20&limit=20>; rel="next", </collections/blog?offset=0&limit=20>; rel="first", </collections/blog?offset=140&limit=20>; rel="last"CORS Support
Section titled “CORS Support”The API supports Cross-Origin Resource Sharing (CORS) for browser-based requests:
// Example browser requestfetch('https://yoursite.com/collections/blog', { method: 'GET', headers: { 'X-API-Key': 'tcms_your_api_key_here', 'Content-Type': 'application/json' }});