Skip to content

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.

  • Base URL: Your site’s API root (e.g., https://yoursite.com/api)
  • Content Type: application/json
  • Authentication: API keys (Pro edition) or session-based

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 keys provide secure, token-based authentication ideal for headless CMS implementations, mobile apps, and third-party integrations.

Using the X-API-Key header (recommended):

Terminal window
curl -H "X-API-Key: tcms_1234567890abcdef1234567890abcdef" \
-H "Content-Type: application/json" \
https://yoursite.com/api/collections/blog

Using the Authorization: Bearer header:

Terminal window
curl -H "Authorization: Bearer tcms_1234567890abcdef1234567890abcdef" \
-H "Content-Type: application/json" \
https://yoursite.com/api/collections/blog

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 UtilitiesAPI 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.

For admin panel and same-origin requests using cookies:

// Include CSRF token for session-based requests
fetch('/api/collections/blog', {
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content,
'Content-Type': 'application/json'
}
});

The CSRF token is required on state-changing requests (POST, PUT, PATCH, DELETE) that authenticate via the session cookie — send it as the X-CSRF-Token header or a csrf_token body field, or the request is rejected with 403. GET requests, API-keyed requests, and OAuth Bearer requests never need it. Scripted callers should use an API key instead of a session cookie.

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

API keys vs. OAuth Bearer tokens — different trust models. Both authenticate via the Authorization: Bearer header (or X-API-Key for API keys), but they resolve authority differently. An API key bypasses access-group checks entirely — it’s a trust-model exception, same reach as its configured scope regardless of who created it. An OAuth Bearer token is checked against the approving user’s access groups on every request, the same group permissions that gate that user in the admin dashboard — a cms:write-scoped token can only write to the collections its approving user’s groups actually allow, not every collection the scope’s REST paths cover. See OAuth Server → Scopes for the full rule.

GET /api/collections

Response:

{
"data": [
{
"name": "blog",
"title": "Blog Posts",
"count": 25,
"schema": "/schemas/blog"
},
{
"name": "products",
"title": "Products",
"count": 150,
"schema": "/schemas/products"
}
]
}
GET /api/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: asc or desc (default: desc)
  • filter - Filter by field values
  • search - Full-text search

Examples:

Terminal window
# Get all blog posts
curl https://yoursite.com/api/collections/blog
# Get published posts only
curl "https://yoursite.com/api/collections/blog?filter[status]=published"
# Get latest 10 posts
curl "https://yoursite.com/api/collections/blog?limit=10&sort=date&order=desc"
# Search posts
curl "https://yoursite.com/api/collections/blog?search=tutorial"
# Pagination
curl "https://yoursite.com/api/collections/blog?limit=20&offset=40"

Response:

{
"data": [
{
"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 /api/collections/{collection}/{id}

Example:

Terminal window
curl https://yoursite.com/api/collections/blog/my-first-post

Response:

{
"data": {
"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"
}
}
POST /api/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):

{
"data": {
"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"
}
}
PUT /api/collections/{collection}/{id}

Request Body:

{
"title": "Updated Blog Post Title",
"status": "published"
}

Response (200 OK):

{
"data": {
"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"
}
}
PATCH /api/collections/{collection}/{id}

Updates only specified fields:

{
"status": "published"
}
DELETE /api/collections/{collection}/{id}

Response (204 No Content)

POST /api/collections/{collection}/{id}/clone

Creates 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"
}
HEAD /api/collections/{collection}

Returns 200 OK if the collection exists, 404 Not Found if it does not. No response body.

HEAD /api/collections/{collection}/{id}

Returns 200 OK if the object exists, 404 Not Found if it does not. No response body.

GET /api/collections/{collection}/schema

Returns the schema definition for a specific collection.

Query Parameters:

  • raw - Set to true to 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
}
}
}

The query endpoint provides paginated access to collection objects with filtering, sorting, and multiple output formats.

GET /api/collections/{collection}/query

Query Parameters:

  • limit - Items per page
  • offset - Pagination offset
  • sort - Property name to sort by
  • search - Full-text search term
  • include - Comma-separated property names to include in results
  • exclude - Comma-separated property names to exclude from results
  • format - Output format: json (default), csv, html

Example:

Terminal window
# Paginated query with sorting
curl "https://yoursite.com/api/collections/blog/query?limit=10&offset=0&sort=date"
# Search with field filtering
curl "https://yoursite.com/api/collections/blog/query?search=tutorial&include=title,date,status"
# Export as CSV
curl "https://yoursite.com/api/collections/blog/query?format=csv&include=title,date"

Response (JSON, default):

