---
title: "Custom Collections API"
url: "https://adaptocms.com/docs/custom-collections-api/"
source: adaptocms.com
---

# 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`

`/v1/custom-collections`

List collections (paginated)

`GET`

`/v1/custom-collections/{collection_id}`

Get collection by ID

`GET`

`/v1/custom-collections/by-slug/{slug}`

Get collection by slug

`GET`

`/v1/custom-collections/{collection_id}/items`

List collection items (paginated)

`GET`

`/v1/custom-collections/{collection_id}/items/preview`

List item previews (lightweight)

`GET`

`/v1/custom-collections/{collection_id}/items/{item_id}`

Get item by ID

`GET`

`/v1/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}/v1/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 /v1/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://public-api.adaptocms.com/v1/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 /v1/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://public-api.adaptocms.com/v1/custom-collections/coll-001" \
  -H "x-api-key: YOUR_API_KEY"
```

* * *

## Get Collection by Slug

```
GET /v1/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://public-api.adaptocms.com/v1/custom-collections/by-slug/team-members" \
  -H "x-api-key: YOUR_API_KEY"
```

* * *

## List Collection Items

```
GET /v1/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://public-api.adaptocms.com/v1/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 /v1/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 /v1/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 /v1/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}/v1/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}/v1/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}
    />
  ))
}
```
