Custom Collections API
Custom Collections are the most flexible content type in Adapto CMS. They allow you to define your own data structures with custom field schemas — team members, products, testimonials, FAQs, documentation pages, or any structured content your application needs.
Endpoints Overview
| Method | Endpoint | Description |
|---|---|---|
GET | /public/custom-collections | List collections (paginated) |
GET | /public/custom-collections/{collection_id} | Get collection by ID |
GET | /public/custom-collections/by-slug/{slug} | Get collection by slug |
GET | /public/custom-collections/{collection_id}/items | List collection items (paginated) |
GET | /public/custom-collections/{collection_id}/items/preview | List item previews (lightweight) |
GET | /public/custom-collections/{collection_id}/items/{item_id} | Get item by ID |
GET | /public/custom-collections/{collection_id}/items/by-slug/{slug} | Get item by slug |
Collection Data Model
A Custom Collection defines a schema (via fields) and contains items that conform to that schema. Understanding this structure is key to working with collections effectively.
Collection Object
When you fetch a collection, you get back its schema definition including the fields array that describes what data each item can hold:
{
"id": "coll-001",
"name": "Team Members",
"slug": "team-members",
"description": "Our team directory",
"language": "en",
"fields": [
{
"name": "role",
"label": "Role",
"type": "text",
"required": true,
"multiple": false,
"description": "Job title or position",
"default_value": null,
"related_collection": null,
"options": null,
"validation": null
},
{
"name": "department",
"label": "Department",
"type": "select",
"required": true,
"multiple": false,
"options": [
{"value": "engineering", "label": "Engineering"},
{"value": "design", "label": "Design"},
{"value": "marketing", "label": "Marketing"}
]
},
{
"name": "bio",
"label": "Biography",
"type": "rich_text",
"required": false,
"multiple": false
}
],
"status": "published",
"created_at": "2026-01-08T10:00:00",
"updated_at": "2026-01-08T10:00:00"
}
Collection Item Object
Each item's data field is a flat object where keys match the field name values from the collection schema, and values conform to the field's type:
{
"id": "item-001",
"collection_id": "coll-001",
"title": "Jane Doe",
"slug": "jane-doe",
"data": {
"role": "Lead Engineer",
"department": "engineering",
"bio": "<p>Jane has 10 years of experience...</p>"
},
"language": "en",
"status": "published",
"created_at": "2026-01-09T10:00:00",
"updated_at": "2026-01-09T10:00:00",
"published_at": "2026-01-09T12:00:00",
"media_objects_placements": [
{
"placement_key": "avatar",
"media_object": {
"id": "mo-001",
"title": "Jane headshot",
"description": null,
"file_id": "file-001",
"url": "https://media.adaptocms.com/tenants/.../jane.jpg",
"type": "image",
"created_at": "2026-01-09T10:00:00",
"updated_at": "2026-01-09T10:00:00"
},
"caption": "Jane Doe, Lead Engineer",
"alt_text": "Photo of Jane Doe",
"meta_data": null
}
],
"translation_of_id": null,
"meta_data": null,
"file_urls": {"file-001": "https://media.adaptocms.com/tenants/.../jane.jpg"}
}
Field Types Reference
Every collection defines its schema through an array of fields. Each field has a type that determines what values can be stored in the item's data object. Here are all 15 available field types with the data values they produce:
| Type | Data Value | Description | Example Value |
|---|---|---|---|
text | string | Single-line text input | "Lead Engineer" |
textarea | string | Multi-line text input | "A longer description\nwith line breaks" |
rich_text | string (HTML) | Rich text content with formatting | "<p>Bold <strong>text</strong></p>" |
number | number | Integer or decimal value | 42 or 99.99 |
date | string (ISO 8601) | Single date value | "2026-03-15" |
date_range | object | Start and end date pair | {"start": "2026-03-01", "end": "2026-03-31"} |
boolean | boolean | True/false toggle | true |
select | string | Single value from predefined options | "engineering" |
multi_select | string[] | Multiple values from predefined options | ["frontend", "backend"] |
reference | string (UUID) | ID pointing to an item in another collection | "a1b2c3d4-..." |
image | string (UUID) | Media object ID for an image file | "mo-img-001" |
file | string (UUID) | Media object ID for any file type | "mo-file-001" |
url | string | URL value | "https://example.com" |
email | string | Email address | "jane@example.com" |
color | string | Color value (typically hex) | "#0369a1" |
Field Definition Properties
Each field in the collection's fields array has these properties:
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Field identifier — used as the key in item data |
label | string | Yes | Human-readable display label |
type | string | Yes | One of the 15 field types above |
required | boolean | No | Whether the field must be populated (default: false) |
multiple | boolean | No | Whether the field holds a list of values (default: false) |
description | string | null | No | Help text shown in the CMS UI |
default_value | any | null | No | Default value when creating items |
related_collection | string | null | No | Required for reference type: the target collection UUID |
options | object[] | null | No | Required for select/multi_select: array of {"value": "...", "label": "..."} |
validation | object | null | No | Additional validation rules |
Multiple Values
When multiple is true, the data value becomes an array of the field's base type. For example, a text field with multiple: true stores ["value1", "value2"] instead of "value1".
Not allowed on: boolean, multi_select, rich_text, date_range (these types already handle multiplicity internally or don't support it).
Select/Multi-Select Options
Fields of type select or multi_select require an options array. Each option is an object with value (stored in data) and label (displayed in UI):
{
"name": "priority",
"label": "Priority",
"type": "select",
"options": [
{"value": "low", "label": "Low"},
{"value": "medium", "label": "Medium"},
{"value": "high", "label": "High"},
{"value": "critical", "label": "Critical"}
]
}
// Item data stores the value:
{"priority": "high"}
Reference Fields
Fields of type reference store the UUID of an item in another collection. The related_collection property specifies which collection is the target:
{
"name": "author",
"label": "Author",
"type": "reference",
"related_collection": "coll-team-members-uuid"
}
// Item data stores the referenced item's ID:
{"author": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}
To resolve the reference, make a separate API call to fetch the referenced item by its ID.
Media Objects & Placements
Every collection item can have media attached via media_objects_placements — an array of placement objects that associate media (images, videos, documents) with named slots on the item.
MediaObjectPlacement
| Field | Type | Description |
|---|---|---|
placement_key | string | Named slot identifier (e.g., "hero", "thumbnail", "gallery_1") |
media_object | MediaObject | The media object details (see below) |
caption | string | null | Caption text for the media |
alt_text | string | null | Alt text for accessibility (images) |
meta_data | string | null | Free-form metadata (JSON string) |
MediaObject
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (UUID) |
title | string | null | Media title |
description | string | null | Media description |
file_id | string | ID of the underlying file in storage |
url | string | Direct CDN URL to the file |
type | string | Media type (see MediaType values below) |
created_at | string | null | Creation timestamp |
updated_at | string | null | Last update timestamp |
MediaType Values
| Value | Description |
|---|---|
image | Image files (JPEG, PNG, GIF, WebP, AVIF, SVG, HEIC) |
video | Video files (MP4, WebM, MOV, etc.) |
audio | Audio files (MP3, WAV, AAC, FLAC, etc.) |
document | Documents (PDF, DOC, DOCX, CSV, XLS, XLSX) |
youtube | YouTube video embed |
vimeo | Vimeo video embed |
tiktok | TikTok video embed |
instagram_reel | Instagram Reel embed |
instagram_post | Instagram Post embed |
other | Any other file type |
The file_urls Convenience Field
Each item response also includes a file_urls object — a flat map of file_id → CDN URL for every file referenced by the item's media placements. This saves you from having to extract URLs from nested placement objects:
{
"file_urls": {
"file-001": "https://media.adaptocms.com/tenants/.../photo.jpg",
"file-002": "https://media.adaptocms.com/tenants/.../document.pdf"
}
}
Working with Media Placements
// JavaScript: Extract the hero image from placements
const item = await fetch(`${API}/public/custom-collections/${collId}/items/${itemId}`, {
headers: { 'x-api-key': API_KEY }
}).then(r => r.json());
// Find a specific placement by key
const heroPlacement = item.media_objects_placements
.find(p => p.placement_key === 'hero');
if (heroPlacement) {
const imageUrl = heroPlacement.media_object.url;
const altText = heroPlacement.alt_text || heroPlacement.media_object.title;
// Use in your template
}
// Or use file_urls for a quick lookup by file_id
const thumbnailUrl = item.file_urls['file-123'];
List Collections
GET /public/custom-collections
Returns a paginated list of custom collections defined in your tenant.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Filter by status |
language | string | No | Filter by language code |
field | string | No | Sort field |
order | string | No | Sort order: asc or desc |
page | integer | No | Page number |
limit | integer | No | Items per page |
Example Request
curl -X GET "https://api.adaptocms.com/public/custom-collections?language=en" \
-H "x-api-key: YOUR_API_KEY"
Example Response
{
"items": [
{
"id": "coll-001",
"name": "Team Members",
"slug": "team-members",
"description": "Our team directory",
"language": "en",
"fields": [
{
"name": "role",
"label": "Role",
"type": "text",
"required": true,
"multiple": false
},
{
"name": "bio",
"label": "Biography",
"type": "rich_text",
"required": false,
"multiple": false
},
{
"name": "avatar",
"label": "Avatar",
"type": "image",
"required": false,
"multiple": false
}
],
"status": "published",
"created_at": "2026-01-08T10:00:00",
"updated_at": "2026-01-08T10:00:00"
}
],
"total": 3,
"page": 1,
"limit": 20,
"pages": 1
}
Get Collection by ID
GET /public/custom-collections/{collection_id}
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
collection_id | string (UUID) | Yes | The unique identifier of the collection |
Returns the full collection object including the fields schema definition. Use this to understand the structure of items in the collection before fetching them.
Example Request
curl -X GET "https://api.adaptocms.com/public/custom-collections/coll-001" \
-H "x-api-key: YOUR_API_KEY"
Get Collection by Slug
GET /public/custom-collections/by-slug/{slug}
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | The URL-friendly slug of the collection |
Example Request
curl -X GET "https://api.adaptocms.com/public/custom-collections/by-slug/team-members" \
-H "x-api-key: YOUR_API_KEY"
List Collection Items
GET /public/custom-collections/{collection_id}/items
Returns a paginated list of items in a collection.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Filter by status |
language | string | No | Filter by language code |
translation_of_id | string | No | Filter by translation source ID |
field | string | No | Sort field |
order | string | No | Sort order |
page | integer | No | Page number |
limit | integer | No | Items per page |
Example Request
curl -X GET "https://api.adaptocms.com/public/custom-collections/coll-001/items?language=en&status=published" \
-H "x-api-key: YOUR_API_KEY"
Example Response
{
"items": [
{
"id": "item-001",
"collection_id": "coll-001",
"title": "Jane Doe",
"slug": "jane-doe",
"data": {
"role": "Lead Engineer",
"bio": "<p>Jane has 10 years of experience...</p>"
},
"language": "en",
"status": "published",
"created_at": "2026-01-09T10:00:00",
"updated_at": "2026-01-09T10:00:00",
"published_at": "2026-01-09T12:00:00",
"media_objects_placements": [
{
"placement_key": "avatar",
"media_object": {
"id": "mo-001",
"title": "Jane headshot",
"description": null,
"file_id": "file-001",
"url": "https://media.adaptocms.com/tenants/.../jane.jpg",
"type": "image",
"created_at": "2026-01-09T10:00:00",
"updated_at": "2026-01-09T10:00:00"
},
"caption": "Jane Doe, Lead Engineer",
"alt_text": "Photo of Jane Doe",
"meta_data": null
}
],
"translation_of_id": null,
"meta_data": null,
"file_urls": {"file-001": "https://media.adaptocms.com/tenants/.../jane.jpg"}
}
],
"total": 5,
"page": 1,
"limit": 20,
"pages": 1
}
List Collection Items Preview
GET /public/custom-collections/{collection_id}/items/preview
Returns a lightweight list without the data, file_urls, or media_objects_placements fields. Use this for listing pages where you only need titles and metadata.
Accepts the same query parameters as the full items list endpoint.
Example Response
{
"items": [
{
"id": "item-001",
"collection_id": "coll-001",
"title": "Jane Doe",
"slug": "jane-doe",
"language": "en",
"status": "published",
"created_at": "2026-01-09T10:00:00",
"updated_at": "2026-01-09T10:00:00",
"published_at": "2026-01-09T12:00:00",
"translation_of_id": null,
"meta_data": null
}
],
"total": 5,
"page": 1,
"limit": 20,
"pages": 1
}
Get Item by ID
GET /public/custom-collections/{collection_id}/items/{item_id}
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
collection_id | string (UUID) | Yes | The collection ID |
item_id | string (UUID) | Yes | The item ID |
Get Item by Slug
GET /public/custom-collections/{collection_id}/items/by-slug/{slug}
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
collection_id | string (UUID) | Yes | The collection ID |
slug | string | Yes | The URL-friendly slug of the item |
Use Case: Dynamic Collection Pages
// Astro example: src/pages/team/[slug].astro
---
const collectionSlug = 'team-members';
// First, resolve collection ID from slug
const collection = await fetch(
`${import.meta.env.ADAPTO_URL}/public/custom-collections/by-slug/${collectionSlug}`,
{ headers: { 'x-api-key': import.meta.env.ADAPTO_API_KEY } }
).then(r => r.json());
// Then fetch item by slug
const member = await fetch(
`${import.meta.env.ADAPTO_URL}/public/custom-collections/${collection.id}/items/by-slug/${Astro.params.slug}`,
{ headers: { 'x-api-key': import.meta.env.ADAPTO_API_KEY } }
).then(r => r.json());
---
<h1>{member.title}</h1>
<p>{member.data.role}</p>
<div set:html={member.data.bio} />
{/* Render media placements */}
{member.media_objects_placements
.filter(p => p.media_object.type === 'image')
.map(p => (
<img
src={p.media_object.url}
alt={p.alt_text || p.media_object.title}
/>
))
}