{
"data": [
{
"id": "my-post",
"title": "My Post",
"date": "2024-01-15"
}
],
"meta": {
"pagination": {
"total": 25,
"count": 10,
"per_page": 10,
"current_page": 1,
"total_pages": 3
}
}
}
GET /api/collections/{collection}/index

Returns the full collection index. Supports filtering and sorting.

Query Parameters:

  • include - Comma-separated property names to include
  • exclude - Comma-separated property names to exclude
  • sort - Property name to sort by
PUT /api/collections/{collection}/index

Rebuilds the collection index from scratch. No request body required. Returns the rebuilt index.

Update, patch, or delete individual properties on an object without sending the entire object.

PUT /api/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 /api/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 /api/collections/{collection}/{id}/{property}

Removes the property from the object entirely.

Response (200 OK): The updated object with the property removed.

POST /api/collections/{collection}/{id}/{property}/increment
POST /api/collections/{collection}/{id}/{property}/increment/{amount}

Increments a numeric property value. Defaults to incrementing by 1.

Examples:

Terminal window
# Increment view count by 1
curl -X POST https://yoursite.com/api/collections/blog/my-post/views/increment
# Increment by 5
curl -X POST https://yoursite.com/api/collections/blog/my-post/views/increment/5

Response (200 OK): The updated property value.

Response (400 Bad Request): If the property is not numeric.

POST /api/collections/{collection}/{id}/{property}/decrement
POST /api/collections/{collection}/{id}/{property}/decrement/{amount}

Decrements a numeric property value. Defaults to decrementing by 1.

Update metadata associated with individual files or items within a property (e.g., alt text on an image, captions on gallery items).

PUT /api/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 /api/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.

CRUD operations for items within a deck (repeatable sections) property. Deck is a Pro edition feature.

POST /api/collections/{collection}/{id}/{property}/deck

Request 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 /api/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.

PUT /api/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 /api/collections/{collection}/{id}/{property}/deck/{itemId}

Response (200 OK): The updated parent object with the deck item removed.

Export collection data as CSV or JSON with field selection.

GET /api/report/collections/{collection}/fields

Returns the list of fields available for export.

Query Parameters:

  • format - json or html (default)

Response (JSON):

[
{
"name": "title",
"label": "Title",
"type": "string"
},
{
"name": "status",
"label": "Status",
"type": "string"
}
]
GET /api/report/collections/{collection}/csv

Query Parameters:

  • fields (required) - Comma-separated field names to export. Use dot notation for deck fields: name,items.value,items.status
  • include - Filter objects by property values
  • exclude - Exclude objects by property values

Example:

Terminal window
curl "https://yoursite.com/api/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.

GET /api/report/collections/{collection}/json

Query Parameters:

  • fields (required) - Comma-separated field names to export. Supports dot notation for deck fields.
  • include - Filter objects by property values
  • exclude - Exclude objects by property values

Example:

Terminal window
curl "https://yoursite.com/api/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"]
}
GET /api/schemas

Response:

{
"schemas": [
{
"name": "blog",
"title": "Blog Posts",
"description": "Blog post collection",
"url": "/schemas/blog"
}
]
}
GET /api/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"
}
}
}

Save, move, delete, and manage files attached to a property on an object. These are the endpoints the admin UI uses behind every image, file, gallery, and depot field — and the ones to call from your own integrations.

Files are stored under tcms-data/{collection}/{id}/{property}/[{path}/]{filename} and served back over the public imageworks / download / stream endpoints documented in File Downloads & Streaming.

Upload a file to an image, file, gallery, or depot property. The same endpoint handles three input modes:

  • Direct uploadmultipart/form-data with the file under the property name (or, for nested fields, under the child field name — see the nested example below).
  • Chunked upload — Dropzone-compatible chunking via dzchunkindex / dztotalchunkcount (or chunkindex / totalchunkcount) in the form body. Intermediate chunks return { "status": "chunk received" }; the final chunk assembles and saves.
  • URL upload — Instead of a file, POST a JSON body with the property name set to a URL. The server downloads the URL (subject to maxDownloadSize in tcms.php) and saves the result as if it had been uploaded directly.
POST /api/collections/{collection}/{id}/{property}
POST /api/collections/{collection}/{id}/{property}/{path}

Behavior:

  • HEIC images are auto-converted to JPEG before saving.
  • The response is the updated object (same shape as GET /api/collections/{collection}/{id}) so clients see the new file in context.
  • Nested-path uploads target card children, deck-item children, or depot subfolders. The form-field name in the request body must match the last segment of {path} (e.g., uploading to …/mycard/image puts the file under the form key image, not mycard).

Direct upload example:

Terminal window
curl -X POST https://yoursite.com/api/collections/blog/my-post/cover \
-H "Authorization: Bearer tcms_1234567890abcdef1234567890abcdef" \
-F "cover=@./hero.jpg"

