openapi: 3.1.0
info:
  title: Collections API
  description: |-
    API for managing collections

    ### Resource identifier format

    Resource identifiers (the `id` field and related global-identifier fields) are returned as JSON
    integers by default. To receive them as strings instead, send the
    `X-Loop-Global-Identifier-Type: string` request header.
  version: "v1"
servers:
  - url: https://api.loopreturns.com/api/2026-10
    description: 2026-10 (Stable)
  - url: https://api.loopreturns.com/api/2026-07
    description: 2026-07
  - url: https://api.loopreturns.com/api/unstable
    description: Unstable (Preview)
tags:
  - name: Collections
paths:
  /collections:
    post:
      summary: Create Collection
      description: Create a new collection.
      operationId: createCollection
      tags:
        - Collections
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCollectionRequest"
      responses:
        "201":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "422":
          description: "Unprocessable Entity"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
      security:
        - write: []
    put:
      summary: Upsert Collection
      description: Upsert a collection, identified using the collection's `name` and `external_id`.
      operationId: upsertCollection
      tags:
        - Collections
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpsertCollectionRequest"
      responses:
        "201":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "422":
          description: "Unprocessable Entity"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
      security:
        - write: []
    get:
      summary: List Collections
      description: Retrieve a list of collections. The list can be filtered, sorted, and
        paginated using the parameters below.
      operationId: listCollections
      tags:
        - Collections
      parameters:
        - name: external_id
          in: query
          description: "An `external_id` used to filter the collections."
          example: 71263csss-12uhsdfh-2138dja
          required: false
          schema:
            type: string
            maxLength: 64
        - name: limit
          in: query
          description: "The maximum number of collections returned."
          example: 20
          required: false
          schema:
            type: integer
            maximum: 250
            default: 50
        - name: sort_order
          in: query
          description: "The sort order for the results."
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
            default: asc
        - name: cursor
          in: query
          description: "The cursor to return the next page of results."
          required: false
          schema:
            type: string
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                type: object
                required:
                  - collections
                properties:
                  collections:
                    type: array
                    items:
                      $ref: "#/components/schemas/CollectionResponseData"
                  next_page_url:
                    type: [string, "null"]
                  previous_page_url:
                    type: [string, "null"]
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - read: []
  /collections/{id}:
    get:
      summary: Get Collection
      description: Get details about a single collection.
      operationId: getCollection
      tags:
        - Collections
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 23141001200513
          required: true
          description: The unique identifier for the collection, created by Loop.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - read: []
    put:
      summary: Update Collection
      description: Update a collection.
      operationId: updateCollection
      tags:
        - Collections
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 23141001200513
          required: true
          description: The unique identifier for the collection, created by Loop.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateCollectionRequest"
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
        "422":
          description: "Unprocessable Entity"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
      security:
        - write: []
    patch:
      summary: Update Collection
      description: Update a collection.
      operationId: patchCollection
      tags:
        - Collections
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 23141001200513
          required: true
          description: The unique identifier for the collection, created by Loop.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateCollectionRequest"
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
        "422":
          description: "Unprocessable Entity"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
      security:
        - write: []
    delete:
      summary: Delete Collection
      description: Delete a collection.
      operationId: deleteCollection
      tags:
        - Collections
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 23141001200513
          required: true
          description: The unique identifier for the collection, created by Loop.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionDeleteResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - write: []
  /collections/external/{externalId}:
    get:
      summary: Get Collection by External ID
      description: >-
        Get details about a single collection by its commerce `external_id`.
      operationId: getCollectionByExternalId
      tags:
        - Collections
      parameters:
        - in: path
          name: externalId
          schema:
            type: string
            maxLength: 64
            examples:
              - 564400c7-7a6b-4f29-b6a5-7bb580b2992c
          required: true
          description: The identifier used by an external source to identify the collection.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - read: []
    delete:
      summary: Delete Collection by External ID
      description: >-
        Delete a collection by its commerce `external_id`.
      operationId: deleteCollectionByExternalId
      tags:
        - Collections
      parameters:
        - in: path
          name: externalId
          schema:
            type: string
            maxLength: 64
            examples:
              - 564400c7-7a6b-4f29-b6a5-7bb580b2992c
          required: true
          description: The identifier used by an external source to identify the collection.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionDeleteByExternalIdResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - write: []
  /collections/{id}/products/{productId}:
    post:
      summary: Add Product to Collection
      description: Attach a product to a collection.
      operationId: attachProductToCollection
      tags:
        - Collections
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 23141001200513
          required: true
          description: The unique identifier for the collection, created by Loop.
        - in: path
          name: productId
          schema:
            type: integer
            examples:
              - 77892011
          required: true
          description: The unique identifier for the product, created by Loop.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - write: []
    delete:
      summary: Delete Product from Collection
      description: Delete a product from a collection.
      operationId: deleteProductFromCollection
      tags:
        - Collections
      parameters:
        - in: path
          name: id
          schema:
            type: integer
            examples:
              - 23141001200513
          required: true
          description: The unique identifier for the collection, created by Loop.
        - in: path
          name: productId
          schema:
            type: integer
            examples:
              - 77892011
          required: true
          description: The unique identifier for the product, created by Loop.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - write: []
  /collections/external/{collectionExternalId}/products/external/{productExternalId}:
    post:
      summary: Add Product to Collection by External ID
      description: Attach a product to a collection by commerce `external_id`.
      operationId: attachProductToCollectionByExternalId
      tags:
        - Collections
      parameters:
        - in: path
          name: collectionExternalId
          schema:
            type: string
            maxLength: 64
            examples:
              - 564400c7-7a6b-4f29-b6a5-7bb580b2992c
          required: true
          description: The identifier used by an external source to identify the collection.
        - in: path
          name: productExternalId
          schema:
            type: string
            maxLength: 64
            examples:
              - 564400c7-7a6b-4f29-b6a5-7bb580b2992c
          required: true
          description: The identifier used by an external source to identify the product.
      responses:
        "201":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - write: []
    delete:
      summary: Delete Product from Collection by External ID
      description: Delete a product from a collection by commerce `external_id`.
      operationId: deleteProductFromCollectionByExternalId
      tags:
        - Collections
      parameters:
        - in: path
          name: collectionExternalId
          schema:
            type: string
            maxLength: 64
            examples:
              - 564400c7-7a6b-4f29-b6a5-7bb580b2992c
          required: true
          description: The identifier used by an external source to identify the collection.
        - in: path
          name: productExternalId
          schema:
            type: string
            maxLength: 64
            examples:
              - 564400c7-7a6b-4f29-b6a5-7bb580b2992c
          required: true
          description: The identifier used by an external source to identify the product.
      responses:
        "200":
          description: "Success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionWrappedResponse"
        "401":
          description: "Unauthorized"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnauthorizedResponse"
        "404":
          description: "Not Found"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
      security:
        - write: []
