# Collections API reference

```yaml
openapi: 3.0.3
info:
  title: Collections API
  # Required by OpenAPI. This info block replaces common.yml's wholesale in the
  # single-app docs merge, so omitting version made dist/docs/collections.yml an
  # invalid spec; kept in step with common.yml's version.
  version: 0.0.1
  x-cld-module-context: cloud
  x-cld-module-name: collections
  description: |
    The Collections API enables you to organize your assets into **collections** and share them with others through **public pages**. Create **manual** collections with an explicit list of assets, or **dynamic** collections defined by a saved filter expression that automatically reflects matching assets.

    The API uses **Basic Authentication** over HTTPS with your **API Key** and **API Secret** from the [Cloudinary Console](https://console.cloudinary.com/app/settings/api-keys), or a user **bearer JWT** as an alternative.

    Key capabilities:
    * Create, update, and delete manual or dynamic collections of assets
    * Add assets to, remove assets from, and list the assets in a collection
    * Publish a collection as a shareable public page, optionally restricted to allowed emails or domains
    * List, update, and unpublish a collection's public pages

    **Note:** Just like the Media Library, Collections in the API are a premium **Assets Enterprise** feature and depend on your account setup. For the **Assets Free** plan, collections are limited to a maximum of 3 collections, with up to 50 assets in a collection. [Contact us](https://cloudinary.com/contact) if you need more collections.
servers:
  - url: https://api.cloudinary.com/v2/asset_management
security:
  - api_key: []
  - user_jwt: []
tags:
  - name: Collections
    description: Create and manage collections of assets, and publish shareable public pages for them.
paths:
  /{cloud_name}/collections:
    parameters:
      - $ref: '#/components/parameters/cloudName'
    get:
      tags:
        - Collections
      x-logical-service: Collections
      summary: List collections
      description: >
        Returns the collections the caller may read, most-recently-updated first by
        default (`updated_at desc`). Results are paginated: pass the returned `next_cursor`
        back to fetch the next page.
      operationId: listCollections
      parameters:
        - in: query
          name: query
          required: false
          schema:
            type: string
          description: Case-insensitive filter on the collection name. Returns collections with a word in their name that starts with this text (for example, `sum` matches "Summer Campaign").
          example: summer
        - in: query
          name: sort_by
          required: false
          schema:
            type: string
          description: >
            Sort expression: `<field> <direction>`, optionally comma-separated for a
            multi-key sort (e.g. `name asc, created_at desc`). `field` is one of `name`,
            `created_at`, or `updated_at`, the same field names the response uses, and
            `direction` is `asc` or `desc`. Defaults to `updated_at desc`.
          example: name asc
        - in: query
          name: max_results
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
          description: Maximum number of collections to return (1-100, default 20).
          example: 20
        - in: query
          name: next_cursor
          required: false
          schema:
            type: string
          description: Opaque cursor from a previous response's `next_cursor` to fetch the next page.
      responses:
        '200':
          description: Collections listed successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.ListCollectionsResponse'
        '400':
          $ref: '#/components/responses/BadRequestResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
    post:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Create a collection
      description: Creates a collection under the given product environment and makes the caller the owner.
      operationId: createCollection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Contract.CreateCollectionRequestPayload'
            example:
              name: Summer campaign images
              type: dynamic
              configuration:
                expression: tags = "summer-campaign" AND resource_type:image
      responses:
        '201':
          description: Collection created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.CreateCollectionResponse'
        '400':
          $ref: '#/components/responses/BadRequestResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
  /{cloud_name}/collections/{collection_id}:
    parameters:
      - $ref: '#/components/parameters/cloudName'
      - in: path
        name: collection_id
        required: true
        schema:
          type: string
        description: External ID of the collection to retrieve, update, or delete.
        example: col_8f3a2b1c9d4e
    get:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Get a collection
      description: Returns a single collection by its external ID.
      operationId: getCollection
      responses:
        '200':
          description: Collection retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.GetCollectionResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
    put:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Update a collection
      description: >
        Updates a collection's name, description, and (for dynamic collections) configuration.
        Only the fields you include in the request are changed. The caller must be allowed to
        update the collection. A collection's type (manual/dynamic) is fixed at creation and
        can't be changed.
      operationId: updateCollection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Contract.UpdateCollectionRequestPayload'
      responses:
        '200':
          description: Collection updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.UpdateCollectionResponse'
        '400':
          $ref: '#/components/responses/BadRequestResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
    delete:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Delete a collection
      description: >
        Deletes a collection. The caller must be allowed to update the collection; the
        underlying assets are not modified or deleted.
      operationId: deleteCollection
      responses:
        '200':
          description: Collection deleted successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.DeleteCollectionResponse'
              example:
                message: Collection deleted successfully.
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
  /{cloud_name}/collections/{collection_id}/assets:
    parameters:
      - $ref: '#/components/parameters/cloudName'
      - in: path
        name: collection_id
        required: true
        schema:
          type: string
        description: External ID of the collection whose assets to list, add, or remove.
        example: col_8f3a2b1c9d4e
    get:
      tags:
        - Collections
      x-logical-service: Collections
      summary: List a collection's assets
      description: >
        Returns the assets in a collection, newest first by default. For a `manual`
        collection, these are the assets that have been added to it; for a `dynamic`
        collection, they're the assets currently matching its filter expression. Only
        assets the caller has permission to view are included. Results are paginated: pass
        the returned `next_cursor` back to fetch the next page.
      operationId: listCollectionAssets
      parameters:
        - in: query
          name: sort_by
          required: false
          schema:
            type: string
          description: >
            Sort expression: `<field> <direction>`, optionally comma-separated for a
            multi-key sort (e.g. `bytes desc, created_at asc`). `field` is one of
            `created_at`, `uploaded_at`, `public_id`, `display_name`, or `bytes`, and
            `direction` is `asc` or `desc`. Defaults to `created_at desc`. When paging with
            `next_cursor`, send the same `sort_by` on every page; a cursor is only valid for
            the sort it was issued with.
          example: bytes desc
        - in: query
          name: max_results
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
          description: Maximum number of assets to return (1-100, default 20).
          example: 20
        - in: query
          name: next_cursor
          required: false
          schema:
            type: string
          description: >
            Opaque cursor from a previous response's `next_cursor` to fetch the next page. Send it
            with the same `sort_by` (and `max_results`) as the request that returned it.
      responses:
        '200':
          description: Assets listed successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.ListCollectionAssetsResponse'
        '400':
          $ref: '#/components/responses/BadRequestResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
    post:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Add assets to a collection
      description: >
        Adds assets to an existing collection. The caller must be allowed to update the
        collection and view the assets.
      operationId: addCollectionAssets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Contract.AddCollectionAssetsRequestPayload'
      responses:
        '200':
          description: Assets added successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.AddCollectionAssetsResponse'
              example:
                data:
                  id: col_8f3a2b1c9d4e
                  name: Summer Campaign Assets
                  creator_type: user
                  creator_id: usr_4a1b2c3d5e6f
                  created_at: '2026-03-14T10:15:00Z'
                  updated_at: '2026-03-15T09:30:00Z'
                  description: Approved photography for the summer campaign launch.
                  type: manual
        '400':
          $ref: '#/components/responses/BadRequestResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
    delete:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Remove assets from a collection
      description: >
        Removes the specified assets from an existing collection. The caller must be allowed
        to update the collection. Assets that are not in the collection are ignored, and the
        underlying assets are not modified or deleted.
      operationId: removeCollectionAssets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Contract.RemoveCollectionAssetsRequestPayload'
      responses:
        '200':
          description: Assets removed successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.RemoveCollectionAssetsResponse'
              example:
                data:
                  id: col_8f3a2b1c9d4e
                  name: Summer Campaign Assets
                  creator_type: user
                  creator_id: usr_4a1b2c3d5e6f
                  created_at: '2026-03-14T10:15:00Z'
                  updated_at: '2026-03-15T09:30:00Z'
                  description: Approved photography for the summer campaign launch.
                  type: manual
        '400':
          $ref: '#/components/responses/BadRequestResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
  /{cloud_name}/collections/{collection_id}/public_pages:
    parameters:
      - $ref: '#/components/parameters/cloudName'
      - in: path
        name: collection_id
        required: true
        schema:
          type: string
        description: External ID of the collection to create or list public pages for.
        example: col_8f3a2b1c9d4e
    post:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Publish a collection
      description: Creates a shareable public page for the collection and returns its URL.
      operationId: createCollectionPublicPage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Contract.CreatePublicPageRequestPayload'
      responses:
        '201':
          description: Public page created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.CreatePublicPageResponse'
        '400':
          $ref: '#/components/responses/BadRequestResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
    get:
      tags:
        - Collections
      x-logical-service: Collections
      summary: List a collection's public pages
      description: Returns the public pages (shareable links) that have been created for the collection.
      operationId: listCollectionPublicPages
      responses:
        '200':
          description: Public pages listed successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.ListCollectionPublicPagesResponse'
              example:
                data:
                  - id: pub_2c4d6e8f0a1b
                    url: https://collection.cloudinary.com/your-cloud-name/pub_2c4d6e8f0a1b
                    name: Summer Campaign Preview
                    available_from: '2026-03-20T00:00:00Z'
                    expires_at: '2026-04-20T00:00:00Z'
                    show_download_original_option: true
                    download_transformations:
                      - name: thumbnail_400
                        transformation: c_thumb,w_400,g_face
                        types: [image]
                    protection_mode: none
                    allowed_emails: []
                    allowed_domains: []
                    allow_any: false
                    custom_layout_url: null
                  - id: pub_5e7f9a1b3c2d
                    url: https://collection.cloudinary.com/your-cloud-name/pub_5e7f9a1b3c2d
                    name: Summer Campaign Press Kit
                    available_from: '2026-04-01T00:00:00Z'
                    expires_at: '2026-06-30T00:00:00Z'
                    show_download_original_option: false
                    download_transformations: []
                    protection_mode: email_otp
                    allowed_emails:
                      - alice@example.com
                    allowed_domains:
                      - example.com
                    allow_any: false
                    custom_layout_url: null
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
  /{cloud_name}/collections/{collection_id}/public_pages/{public_page_id}:
    parameters:
      - $ref: '#/components/parameters/cloudName'
      - in: path
        name: collection_id
        required: true
        schema:
          type: string
        description: External ID of the collection the public page belongs to.
        example: col_8f3a2b1c9d4e
      - in: path
        name: public_page_id
        required: true
        schema:
          type: string
        description: External ID of the public page.
        example: pub_2c4d6e8f0a1b
    get:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Get a public page
      description: Returns a single public page of a collection by its external ID.
      operationId: getCollectionPublicPage
      responses:
        '200':
          description: Public page retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.GetCollectionPublicPageResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
    put:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Update a public page
      description: >
        Updates the settings of a collection's public page and returns the updated page.
        Protecting a page (`protection_mode: email_otp`) requires the secured-sharing
        feature on your plan; otherwise the request fails with 403. Invalid protection
        settings fail with 400 and error code `invalid_protection_settings`, listing every
        problem found.
      operationId: updateCollectionPublicPage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Contract.UpdatePublicPageRequestPayload'
      responses:
        '200':
          description: Public page updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.UpdatePublicPageResponse'
        '400':
          $ref: '#/components/responses/BadRequestResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
    delete:
      tags:
        - Collections
      x-logical-service: Collections
      summary: Unpublish a collection's public page
      description: >
        Deletes a collection's public page so it's no longer shareable. The collection and
        its assets aren't changed or deleted.
      operationId: deleteCollectionPublicPage
      responses:
        '200':
          description: Public page deleted successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract.DeletePublicPageResponse'
              example:
                message: Public page deleted successfully.
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/ForbiddenResponse'
        '404':
          $ref: '#/components/responses/NotFoundResponse'
        '500':
          $ref: '#/components/responses/InternalServerErrorResponse'
components:
  securitySchemes:
    api_key:
      type: http
      scheme: basic
      description: >
        Cloudinary API key and secret supplied as HTTP Basic credentials
        (`Authorization: Basic base64(api_key:api_secret)`).
    user_jwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        Cloudinary user token supplied as a bearer JWT
        (`Authorization: Bearer <jwt>`). Accepted as an alternative to `api_key`
        on management endpoints; the request is then authorized as the user
        principal rather than the API key.
  parameters:
    cloudName:
      in: path
      name: cloud_name
      description: The cloud name of the product environment.
      example: your-cloud-name
      schema:
        type: string
      required: true
  schemas:
    Contract.CreateCollectionRequestPayload:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: Name of the collection.
          example: Summer Campaign Assets
        description:
          type: string
          description: Optional description of the collection.
          example: Approved photography for the summer campaign launch.
        type:
          type: string
          default: manual
          enum:
            - manual
            - dynamic
          description: >
            Type of the collection. `manual` holds an explicit asset list and starts empty
            until you add assets with the **Add assets to a collection** endpoint; `dynamic` is
            defined by a saved filter expression and is populated automatically.
          example: dynamic
        configuration:
          $ref: '#/components/schemas/Entity.CollectionConfiguration'
    Entity.CollectionConfiguration:
      type: object
      description: >
        Configuration for a `dynamic` collection. Required when creating a `dynamic` collection,
        and not allowed for a `manual` collection.
      properties:
        expression:
          type: string
          description: >
            The [search expression](search_expressions) that defines which assets are included in
            a dynamic collection. Cloudinary evaluates the expression against your product
            environment's assets, so the collection's contents update as assets begin or stop
            matching the criteria. In the Media Library, a collection whose expression was set
            through the API shows it as a single **Expression** filter; any filters added to the
            collection there are combined with it using AND.
          example: tags = "summer-campaign" AND resource_type:image
    Entity.Collection:
      type: object
      description: A collection resource.
      properties:
        id:
          type: string
          description: The external ID of the collection.
          example: col_8f3a2b1c9d4e
        name:
          type: string
          description: Name of the collection.
          example: Summer Campaign Assets
        creator_type:
          type: string
          enum:
            - user
            - api
          description: Indicates whether the collection was created by a Console user or through the API.
          example: user
        creator_id:
          type: string
          description: Who created the collection. When `creator_type` is `user`, this is the external ID of the Console user; Enterprise customers can get more details about the user with the [Get user](provisioning_api#tag/users/GET/users/{user_id}) endpoint of the Provisioning API. When `creator_type` is `api`, this is the API key used to create the collection.
          example: usr_4a1b2c3d5e6f
        created_at:
          type: string
          format: date-time
          description: When the collection was created.
          example: '2026-03-14T10:15:00Z'
        updated_at:
          type: string
          format: date-time
          description: When the collection was last modified.
          example: '2026-03-15T09:30:00Z'
        description:
          type: string
          description: Optional description of the collection.
          example: Approved photography for the summer campaign launch.
        type:
          type: string
          enum:
            - manual
            - dynamic
          description: The type of collection. `manual` holds an explicit asset list and starts empty until you add assets with the **Add assets to a collection** endpoint; `dynamic` is defined by a saved filter expression and is populated automatically.
          example: dynamic
        configuration:
          $ref: '#/components/schemas/Entity.CollectionConfiguration'
        asset_count:
          type: integer
          nullable: true
          description: >
            Number of assets in the collection. Returned by the `Get a collection` and `List collections` endpoints. Omitted from the responses of the endpoints that create, update, or change the assets of a collection.
          example: 12
        cover_asset_url:
          type: string
          nullable: true
          description: >
            Delivery URL of an automatically selected cover asset for the collection. Returned by the `Get a collection` and `List collections` endpoints. Omitted from the responses of the endpoints that create, update, or change the assets of a collection.
          example: https://res.cloudinary.com/demo/image/upload/sample.jpg
    Contract.CreateCollectionResponse:
      type: object
      description: Response returned by the **Create a collection** endpoint.
      properties:
        data:
          $ref: '#/components/schemas/Entity.Collection'
    Contract.GetCollectionResponse:
      type: object
      description: Response returned by the **Get a collection** endpoint.
      properties:
        data:
          $ref: '#/components/schemas/Entity.Collection'
    Contract.UpdateCollectionRequestPayload:
      type: object
      description: >
        The collection attributes to update. Only the fields you include are changed. A
        collection's type is set when the collection is created and can't be changed.
      properties:
        name:
          type: string
          description: New name for the collection. Omit to leave the current name unchanged.
          example: Summer Campaign Assets 2026
        description:
          type: string
          nullable: true
          description: New description for the collection. Send `null` or an empty string to clear it. Omit to leave the current description unchanged.
          example: Approved photography for the 2026 summer campaign launch.
        configuration:
          $ref: '#/components/schemas/Entity.CollectionConfiguration'
    Contract.UpdateCollectionResponse:
      type: object
      description: Response returned by the **Update a collection** endpoint.
      properties:
        data:
          $ref: '#/components/schemas/Entity.Collection'
    Contract.ListCollectionsResponse:
      type: object
      description: >
        Response returned by the **List collections** endpoint. `data` and `total_count`
        are always present, so a search that matches nothing returns
        `{"data": [], "total_count": 0}` rather than an empty object. `next_cursor` follows
        the usual cursor convention and is omitted once there are no further pages.
      required:
        - data
        - total_count
      properties:
        data:
          type: array
          description: The page of collections.
          items:
            $ref: '#/components/schemas/Entity.Collection'
        next_cursor:
          type: string
          nullable: true
          description: Opaque cursor for the next page. Omitted when there are no further pages.
          example: eyJsYXN0X21vZGlmaWVkX2F0IjoxNzQwNTg0MzAwfQ
        total_count:
          type: integer
          description: Total number of collections matching the query across all pages.
          example: 42
    Entity.CollectionAsset:
      type: object
      description: >
        An asset in a collection. Every field is always present in a response: an unset value
        (`display_name`, or `width` / `height` for assets without dimensions) is returned as
        `null` rather than being omitted, the same as on the public page entity.
      required:
        - asset_id
        - public_id
        - display_name
        - resource_type
        - type
        - format
        - bytes
        - width
        - height
        - secure_url
        - created_at
      properties:
        asset_id:
          type: string
          description: >
            The immutable ID of the asset. This is the value the **Add assets to a
            collection** and **Remove assets from a collection** endpoints accept.
          example: 3515c6000a548515f1134043f9785c2f
        public_id:
          type: string
          description: The public ID of the asset.
          example: summer/beach-hero
        display_name:
          type: string
          nullable: true
          description: The asset's display name, or null when it has none.
          example: beach-hero
        resource_type:
          type: string
          description: The resource type of the asset (`image`, `video`, or `raw`).
          example: image
        type:
          type: string
          description: The delivery type of the asset, e.g. `upload`, `private`, or `authenticated`.
          example: upload
        format:
          type: string
          description: The asset's file format. Empty for raw assets without an extension.
          example: jpg
        bytes:
          type: integer
          format: int64
          description: The asset's size in bytes.
          example: 482113
        width:
          type: integer
          nullable: true
          description: Width in pixels, or null for assets without dimensions (e.g. raw files).
          example: 1920
        height:
          type: integer
          nullable: true
          description: Height in pixels, or null for assets without dimensions (e.g. raw files).
          example: 1080
        secure_url:
          type: string
          description: The HTTPS delivery URL of the asset.
          example: https://res.cloudinary.com/demo/image/upload/v1720000000/summer/beach-hero.jpg
        created_at:
          type: string
          format: date-time
          description: When the asset was created.
          example: '2026-03-14T10:15:00Z'
    Contract.ListCollectionAssetsResponse:
      type: object
      description: >
        Response returned by the **List a collection's assets** endpoint. `data` and
        `total_count` are always present, so a collection with no assets returns
        `{"data": [], "total_count": 0}`. `next_cursor` follows the usual cursor convention
        and is omitted once there are no further pages.
      required:
        - data
        - total_count
      properties:
        data:
          type: array
          description: The page of assets.
          items:
            $ref: '#/components/schemas/Entity.CollectionAsset'
        next_cursor:
          type: string
          nullable: true
          description: Opaque cursor for the next page. Omitted when there are no further pages.
          example: 8edbc61040178db60b0973ca9494bf3a
        total_count:
          type: integer
          description: Total number of assets in the collection that the caller may view.
          example: 12
    Contract.AddCollectionAssetsRequestPayload:
      type: array
      description: A list of `asset_id` values of the assets to add to the collection.
      items:
        type: string
        example: ast_1a2b3c4d5e6f
    Contract.AddCollectionAssetsResponse:
      type: object
      description: Response returned by the **Add assets to a collection** endpoint.
      properties:
        data:
          $ref: '#/components/schemas/Entity.Collection'
    Contract.RemoveCollectionAssetsRequestPayload:
      type: array
      description: A list of `asset_id` values of the assets to remove from the collection.
      items:
        type: string
        example: ast_1a2b3c4d5e6f
    Contract.RemoveCollectionAssetsResponse:
      type: object
      description: Response returned by the **Remove assets from a collection** endpoint.
      properties:
        data:
          $ref: '#/components/schemas/Entity.Collection'
    Contract.DeleteCollectionResponse:
      type: object
      required: [message]
      description: Response returned by the **Delete a collection** endpoint.
      properties:
        message:
          type: string
          description: Confirmation that the collection was deleted.
          example: Collection deleted successfully.
    Contract.CreatePublicPageRequestPayload:
      type: object
      description: >
        Settings for the public page. Every field is optional; omitted fields use their
        documented default.
      properties:
        name:
          type: string
          nullable: true
          description: Optional display name for the public page.
          example: Summer Campaign Preview
        available_from:
          type: string
          format: date-time
          nullable: true
          description: When the page becomes active. If omitted or null, the page is active immediately.
          example: '2026-03-20T00:00:00Z'
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: When the page expires. If omitted or null, the page never expires.
          example: '2026-04-20T00:00:00Z'
        show_download_original_option:
          type: boolean
          nullable: true
          description: >
            Whether viewers can download the original asset. Omit to use the
            account default set by an administrator in the [Media Library Preferences](dam_admin_media_library_options#download_options_for_external_sharing).
          example: true
        download_transformations:
          type: array
          nullable: true
          maxItems: 200
          items:
            $ref: '#/components/schemas/Entity.DownloadTransformationInput'
          description: >
            Named transformations that viewers of the public page can apply when downloading
            assets, referenced by name (see the Admin API's [Get transformations](admin_api#get_transformations)
            endpoint to list the named transformations in your product environment). By default,
            each name must already be set as a transformation template; set
            `create_download_transformations` to `true` to set any listed name that isn't one yet
            automatically. Omit or pass null to leave the page without download
            transformations. At most 200 entries.
          example:
            - name: thumbnail_400
            - name: trimmed_preview
              types: [video]
        create_download_transformations:
          type: boolean
          nullable: true
          default: false
          description: >
            When `true`, any name in `download_transformations` that isn't yet set as a
            transformation template is set as one automatically. It then appears in the
            Console's templates list and counts toward your product environment's template
            limit. When `false` or omitted, the request instead fails with
            `download_transformation_not_available` if any listed name isn't yet set as a
            transformation template, and nothing is changed.
          example: true
    Entity.DownloadTransformationInput:
      type: object
      required: [name]
      description: A named transformation to offer for download on a public page.
      properties:
        name:
          type: string
          description: Name of an existing named transformation in this product environment.
          example: thumbnail_400
        types:
          type: array
          nullable: true
          minItems: 1
          items:
            type: string
            enum: [image, video]
          description: >
            Asset types the transformation is offered for. Restrict this to the types the
            transformation actually produces correct results for (for example, a duration/trim
            transformation only makes sense for video), to avoid download errors for the other
            type. This is a property of the transformation template itself, shared by every public
            page in the product environment, not just this one: it can only be set the first time
            any public page offers this name for download. Omit `types` on that first request to
            make it available for both `image` and `video`. If the name is already available for
            download (set by an earlier request on this or another public page), omit `types` to
            keep its existing setting, or send its exact current list; sending a different list
            fails with `download_transformation_types_conflict`. An empty list is rejected.
          example: [video]
    Entity.DownloadTransformation:
      type: object
      description: A transformation template offered for download on a public page.
      properties:
        name:
          type: string
          description: Name of the transformation template.
          example: thumbnail_400
        transformation:
          type: string
          description: The transformation definition behind the name.
          example: c_thumb,w_400,g_face
        types:
          type: array
          items:
            type: string
            enum: [image, video]
          description: Asset types the transformation is offered for.
          example: [image]
    Entity.PublicPage:
      type: object
      description: >
        A public page (shareable link) for a collection. Every field is always present in a
        response: an unset value is returned as `null` (or `false` / `[]`) rather than being
        omitted, so a read-modify-write round-trip never has to guess what an absent field meant.
      required:
        - id
        - url
        - name
        - available_from
        - expires_at
        - show_download_original_option
        - download_transformations
        - protection_mode
        - allowed_emails
        - allowed_domains
        - allow_any
        - custom_layout_url
      properties:
        id:
          type: string
          description: External ID of the public page.
          example: pub_2c4d6e8f0a1b
        url:
          type: string
          description: >
            The shareable URL of the public page, in the form
            `https://collection.cloudinary.com/<cloud_name>/<public_page_id>`. It doesn't
            include the collection ID.
          example: https://collection.cloudinary.com/your-cloud-name/pub_2c4d6e8f0a1b
        name:
          type: string
          nullable: true
          description: Display name of the public page. May be null.
          example: Summer Campaign Preview
        available_from:
          type: string
          format: date-time
          nullable: true
          description: When the page becomes active. Null if the page is active immediately.
          example: '2026-03-20T00:00:00Z'
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: When the page expires. Null if the page never expires.
          example: '2026-04-20T00:00:00Z'
        show_download_original_option:
          type: boolean
          description: Whether the option to download the original asset is shown.
          example: true
        download_transformations:
          type: array
          items:
            $ref: '#/components/schemas/Entity.DownloadTransformation'
          description: >
            Transformation templates that viewers of the public page can apply when downloading
            assets. Empty when the page offers none.
          example:
            - name: thumbnail_400
              transformation: c_thumb,w_400,g_face
              types: [image]
        protection_mode:
          type: string
          enum:
            - none
            - email_otp
          description: >
            Secured-sharing protection mode of the public page. `none` means the page is
            unprotected; `email_otp` means viewers must verify an allowed email address with a
            one-time passcode before accessing the page.
          example: email_otp
        allowed_emails:
          type: array
          items:
            type: string
            format: email
          description: >
            Email addresses allowed to access the page when it is protected, stored lowercased.
            Empty when the page is unprotected.
          example:
            - alice@example.com
            - bob@example.com
        allowed_domains:
          type: array
          items:
            type: string
            pattern: '^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$'
          description: >
            Email domains allowed to access the page when it is protected, stored lowercased
            and without a leading `@`. Empty when the page is unprotected.
          example:
            - example.com
        allow_any:
          type: boolean
          description: Whether any email address is allowed to access the page when it is protected.
          example: false
        custom_layout_url:
          type: string
          nullable: true
          description: >
            URL of a custom HTML page in your product environment that replaces the public
            page's default design. Null when the page uses the default design. Only applied
            while the custom collection layout feature is on your plan.
          example: https://res.cloudinary.com/demo/raw/upload/layouts/summer-page.html
    Contract.CreatePublicPageResponse:
      type: object
      description: Response returned by the **Publish a collection** endpoint.
      properties:
        data:
          $ref: '#/components/schemas/Entity.PublicPage'
    Contract.ListCollectionPublicPagesResponse:
      type: object
      description: >
        Response returned by the **List a collection's public pages** endpoint. `data` is
        always present; a collection with no public pages returns `{"data": []}`.
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Entity.PublicPage'
    Contract.GetCollectionPublicPageResponse:
      type: object
      description: Response returned by the **Get a public page** endpoint.
      properties:
        data:
          $ref: '#/components/schemas/Entity.PublicPage'
    Contract.UpdatePublicPageRequestPayload:
      type: object
      description: >
        Partial-update payload. Only the fields you include are forwarded and
        changed; any field you omit is left unchanged. Sending null does not
        clear a value.
      properties:
        name:
          type: string
          nullable: true
          description: New display name for the public page. Omit to leave the current value unchanged.
          example: Summer Campaign Preview
        available_from:
          type: string
          format: date-time
          nullable: true
          description: New activation time for the page. Omit to leave the current value unchanged.
          example: '2026-03-20T00:00:00Z'
        expires_at:
          type: string
          format: date-time
          nullable: true
          description: New expiry time for the page. Omit to leave the current value unchanged.
          example: '2026-04-20T00:00:00Z'
        show_download_original_option:
          type: boolean
          nullable: true
          description: >
            Whether viewers may download the original asset. Omit to leave the
            current value unchanged.
          example: true
        download_transformations:
          type: array
          nullable: true
          maxItems: 200
          items:
            $ref: '#/components/schemas/Entity.DownloadTransformationInput'
          description: >
            Named transformations that viewers of the public page can apply when downloading
            assets, referenced by name (see the Admin API's [Get transformations](admin_api#get_transformations)
            endpoint to list the named transformations in your product environment). The list
            replaces the page's current download transformations; pass an empty list to remove
            them all. Omit to leave the current value unchanged. At most 200 entries.
          example:
            - name: thumbnail_400
            - name: trimmed_preview
              types: [video]
        create_download_transformations:
          type: boolean
          nullable: true
          default: false
          description: >
            When `true`, any name in `download_transformations` that isn't yet set as a
            transformation template is set as one automatically. It then appears in the
            Console's templates list and counts toward your product environment's template
            limit. When `false` or omitted, the request instead fails with
            `download_transformation_not_available` if any listed name isn't yet set as a
            transformation template, and nothing is changed.
          example: true
        protection_mode:
          type: string
          nullable: true
          enum:
            - none
            - email_otp
          description: >
            Secured-sharing protection mode. `none` removes protection and clears the allowed
            emails, domains and `allow_any`. `email_otp` protects the page so viewers must
            verify an allowed email address with a one-time passcode; a protected page needs at
            least one allowed email or domain, or `allow_any: true`. Omit to leave the current
            mode unchanged: on a protected page the allowlist fields below then update its
            allowlist, but on an unprotected page they are ignored, so send `email_otp` to
            protect it.
          example: email_otp
        allowed_emails:
          type: array
          nullable: true
          items:
            type: string
            format: email
          description: >
            Email addresses allowed to access the page when it is protected. The list replaces
            the current one; send an empty array to clear it (the page must still satisfy the
            `allow_any` rule). Together with `allowed_domains`, at most 500 entries. Omit to
            leave the current value unchanged.
          example:
            - alice@example.com
            - bob@example.com
        allowed_domains:
          type: array
          nullable: true
          items:
            type: string
            pattern: '^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$'
          description: >
            Email domains allowed to access the page when it is protected, as bare lowercase
            domains such as `example.com`. The list replaces the current one; send an empty array
            to clear it (the page must still satisfy the `allow_any` rule). Together with
            `allowed_emails`, at most 500 entries. Omit to leave the current value unchanged.
          example:
            - example.com
        allow_any:
          type: boolean
          nullable: true
          description: >
            Whether any email address is allowed to access the page when it is protected. After
            the update, an `email_otp` page needs `allow_any: true` or at least one entry in
            `allowed_emails` or `allowed_domains`; otherwise the request fails with 400
            `invalid_protection_settings`. Omit to leave the current value unchanged.
          example: false
        custom_layout_url:
          type: string
          nullable: true
          maxLength: 2048
          pattern: '^(|https://[A-Za-z0-9.-]+\.cloudinary\.com/.*)$'
          description: >
            URL of a custom HTML page in your product environment to use in place of the public
            page's default design. It must be an `https` URL on a `cloudinary.com` host that
            belongs to this product environment, at most 2048 bytes. Setting it requires the
            custom collection layout feature on your plan; otherwise the request fails with 403.
            Pass an empty string to return to the default design. Omit to leave the current value
            unchanged.
          example: https://res.cloudinary.com/demo/raw/upload/layouts/summer.html
    Contract.UpdatePublicPageResponse:
      type: object
      description: Response returned by the **Update a public page** endpoint.
      properties:
        data:
          $ref: '#/components/schemas/Entity.PublicPage'
    Contract.DeletePublicPageResponse:
      type: object
      description: Response returned by the **Unpublish a collection's public page** endpoint.
      required:
        - message
      properties:
        message:
          type: string
          description: Confirmation that the public page was deleted.
          example: Public page deleted successfully.
  responses:
    CreatedResponse:
      description: Resource created successfully.
      content:
        application/json:
          schema:
            type: object
            properties:
              id:
                type: string
                format: uuid
                description: Unique identifier of the created resource.
    BadRequestResponse:
      description: Bad request.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                description: Human-readable description of the error.
                example: 'Bad Request. Please check your request for errors.'
    UnauthorizedResponse:
      description: Unauthorized.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                description: Human-readable description of the error.
                example: 'Invalid credentials'
    ForbiddenResponse:
      description: Forbidden.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                description: Human-readable description of the error.
                example: 'Forbidden.'
    NotFoundResponse:
      description: Not found.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                description: Human-readable description of the error.
                example: 'Not found.'
    ConflictResponse:
      description: Conflict.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                description: Human-readable description of the error.
                example: 'The resource is in a state that does not allow the requested action.'
    TooManyRequestsResponse:
      description: Too many requests.
      headers:
        Retry-After:
          description: Number of seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                description: Human-readable description of the error.
                example: 'Too many concurrent requests. Please retry later.'
    InternalServerErrorResponse:
      description: Internal server error.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                description: Human-readable description of the error.
                example: 'Internal server error. Please try again later.'
    ServiceUnavailableResponse:
      description: Service unavailable.
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
                description: Human-readable description of the error.
                example: 'Service unavailable. Please try again later.'
```