URL upload example:

Terminal window
curl -X POST https://yoursite.com/api/collections/blog/my-post/cover \
-H "Authorization: Bearer tcms_..." \
-H "Content-Type: application/json" \
-d '{"cover": "https://example.com/external-hero.jpg"}'

Nested upload example (image inside a card field):

Terminal window
curl -X POST https://yoursite.com/api/collections/blog/my-post/hero-card/image \
-H "Authorization: Bearer tcms_..." \
-F "image=@./photo.jpg"

Response (full object with updated file metadata):

{
"data": {
"id": "my-post",
"cover": {
"name": "hero.jpg",
"size": 245760,
"alt": "",
"focalpoint": { "x": 50, "y": 50 }
}
},
"meta": { ... }
}

Common errors:

{ "error": "No file found in request for property: cover" }
{ "error": "File exceeds upload_max_filesize in php.ini" }

Remove a single file from a gallery or depot, or delete the file held by an image/file field.

DELETE /api/collections/{collection}/{id}/{property}/{path}

{path} is the file’s name relative to the property — photo.jpg for a top-level file, 2026/photo.jpg for a depot subfolder, or the child field key for a nested property delete (e.g., mycard/image).

Example:

Terminal window
curl -X DELETE https://yoursite.com/api/collections/blog/my-post/gallery/old-photo.jpg \
-H "Authorization: Bearer tcms_..."

Response is the updated object after the delete.

Move a file from one depot subfolder to another. Used by the depot field’s drag-and-drop UI; the request body’s destination is the new subfolder path.

PUT /api/collections/{collection}/{id}/{property}/{name}/move

Query parameters:

  • path — Current subfolder containing the file. Omit if the file is at the root of the property.

Request body:

  • destination — Target subfolder path.

Example:

Terminal window
curl -X PUT "https://yoursite.com/api/collections/blog/my-post/files/report.pdf/move?path=2025" \
-H "Authorization: Bearer tcms_..." \
-H "Content-Type: application/json" \
-d '{"destination": "2026/january"}'

Response:

{ "moved": true }

Create a subfolder inside a depot property.

POST /api/collections/{collection}/{id}/{property}/folder

Request body:

  • path — Folder path to create. Forward slashes create nested folders in one call.
Terminal window
curl -X POST https://yoursite.com/api/collections/blog/my-post/files/folder \
-H "Authorization: Bearer tcms_..." \
-H "Content-Type: application/json" \
-d '{"path": "2026/q1"}'

Response is the updated object showing the new folder structure.

PUT /api/collections/{collection}/{id}/{property}/folder/rename

Query parameters:

  • path — Current folder path to rename.

Request body:

  • name — New folder name (just the last segment, not a full path).
Terminal window
curl -X PUT "https://yoursite.com/api/collections/blog/my-post/files/folder/rename?path=2025/q4" \
-H "Authorization: Bearer tcms_..." \
-H "Content-Type: application/json" \
-d '{"name": "archived-q4"}'

Force-regenerate every cached image derivative (ImageWorks output) for a property. Useful after changing image presets or watermark settings.

DELETE /api/collections/{collection}/{id}/{property}/cache

Response:

{ "deleted": 12 }

deleted is the number of cached files that were removed.

Clear the cache for a single gallery item, or the cache that lives under a card / deck child property.

DELETE /api/collections/{collection}/{id}/{property}/{path}/cache

The action picks the right cache to clear based on filesystem state: a gallery file name clears just that file’s image derivatives; a child-property key clears the nested property’s cache directory.

