Spree Commerce Product Catalog API

Products and categories

OpenAPI Specification

spree-product-catalog-api-openapi.yml Raw ↑
openapi: 3.0.3
info:
  title: Admin Account / Address Product Catalog API
  contact:
    name: Spree Commerce
    url: https://spreecommerce.org
    email: hello@spreecommerce.org
  description: "Spree Admin API v3 - Administrative API for managing products, orders, and store settings.\n\n## Authentication\n\nThe Admin API requires a secret API key passed in the `x-spree-api-key` header.\nSecret API keys can be generated in the Spree admin dashboard.\n\n## Response Format\n\nAll responses are JSON. List endpoints return paginated responses with `data` and `meta` keys.\nSingle resource endpoints return a flat JSON object.\n\n## Resource IDs\n\nEvery resource is identified by an opaque string ID (e.g. `prod_86Rf07xd4z`,\n`variant_k5nR8xLq`, `or_UkLWZg9DAJ`). Use these IDs everywhere — URL paths,\nrequest bodies, and Ransack filters all accept them directly.\n\n## Error Handling\n\nErrors return a consistent format:\n```json\n{\n  \"error\": {\n    \"code\": \"validation_error\",\n    \"message\": \"Validation failed\",\n    \"details\": { \"name\": [\"can't be blank\"] }\n  }\n}\n```\n"
  version: v3
servers:
- url: http://{defaultHost}
  variables:
    defaultHost:
      default: localhost:3000
tags:
- name: Product Catalog
  description: Products and categories