components:
  schemas:
    CreateCollectionRequest:
      type: object
      properties:
        external_id:
          type: [string, "null"]
          maxLength: 100
          examples:
            - 71263csss-12uhsdfh-2138dja
          description: "The provided `external_id` must be unique. If a non-unique
            `external_id` is provided, the API will respond with an error."
        sales_channel:
          type: string
          maxLength: 255
          examples:
            - shopify
          description: "If a channel with this name is not already associated with the
            shop, it will be used to create a new channel."
        name:
          type: string
          maxLength: 255
          description: "The name of the collection."
          examples:
            - Footwear
        products:
          type: [array, "null"]
          items:
            $ref: "#/components/schemas/ProductRequestData"
          description: "An array of products to add to the collection."
      required:
        - name
        - sales_channel
    UpsertCollectionRequest:
      type: object
      properties:
        external_id:
          type: string
          maxLength: 100
          description: "The identifier used by an external source to identify the collection."
          examples:
            - 1263csss-12uhsdfh-2138dja
        sales_channel:
          type: string
          maxLength: 255
          examples:
            - shopify
          description: "If a channel with this name is not already associated with the shop, it
            will be used to create a new channel."
        name:
          type: string
          maxLength: 255
          description: "The name of the collection."
          examples:
            - Footwear
        products:
          type: [array, "null"]
          items:
            $ref: "#/components/schemas/ProductRequestData"
          description: "An array of products to add to the collection."
      required:
        - external_id
        - name
        - sales_channel
    UpdateCollectionRequest:
      type: object
      properties:
        external_id:
          type: [string, "null"]
          maxLength: 100
          description: "The identifier used by an external source to identify the collection."
          examples:
            - 1263csss-12uhsdfh-2138dja
        sales_channel:
          type: string
          maxLength: 255
          examples:
            - shopify
          description: "If a channel with this name is not already associated with the
            shop, it will be used to create a new channel."
        name:
          type: string
          maxLength: 255
          description: "The name of the collection."
          examples:
            - Footwear
        products:
          type: [array, "null"]
          items:
            $ref: "#/components/schemas/ProductRequestData"
          description: "An array of products to add to the collection."
      required:
        - name
        - sales_channel
    ProductRequestData:
      description: A product to add to the collection, provided as a plain integer
        or as an object with at least one of `id` / `external_id`. When both are sent,
        the server uses `external_id` and ignores `id`. A mismatch is not a 422.
      oneOf:
        - type: integer
          description: The product's global identifier, provided as a plain integer.
        - type: object
          properties:
            id:
              type: integer
              description: The product's global identifier.
            global_id:
              type: integer
              description: Alias for `id` when `id` is omitted.
            external_id:
              type: string
              maxLength: 100
              description: The identifier used by an external source to identify the
                product.
          anyOf:
            - required:
                - id
            - required:
                - external_id
            - required:
                - global_id
    CollectionResponseData:
      type: object
      required:
        - id
      properties:
        id:
          type: integer
          format: int64
          description: The unique identifier for the collection, created by Loop.
        external_id:
          type: [string, "null"]
          description: The identifier used by an external source to identify the collection.
        sales_channel:
          type: [string, "null"]
          description: "The name of the channel."
        name:
          type: [string, "null"]
          description: The name of the collection.
        products:
          type: [array, "null"]
          items:
            $ref: "#/components/schemas/ProductResponse"
          description: An array of products associated with the collection.
    CollectionWrappedResponse:
      type: object
      required:
        - collection
      properties:
        collection:
          $ref: "#/components/schemas/CollectionResponseData"
    CollectionDeleteResponse:
      type: object
      properties:
        id:
          type: integer
          format: int64
          description: The unique identifier for the collection, created by Loop.
          examples:
            - 23141001200513
        success:
          type: boolean
          description: Whether the collection was successfully deleted.
          examples:
            - true
    CollectionDeleteByExternalIdResponse:
      type: object
      required:
        - id
        - external_id
        - success
      properties:
        id:
          type: integer
          format: int64
          description: The unique identifier for the collection, created by Loop.
          examples:
            - 23141001200513
        external_id:
          type: string
          description: The identifier used by an external source to identify the collection.
          examples:
            - 564400c7-7a6b-4f29-b6a5-7bb580b2992c
        success:
          type: boolean
          description: Whether the collection was successfully deleted.
          examples:
            - true
    ProductResponse:
      title: ProductResponse
      type: object
      properties:
        id:
          type: [integer, "null"]
          format: int64
          minimum: 1
          examples:
            - 810772
          readOnly: true
        external_id:
          type:
            - string
            - "null"
          examples:
            - 564400c7-7a6b-4f29-b6a5-7bb580b2992c
        sku:
          type:
            - string
            - "null"
          examples:
            - SHOES-US44-M-Brwn
        name:
          type: [string, "null"]
    NotFoundResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          examples:
            - "Resource not found."
    UnauthorizedResponse:
      type: object
      required:
        - errors
      properties:
        errors:
          type: string
          examples:
            - "Unauthorized."
    ValidationErrorResponse:
      type: object
      description: >-
        Returned when the request body fails validation. Uses Laravel's standard
        validation error shape.
      properties:
        message:
          type: string
          examples:
            - "The name field is required."
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          examples:
            - name:
                - "The name field is required."
  securitySchemes:
    read:
      type: apiKey
      in: header
      name: X-Authorization
      description: 'API Scope: "Collections (read)"'
      x-scope: Collections (Read)
    write:
      name: X-Authorization
      type: apiKey
      in: header
      description: 'API Scope: "Collections (write)"'