The styled-text (Tiptap) editor uses a separate, internal /api/upload/* namespace for inline image and file embeds inserted from the rich-text toolbar:

POST /api/upload/{collection}/{id}/{property}[/{path}]
GET /api/upload/{collection}/{id}/{property}
GET /api/upload/{collection}/{id}/{property}/{path}
DELETE /api/upload/{collection}/{id}/{property}/{path}

These behave similarly to the property-file endpoints above but return a single link URL (image/audio/video/file get routed to imageworks / stream / download respectively) rather than the full object. They’re public-facing in the sense that the URLs need to remain stable for content embedded in saved HTML, but they’re not the recommended integration surface — prefer the /api/collections/{collection}/{id}/{property} endpoints above for any first-party file upload work.

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 ID
  • property - Property name containing the file

Query Parameters:

  • pwd - Encrypted password for protected files

Examples:

Terminal window
# Basic file download
curl -O https://yoursite.com/download/files/manual/file
# Download with custom collection/property
curl -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 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 name
  • id - Object ID
  • property - Depot property name
  • filename - Specific file to download

Query Parameters:

  • path - Subfolder path within depot
  • pwd - Encrypted password for protected files

Examples:

Terminal window
# Download specific depot file
curl -O https://yoursite.com/download/depot/assets/files/document.pdf
# Download from subfolder
curl -O "https://yoursite.com/download/depot/assets/files/image.jpg?path=photos/vacation"
# Password-protected depot file
curl -O "https://yoursite.com/download/depot/private/files/secret.zip?pwd=ENCRYPTED_PASSWORD"

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 name
  • id - Object ID
  • property - 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: bytes
  • Content-Range - For partial content responses (206)
  • Content-Length - File or range size

Examples:

Terminal window
# Stream video file
curl https://yoursite.com/stream/videos/movie/video
# Range request for video seeking
curl -H "Range: bytes=0-1023" https://yoursite.com/stream/videos/movie/video
# Password-protected streaming
curl "https://yoursite.com/stream/private/secret/video?pwd=ENCRYPTED_PASSWORD"

Stream a specific file from a depot property.

GET /stream/{collection}/{id}/{property}/{filename}

Path Parameters:

  • collection - Collection name
  • id - Object ID
  • property - Depot property name
  • filename - Specific file to stream

Query Parameters:

  • path - Subfolder path within depot
  • pwd - Encrypted password for protected files

Examples:

Terminal window
# Stream depot video
curl https://yoursite.com/stream/media/playlist/videos/movie.mp4
# Stream with subfolder path
curl "https://yoursite.com/stream/media/playlist/videos/song.mp3?path=albums/rock"

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>
FeatureDownloadStream
Content-Dispositionattachmentinline
Browser BehaviorForces download dialogPlays/displays in browser
Range RequestsNoYes (HTTP 206)
Video/Audio SupportBasicFull seeking/scrubbing
Safari CompatibilityStandardEnhanced for media
Use CasesDocuments, archivesVideo, audio, PDFs

Both download and stream endpoints support password protection:

  1. Frontend: Use Twig functions that auto-encrypt passwords
  2. API: Passwords must be encrypted using the Cipher class
  3. 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}) }}
GET /imageworks/{collection}/{id}/{property}.{format}

Parameters:

  • w - Width in pixels
  • h - Height in pixels
  • fit - Resize mode: crop, contain, cover, fill, inside, outside
  • format - Output format: jpg, png, webp, avif
  • quality - 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:

Terminal window
# Resize to 800x600
curl "https://yoursite.com/imageworks/gallery/hero/image.jpg?w=800&h=600"
# Crop to square thumbnail
curl "https://yoursite.com/imageworks/products/laptop/image.jpg?w=300&h=300&fit=crop"
# Convert to WebP with quality
curl "https://yoursite.com/imageworks/blog/featured/image.webp?quality=80"
# Apply filters
curl "https://yoursite.com/imageworks/portfolio/photo/image.jpg?grayscale=true&contrast=20"
# Responsive image with blur
curl "https://yoursite.com/imageworks/hero/banner/image.jpg?w=1200&blur=5"
GET /imageworks/{collection}/{id}/{property}/{name}.{format}

Fetch a specific image from a gallery property.

GET /imageworks/{collection}/{id}/{property}/{action}

Actions:

  • first - Get the first image
  • last - Get the last image
  • random - Get a random image
  • featured - Get the featured image
{
"error": {
"code": 400,
"message": "Validation failed",
"details": {
"title": ["Title is required"],
"email": ["Invalid email format"]
}
}
}
  • 200 OK - Successful request
  • 201 Created - Resource created successfully
  • 204 No Content - Successful request with no response body
  • 400 Bad Request - Invalid request data
  • 401 Unauthorized - Authentication required
  • 403 Forbidden - Insufficient permissions
  • 404 Not Found - Resource not found
  • 422 Unprocessable Entity - Validation errors
  • 429 Too Many Requests - Rate limit exceeded
  • 500 Internal Server Error - Server error

Headers:

  • X-RateLimit-Limit - Request limit per window
  • X-RateLimit-Remaining - Remaining requests
  • X-RateLimit-Reset - Time when limit resets

Example Response Headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1705751400

For endpoints that return multiple items:

Headers:

  • X-Total-Count - Total number of items
  • Link - Pagination links (next, prev, first, last)

Example:

X-Total-Count: 150
Link: </collections/blog?offset=20&limit=20>; rel="next",
</collections/blog?offset=0&limit=20>; rel="first",
</collections/blog?offset=140&limit=20>; rel="last"

The API supports Cross-Origin Resource Sharing (CORS) for browser-based requests:

// Example browser request
fetch('https://yoursite.com/api/collections/blog', {
method: 'GET',
headers: {
'X-API-Key': 'tcms_your_api_key_here',
'Content-Type': 'application/json'
}
});