paths:
  /api/v3/store/categories:
    get:
      summary: List categories
      tags:
      - Product Catalog
      security:
      - api_key: []
      description: Returns a paginated list of categories for the current store
      x-codeSamples:
      - lang: javascript
        label: Spree SDK
        source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n  baseUrl: 'https://your-store.com',\n  publishableKey: '<api-key>',\n})\n\nconst categories = await client.categories.list({\n  page: 1,\n  limit: 25,\n})"
      parameters:
      - name: x-spree-api-key
        in: header
        required: true
        schema:
          type: string
      - name: page
        in: query
        required: false
        schema:
          type: integer
      - name: limit
        in: query
        required: false
        schema:
          type: integer
      - name: q[name_cont]
        in: query
        required: false
        description: Filter by name
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: Comma-separated list of fields to include (e.g., name,slug,price). id is always included.
        schema:
          type: string
      responses:
        '200':
          description: categories found
          content:
            application/json:
              example:
                data:
                - id: ctg_UkLWZg9DAJ
                  name: taxonomy_3
                  permalink: taxonomy-3
                  position: 0
                  depth: 0
                  meta_title: null
                  meta_description: null
                  meta_keywords: null
                  children_count: 1
                  parent_id: null
                  description: ''
                  description_html: ''
                  image_url: null
                  square_image_url: null
                  is_root: true
                  is_child: false
                  is_leaf: false
                - id: ctg_gbHJdmfrXB
                  name: taxon_3
                  permalink: taxonomy-3/taxon-3
                  position: 0
                  depth: 1
                  meta_title: null
                  meta_description: null
                  meta_keywords: null
                  children_count: 1
                  parent_id: ctg_UkLWZg9DAJ
                  description: ''
                  description_html: ''
                  image_url: null
                  square_image_url: null
                  is_root: false
                  is_child: true
                  is_leaf: false
                - id: ctg_EfhxLZ9ck8
                  name: taxon_4
                  permalink: taxonomy-3/taxon-3/taxon-4
                  position: 0
                  depth: 2
                  meta_title: null
                  meta_description: null
                  meta_keywords: null
                  children_count: 0
                  parent_id: ctg_gbHJdmfrXB
                  description: ''
                  description_html: ''
                  image_url: null
                  square_image_url: null
                  is_root: false
                  is_child: true
                  is_leaf: true
                meta:
                  page: 1
                  limit: 25
                  count: 3
                  pages: 1
                  from: 1
                  to: 3
                  in: 3
                  previous: null
                  next: null
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Category'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          description: unauthorized
          content:
            application/json:
              example:
                error:
                  code: invalid_token
                  message: Valid API key required
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v3/store/categories/{id}:
    get:
      summary: Get a category
      tags:
      - Product Catalog
      security:
      - api_key: []
      description: Returns a single category by permalink or prefix ID
      x-codeSamples:
      - lang: javascript
        label: Spree SDK
        source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n  baseUrl: 'https://your-store.com',\n  publishableKey: '<api-key>',\n})\n\nconst category = await client.categories.get('categories/clothing/shirts', {\n  expand: ['children'],\n})"
      parameters:
      - name: x-spree-api-key
        in: header
        required: true
        schema:
          type: string
      - name: id
        in: path
        required: true
        description: Category permalink (e.g., clothing/shirts) or prefix ID (e.g., ctg_abc123)
        schema:
          type: string
      - name: expand
        in: query
        required: false
        description: Expand associations (children, parent, ancestors, custom_fields)
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: Comma-separated list of fields to include (e.g., name,slug,price). id is always included.
        schema:
          type: string
      responses:
        '200':
          description: category found by prefix ID
          content:
            application/json:
              example:
                id: ctg_gbHJdmfrXB
                name: taxon_12
                permalink: taxonomy-9/taxon-12
                position: 0
                depth: 1
                meta_title: null
                meta_description: null
                meta_keywords: null
                children_count: 1
                parent_id: ctg_UkLWZg9DAJ
                description: ''
                description_html: ''
                image_url: null
                square_image_url: null
                is_root: false
                is_child: true
                is_leaf: false
              schema:
                $ref: '#/components/schemas/Category'
        '404':
          description: category from other store not accessible
          content:
            application/json:
              example:
                error:
                  code: record_not_found
                  message: Category not found
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: unauthorized
          content:
            application/json:
              example:
                error:
                  code: invalid_token
                  message: Valid API key required
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v3/store/products:
    get:
      summary: List products
      tags:
      - Product Catalog
      security:
      - api_key: []
      description: Returns a paginated list of active products for the current store
      x-codeSamples:
      - lang: javascript
        label: Spree SDK
        source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n  baseUrl: 'https://your-store.com',\n  publishableKey: '<api-key>',\n})\n\nconst products = await client.products.list({\n  page: 1,\n  limit: 25,\n  sort: 'price',\n  name_cont: 'shirt',\n  price_gte: 20,\n  price_lte: 100,\n  with_option_value_ids: ['optval_abc', 'optval_def'],\n  expand: ['variants', 'media'],\n})"
      parameters:
      - name: x-spree-api-key
        in: header
        required: true
        description: Publishable API key
        schema:
          type: string
      - name: page
        in: query
        required: false
        description: 'Page number (default: 1)'
        schema:
          type: integer
      - name: limit
        in: query
        required: false
        description: 'Number of items per page (default: 25, max: 100)'
        schema:
          type: integer
      - name: sort
        in: query
        required: false
        description: 'Sort order. Prefix with - for descending. Values: price, -price, best_selling, name, -name, -available_on, available_on'
        schema:
          type: string
      - name: q[name_cont]
        in: query
        required: false
        description: Filter by name containing string
        schema:
          type: string
      - name: q[in_category]
        in: query
        required: false
        description: Filter by category prefixed ID (includes descendants)
        schema:
          type: string
      - name: q[in_categories][]
        in: query
        required: false
        description: Filter by multiple category prefixed IDs (OR logic, includes descendants)
        schema:
          type: string
      - name: q[price_gte]
        in: query
        required: false
        description: Filter by minimum price
        schema:
          type: number
      - name: q[price_lte]
        in: query
        required: false
        description: Filter by maximum price
        schema:
          type: number
      - name: q[with_option_value_ids][]
        in: query
        required: false
        description: Filter by option value prefix IDs (e.g., optval_abc). Pass multiple values for OR logic.
        schema:
          type: string
      - name: q[in_stock]
        in: query
        required: false
        description: Filter to only in-stock products
        schema:
          type: boolean
      - name: expand
        in: query
        required: false
        description: Comma-separated associations to expand (variants, media, categories, option_types)
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: Comma-separated list of fields to include (e.g., name,slug,price). id is always included.
        schema:
          type: string
      responses:
        '200':
          description: products found
          content:
            application/json:
              example:
                data:
                - id: prod_UkLWZg9DAJ
                  name: Product 1421251
                  slug: product-1421251
                  meta_title: null
                  meta_description: null
                  meta_keywords: null
                  variant_count: 1
                  available_on: '2025-05-26T16:14:22.402Z'
                  purchasable: true
                  in_stock: false
                  backorderable: true
                  available: true
                  description: A comfortable cotton t-shirt.
                  description_html: <p>A <strong>comfortable</strong> cotton t-shirt.</p>
                  default_variant_id: variant_gbHJdmfrXB
                  thumbnail_url: null
                  tags: []
                  price:
                    id: price_gbHJdmfrXB
                    amount: '19.99'
                    amount_in_cents: 1999
                    compare_at_amount: null
                    compare_at_amount_in_cents: null
                    currency: USD
                    display_amount: $19.99
                    display_compare_at_amount: null
                    price_list_id: null
                  original_price: null
                - id: prod_gbHJdmfrXB
                  name: Product 1435220
                  slug: product-1435220
                  meta_title: null
                  meta_description: null
                  meta_keywords: null
                  variant_count: 0
                  available_on: '2025-05-26T16:14:22.472Z'
                  purchasable: true
                  in_stock: false
                  backorderable: true
                  available: true
                  description: Architecto dignissimos nemo inventore incidunt enim. Odit accusamus repellat error saepe culpa unde eius. Cupiditate officiis voluptatem autem perferendis qui vitae omnis sunt. Debitis dolor tempore ad impedit itaque reprehenderit delectus. Doloremque laudantium iure recusandae iusto debitis laborum consequuntur. Impedit eum optio commodi tempora cum quidem. Facere voluptatum nam possimus veritatis nostrum explicabo dolore ex. Adipisci iure unde nobis itaque amet nesciunt voluptatibus impedit. Numquam in accusantium magni itaque exercitationem. Quas vero maxime voluptatem rem impedit inventore. Esse perferendis asperiores ea expedita aut tenetur soluta. Enim accusantium dolor adipisci impedit. Error maxime neque accusamus facere provident eum. Cum officia velit asperiores quod cupiditate fugiat. Possimus tenetur aut consequuntur iusto cumque quas. Ab velit ullam qui pariatur veritatis omnis. Accusantium vero occaecati explicabo neque animi commodi. Necessitatibus perspiciatis iste culpa totam quibusdam voluptate distinctio. Quod aliquam ut et autem. Saepe ut asperiores et quisquam perspiciatis molestiae. Vel recusandae sunt nemo et accusamus veniam ducimus. Laborum modi quasi perferendis culpa laboriosam quaerat magnam. Facere at tempora iusto ex magni aliquam modi debitis.
                  description_html: 'Architecto dignissimos nemo inventore incidunt enim. Odit accusamus repellat error saepe culpa unde eius. Cupiditate officiis voluptatem autem perferendis qui vitae omnis sunt. Debitis dolor tempore ad impedit itaque reprehenderit delectus. Doloremque laudantium iure recusandae iusto debitis laborum consequuntur.

                    Impedit eum optio commodi tempora cum quidem. Facere voluptatum nam possimus veritatis nostrum explicabo dolore ex. Adipisci iure unde nobis itaque amet nesciunt voluptatibus impedit. Numquam in accusantium magni itaque exercitationem. Quas vero maxime voluptatem rem impedit inventore.

                    Esse perferendis asperiores ea expedita aut tenetur soluta. Enim accusantium dolor adipisci impedit. Error maxime neque accusamus facere provident eum. Cum officia velit asperiores quod cupiditate fugiat. Possimus tenetur aut consequuntur iusto cumque quas.

                    Ab velit ullam qui pariatur veritatis omnis. Accusantium vero occaecati explicabo neque animi commodi. Necessitatibus perspiciatis iste culpa totam quibusdam voluptate distinctio. Quod aliquam ut et autem. Saepe ut asperiores et quisquam perspiciatis molestiae.

                    Vel recusandae sunt nemo et accusamus veniam ducimus. Laborum modi quasi perferendis culpa laboriosam quaerat magnam. Facere at tempora iusto ex magni aliquam modi debitis.'
                  default_variant_id: variant_EfhxLZ9ck8
                  thumbnail_url: null
                  tags: []
                  price:
                    id: price_EfhxLZ9ck8
                    amount: '19.99'
                    amount_in_cents: 1999
                    compare_at_amount: null
                    compare_at_amount_in_cents: null
                    currency: USD
                    display_amount: $19.99
                    display_compare_at_amount: null
                    price_list_id: null
                  original_price: null
                meta:
                  page: 1
                  limit: 25
                  count: 2
                  pages: 1
                  from: 1
                  to: 2
                  in: 2
                  previous: null
                  next: null
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Product'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
                required:
                - data
                - meta
        '401':
          description: unauthorized - invalid or missing API key
          content:
            application/json:
              example:
                error:
                  code: invalid_token
                  message: Valid API key required
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v3/store/products/{id}:
    get:
      summary: Get a product
      tags:
      - Product Catalog
      security:
      - api_key: []
      description: Returns a single product by slug or prefix ID
      x-codeSamples:
      - lang: javascript
        label: Spree SDK
        source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n  baseUrl: 'https://your-store.com',\n  publishableKey: '<api-key>',\n})\n\nconst product = await client.products.get('spree-tote', {\n  expand: ['variants', 'media'],\n})"
      parameters:
      - name: x-spree-api-key
        in: header
        required: true
        description: Publishable API key
        schema:
          type: string
      - name: id
        in: path
        required: true
        description: Product slug (e.g., spree-tote) or prefix ID (e.g., product_abc123)
        schema:
          type: string
      - name: expand
        in: query
        required: false
        description: Comma-separated associations to expand
        schema:
          type: string
      - name: fields
        in: query
        required: false
        description: Comma-separated list of fields to include (e.g., name,slug,price). id is always included.
        schema:
          type: string
      responses:
        '200':
          description: product without prior_price expanded does not include field
          content:
            application/json:
              example:
                id: prod_UkLWZg9DAJ
                name: Product 1702106
                slug: product-1702106
                meta_title: null
                meta_description: null
                meta_keywords: null
                variant_count: 1
                available_on: '2025-05-26T16:14:24.749Z'
                purchasable: true
                in_stock: false
                backorderable: true
                available: true
                description: A comfortable cotton t-shirt.
                description_html: <p>A <strong>comfortable</strong> cotton t-shirt.</p>
                default_variant_id: variant_gbHJdmfrXB
                thumbnail_url: null
                tags: []
                price:
                  id: price_gbHJdmfrXB
                  amount: '19.99'
                  amount_in_cents: 1999
                  compare_at_amount: null
                  compare_at_amount_in_cents: null
                  currency: USD
                  display_amount: $19.99
                  display_compare_at_amount: null
                  price_list_id: null
                original_price: null
                prior_price:
                  id: _AXs1igzRC6
                  amount: '9.99'
                  amount_in_cents: 999
                  currency: USD
                  display_amount: $9.99
                  recorded_at: '2026-05-11T16:14:24Z'
              schema:
                $ref: '#/components/schemas/Product'
        '404':
          description: draft product not visible
          content:
            application/json:
              example:
                error:
                  code: record_not_found
                  message: Product not found
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v3/store/products/filters:
    get:
      summary: Get product filters
      tags:
      - Product Catalog
      security:
      - api_key: []
      description: 'Returns available filters for products with their options and counts.

        Use this endpoint to build filter UIs for product listing pages.


        The filters are context-aware - when a category_id is provided, only filters

        relevant to products in that category are returned.

        '
      x-codeSamples:
      - lang: javascript
        label: Spree SDK
        source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n  baseUrl: 'https://your-store.com',\n  publishableKey: '<api-key>',\n})\n\nconst filters = await client.products.filters({\n  category_id: 'ctg_abc123',\n})"
      parameters:
      - name: x-spree-api-key
        in: header
        required: true
        description: Publishable API key
        schema:
          type: string
      - name: category_id
        in: query
        required: false
        description: Scope filters to products in this category (prefix ID)
        schema:
          type: string
      - name: q[name_cont]
        in: query
        required: false
        description: Filter by name containing string
        schema:
          type: string
      responses:
        '200':
          description: filters scoped to category
          content:
            application/json:
              example:
                filters:
                - id: price
                  type: price_range
                  min: 19.99
                  max: 19.99
                  currency: USD
                - id: availability
                  type: availability
                  options:
                  - id: in_stock
                    count: 2
                  - id: out_of_stock
                    count: 0
                - id: opt_UkLWZg9DAJ
                  type: option
                  name: size
                  label: Size
                  kind: dropdown
                  options:
                  - id: optval_UkLWZg9DAJ
                    name: small
                    label: S
                    position: 1
                    color_code: null
                    image_url: null
                    count: 1
                - id: categories
                  type: category
                  options:
                  - id: ctg_EfhxLZ9ck8
                    name: Shirts
                    permalink: taxonomy-27/taxon-34/shirts
                    count: 2
                sort_options:
                - id: manual
                - id: best_selling
                - id: price
                - id: -price
                - id: -available_on
                - id: available_on
                - id: name
                - id: -name
                default_sort: manual
                total_count: 2
              schema:
                type: object
                properties:
                  filters:
                    type: array
                    description: Available filters (price_range, availability, option, category)
                    items:
                      type: object
                  sort_options:
                    type: array
                    description: Available sort options
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                      required:
                      - id
                  default_sort:
                    type: string
                    description: Default sort option ID
                  total_count:
                    type: integer
                    description: Total products matching current filters
                required:
                - filters
                - sort_options
                - default_sort
                - total_count
        '401':
          description: unauthorized - invalid or missing API key
          content:
            application/json:
              example:
                error:
                  code: invalid_token
                  message: Valid API key required
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    Price:
      type: object
      properties:
        id:
          type: string
        amount:
          type: string
          nullable: true
        amount_in_cents:
          type: number
          nullable: true
        compare_at_amount:
          type: string
          nullable: true
        compare_at_amount_in_cents:
          type: number
          nullable: true
        currency:
          type: string
          nullable: true
        display_amount:
          type: string
          nullable: true
        display_compare_at_amount:
          type: string
          nullable: true
        price_list_id:
          type: string
          nullable: true
      required:
      - id
      - amount
      - amount_in_cents
      - compare_at_amount
      - compare_at_amount_in_cents
      - currency
      - display_amount
      - display_compare_at_amount
      - price_list_id
      x-typelizer: true
    OptionValue:
      type: object
      properties:
        id:
          type: string
        option_type_id:
          type: string
        name:
          type: string
        label:
          type: string
        position:
          type: number
        color_code:
          type: string
          nullable: true
        option_type_name:
          type: string
        option_type_label:
          type: string
        image_url:
          type: string
          nullable: true
      required:
      - id
      - option_type_id
      - name
      - label
      - position
      - color_code
      - option_type_name
      - option_type_label
      - image_url
      x-typelizer: true
    PriceHistory:
      type: object
      properties:
        id:
          type: string
        amount:
          type: string
        amount_in_cents:
          type: number
        currency:
          type: string
        display_amount:
          type: string
        recorded_at:
          type: string
      required:
      - id
      - amount
      - amount_in_cents
      - currency
      - display_amount
      - recorded_at
      x-typelizer: true
    OptionType:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        label:
          type: string
        position:
          type: number
        kind:
          type: string
      required:
      - id
      - name
      - label
      - position
      - kind
      x-typelizer: true
    Category:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        permalink:
          type: string
        position:
          type: number
        depth:
          type: number
        meta_title:
          type: string
          nullable: true
        meta_description:
          type: string
          nullable: true
        meta_keywords:
          type: string
          nullable: true
        children_count:
          type: number
        parent_id:
          type: string
          nullable: true
        description:
          type: string
        description_html:
          type: string
        image_url:
          type: string
          nullable: true
        square_image_url:
          type: string
          nullable: true
        is_root:
          type: boolean
        is_child:
          type: boolean
        is_leaf:
          type: boolean
        parent:
          $ref: '#/components/schemas/Category'
        children:
          type: array
          items:
            $ref: '#/components/schemas/Category'
        ancestors:
          type: array
          items:
            $ref: '#/components/schemas/Category'
        custom_fields:
          type: array
          items:
            $ref: '#/components/schemas/CustomField'
      required:
      - id
      - name
      - permalink
      - position
      - depth
      - meta_title
      - meta_description
      - meta_keywords
      - children_count
      - parent_id
      - description
      - description_html
      - image_url
      - square_image_url
      - is_root
      - is_child
      - is_leaf
      x-typelizer: true
    PaginationMeta:
      type: object
      properties:
        page:
          type: integer
          example: 1
        limit:
          type: integer
          example: 25
        count:
          type: integer
          example: 100
          description: Total number of records
        pages:
          type: integer
          example: 4
          description: Total number of pages
        from:
          type: integer
          example: 1
          description: Index of first record on this page
        to:
          type: integer
          example: 25
          description: Index of last record on this page
        in:
          type: integer
          example: 25
          description: Number of records on this page
        previous:
          type: integer
          nullable: true
          example: null
          description: Previous page number
        next:
          type: integer
          nullable: true
          example: 2
          description: Next page number
      required:
      - page
      - limit
      - count
      - pages
      - from
      - to
      - in
    Product:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        slug:
          type: string
        meta_title:
          type: string
          nullable: true
        meta_description:
          type: string
          nullable: true
        meta_keywords:
          type: string
          nullable: true
        variant_count:
          type: number
        available_on:
          type: string
          nullable: true
        purchasable:
          type: boolean
        in_stock:
          type: boolean
        backorderable:
          type: boolean
        available:
          type: boolean
        description:
          type: string
          nullable: true
        description_html:
          type: string
          nullable: true
        default_variant_id:
          type: string
        thumbnail_url:
          type: string
          nullable: true
        tags:
          type: array
          items:
            type: string
        price:
          $ref: '#/components/schemas/Price'
        original_price:
          allOf:
          - $ref: '#/components/schemas/Price'
          nullable: true
        primary_media:
          $ref: '#/components/schemas/Media'
        media:
          type: array
          items:
            $ref: '#/components/schemas/Media'
        variants:
          type: array
          items:
            $ref: '#/components/schemas/Variant'
        default_variant:
          $ref: '#/components/schemas/Variant'
        option_types:
          type: array
          items:
            $ref: '#/components/schemas/OptionType'
        categories:
          type: array
          items:
            $ref: '#/components/schemas/Category'
        custom_fields:
          type: array
          items:
            $ref: '#/components/schemas/CustomField'
        prior_price:
          allOf:
          - $ref: '#/components/schemas/PriceHistory'
          nullable: true
      required:
      - id
      - name
      - slug
      - meta_title
      - meta_description
      - meta_keywords
      - variant_count
      - available_on
      - purchasable
      - in_stock
      - backorderable
      - available
      - description
      - description_html
      - default_variant_id
      - thumbnail_url
      - tags
      - price
      - original_price
      x-typelizer: true
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: record_not_found
        

# --- truncated at 32 KB (37 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/spree/refs/heads/main/openapi/spree-product-catalog-api-openapi.yml