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, variants, and option types
paths:
/api/v3/admin/option_types:
get:
summary: List option types
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Returns a paginated list of option types.
**Required scope:** `read_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { data: optionTypes } = await client.optionTypes.list()"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
description: Bearer token for admin authentication
schema:
type: string
- name: page
in: query
required: false
description: Page number
schema:
type: integer
- name: limit
in: query
required: false
description: Number of records per page
schema:
type: integer
- name: q[name_cont]
in: query
required: false
description: Filter by name (contains)
schema:
type: string
- name: expand
in: query
required: false
description: Comma-separated associations to expand (e.g., option_values). Use dot notation for nested expand (max 4 levels).
schema:
type: string
- name: fields
in: query
required: false
description: Comma-separated list of fields to include (e.g., name,label,position). id is always included.
schema:
type: string
responses:
'200':
description: option types found
content:
application/json:
example:
data:
- id: opt_UkLWZg9DAJ
name: foo-size-1
label: Size
position: 1
kind: dropdown
metadata: {}
filterable: true
created_at: '2026-05-24T17:37:03.133Z'
updated_at: '2026-05-24T17:37:03.133Z'
meta:
page: 1
limit: 25
count: 1
pages: 1
from: 1
to: 1
in: 1
previous: null
next: null
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/OptionType'
meta:
$ref: '#/components/schemas/PaginationMeta'
required:
- data
- meta
'401':
description: unauthorized
content:
application/json:
example:
error:
code: authentication_required
message: Authentication required
schema:
$ref: '#/components/schemas/ErrorResponse'
post:
summary: Create an option type
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Creates a new option type. Supports nested option values.
Option values can be provided inline and will be created or updated by name.
**Required scope:** `write_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst optionType = await client.optionTypes.create({\n name: 'color',\n presentation: 'Color',\n option_values: [\n { name: 'red', presentation: 'Red' },\n { name: 'navy', presentation: 'Navy' },\n ],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
description: Bearer token for admin authentication
schema:
type: string
responses:
'201':
description: option type created
content:
application/json:
example:
id: opt_gbHJdmfrXB
name: material
label: Material
position: 2
kind: dropdown
metadata: {}
filterable: true
created_at: '2026-05-24T17:37:03.722Z'
updated_at: '2026-05-24T17:37:03.722Z'
schema:
$ref: '#/components/schemas/OptionType'
'422':
description: validation error
content:
application/json:
example:
error:
code: validation_error
message: Name can't be blank and Presentation can't be blank
details:
name:
- can't be blank
presentation:
- can't be blank
schema:
$ref: '#/components/schemas/ErrorResponse'
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
example: color
label:
type: string
example: Color
position:
type: integer
example: 1
filterable:
type: boolean
example: true
option_values:
type: array
items:
type: object
properties:
name:
type: string
example: red
label:
type: string
example: Red
position:
type: integer
required:
- name
- label
/api/v3/admin/option_types/{id}:
get:
summary: Get an option type
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Returns a single option type by ID, including its option values.
**Required scope:** `read_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst optionType = await client.optionTypes.get('ot_UkLWZg9DAJ')"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
description: Bearer token for admin authentication
schema:
type: string
- name: id
in: path
required: true
description: Option type ID
schema:
type: string
- name: expand
in: query
required: false
description: Comma-separated associations to expand (e.g., option_values). Use dot notation for nested expand (max 4 levels).
schema:
type: string
- name: fields
in: query
required: false
description: Comma-separated list of fields to include (e.g., name,label,position). id is always included.
schema:
type: string
responses:
'200':
description: option type found
content:
application/json:
example:
id: opt_UkLWZg9DAJ
name: foo-size-5
label: Size
position: 1
kind: dropdown
metadata: {}
filterable: true
created_at: '2026-05-24T17:37:04.014Z'
updated_at: '2026-05-24T17:37:04.014Z'
schema:
$ref: '#/components/schemas/OptionType'
'404':
description: option type not found
content:
application/json:
example:
error:
code: record_not_found
message: Option type not found
schema:
$ref: '#/components/schemas/ErrorResponse'
patch:
summary: Update an option type
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Updates an option type. Supports updating nested option values.
**Required scope:** `write_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst optionType = await client.optionTypes.update('ot_UkLWZg9DAJ', {\n presentation: 'Updated Presentation',\n option_values: [\n { name: 'red', presentation: 'Crimson' },\n ],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
description: Bearer token for admin authentication
schema:
type: string
- name: id
in: path
required: true
description: Option type ID
schema:
type: string
responses:
'200':
description: option type updated
content:
application/json:
example:
id: opt_UkLWZg9DAJ
name: foo-size-7
label: Updated Label
position: 1
kind: dropdown
metadata: {}
filterable: true
created_at: '2026-05-24T17:37:04.617Z'
updated_at: '2026-05-24T17:37:04.942Z'
schema:
$ref: '#/components/schemas/OptionType'
'422':
description: validation error
content:
application/json:
example:
error:
code: validation_error
message: Presentation can't be blank
details:
presentation:
- can't be blank
schema:
$ref: '#/components/schemas/ErrorResponse'
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
example: color
label:
type: string
example: Color
position:
type: integer
example: 1
filterable:
type: boolean
example: true
option_values:
type: array
items:
type: object
properties:
id:
type: string
description: Existing option value ID to update
name:
type: string
example: red
label:
type: string
example: Red
position:
type: integer
delete:
summary: Delete an option type
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Deletes an option type.
**Required scope:** `write_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nawait client.optionTypes.delete('ot_UkLWZg9DAJ')"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
description: Bearer token for admin authentication
schema:
type: string
- name: id
in: path
required: true
description: Option type ID
schema:
type: string
responses:
'204':
description: option type deleted
/api/v3/admin/products:
get:
summary: List products
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Returns a paginated list of products for the current store.
**Required scope:** `read_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { data: products } = await client.products.list({\n name_cont: 'shirt',\n status_eq: 'active',\n sort: '-created_at',\n limit: 25,\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
description: Bearer token for admin authentication
schema:
type: string
- name: page
in: query
required: false
description: Page number
schema:
type: integer
- name: limit
in: query
required: false
description: Number of records per page
schema:
type: integer
- name: sort
in: query
required: false
description: Sort field (e.g., name, -name, price, -price, best_selling)
schema:
type: string
- name: expand
in: query
required: false
description: Comma-separated associations to expand (e.g., variants, media, option_types, categories). Use dot notation for nested expand (max 4 levels).
schema:
type: string
- name: fields
in: query
required: false
description: Comma-separated list of fields to include (e.g., name,slug,price,status). id is always included.
schema:
type: string
- name: q[name_cont]
in: query
required: false
description: Filter by name (contains)
schema:
type: string
- name: q[status_eq]
in: query
required: false
description: Filter by status
schema:
type: string
responses:
'200':
description: products found
content:
application/json:
example:
data:
- id: prod_UkLWZg9DAJ
name: Product 396394
slug: product-396394
meta_title: null
meta_description: null
meta_keywords: null
variant_count: 0
available_on: '2025-05-24T17:37:34.067Z'
purchasable: true
in_stock: false
backorderable: true
available: true
description: Eaque soluta sit officiis sequi. Deleniti perspiciatis repudiandae ratione quod illum enim. Tenetur tempora similique soluta corporis magni ea itaque dolore. Harum aliquid tempore facilis amet deleniti facere. Nisi ducimus nihil a optio veniam totam. Libero aut deleniti laudantium aliquam reprehenderit. Nam neque assumenda a nisi iusto aliquid perferendis. Quas tenetur non consequatur assumenda. Quos explicabo error culpa aspernatur quod earum cupiditate omnis. Eaque id iste saepe ratione repellendus. Porro cum ducimus eaque occaecati natus cupiditate itaque. Quisquam esse dolore distinctio labore. Minima excepturi fugiat quibusdam voluptatem.
description_html: 'Eaque soluta sit officiis sequi. Deleniti perspiciatis repudiandae ratione quod illum enim. Tenetur tempora similique soluta corporis magni ea itaque dolore. Harum aliquid tempore facilis amet deleniti facere.
Nisi ducimus nihil a optio veniam totam. Libero aut deleniti laudantium aliquam reprehenderit. Nam neque assumenda a nisi iusto aliquid perferendis. Quas tenetur non consequatur assumenda. Quos explicabo error culpa aspernatur quod earum cupiditate omnis.
Eaque id iste saepe ratione repellendus. Porro cum ducimus eaque occaecati natus cupiditate itaque. Quisquam esse dolore distinctio labore. Minima excepturi fugiat quibusdam voluptatem.'
default_variant_id: variant_UkLWZg9DAJ
thumbnail_url: null
tags: []
price:
id: price_UkLWZg9DAJ
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
variant_id: variant_UkLWZg9DAJ
created_at: '2026-05-24T17:37:34.092Z'
updated_at: '2026-05-24T17:37:34.092Z'
original_price: null
status: active
make_active_at: '2025-05-24T17:37:34.067Z'
discontinue_on: null
metadata: {}
deleted_at: null
created_at: '2026-05-24T17:37:34.079Z'
updated_at: '2026-05-24T17:37:34.093Z'
tax_category_id: taxcat_UkLWZg9DAJ
meta:
page: 1
limit: 25
count: 1
pages: 1
from: 1
to: 1
in: 1
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
content:
application/json:
example:
error:
code: authentication_required
message: Authentication required
schema:
$ref: '#/components/schemas/ErrorResponse'
post:
summary: Create a product
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Creates a new product. Supports nested variants with prices and option types.
Option types and values are auto-created if they don''t exist.
Prices are upserted by currency. Stock items are upserted by stock location.
**Required scope:** `write_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst product = await client.products.create({\n name: 'Premium T-Shirt',\n description: 'Soft, organic cotton.',\n status: 'active',\n variants: [\n {\n sku: 'TSHIRT-S-NAVY',\n options: [\n { name: 'size', value: 'Small' },\n { name: 'color', value: 'navy' },\n ],\n prices: [\n { currency: 'USD', amount: 29.99 },\n { currency: 'EUR', amount: 27.99 },\n ],\n stock_items: [\n { stock_location_id: 'sloc_UkLWZg9DAJ', count_on_hand: 50 },\n ],\n },\n {\n sku: 'TSHIRT-M-NAVY',\n options: [\n { name: 'size', value: 'Medium' },\n { name: 'color', value: 'navy' },\n ],\n prices: [{ currency: 'USD', amount: 29.99 }],\n stock_items: [\n { stock_location_id: 'sloc_UkLWZg9DAJ', count_on_hand: 30 },\n ],\n },\n ],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
description: Bearer token for admin authentication
schema:
type: string
responses:
'201':
description: product created
content:
application/json:
example:
id: prod_gbHJdmfrXB
name: New Product
slug: new-product
meta_title: null
meta_description: null
meta_keywords: null
variant_count: 0
available_on: null
purchasable: false
in_stock: false
backorderable: false
available: false
description: null
description_html: null
default_variant_id: variant_gbHJdmfrXB
thumbnail_url: null
tags: []
price: null
original_price: null
status: draft
make_active_at: null
discontinue_on: null
metadata: {}
deleted_at: null
created_at: '2026-05-24T17:37:34.756Z'
updated_at: '2026-05-24T17:37:34.758Z'
tax_category_id: null
schema:
$ref: '#/components/schemas/Product'
'422':
description: validation error
content:
application/json:
example:
error:
code: validation_error
message: Name can't be blank
details:
name:
- can't be blank
schema:
$ref: '#/components/schemas/ErrorResponse'
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
example: Premium T-Shirt
description:
type: string
slug:
type: string
status:
type: string
enum:
- draft
- active
- archived
tax_category_id:
type: string
description: Tax category ID
category_ids:
type: array
items:
type: string
description: Array of category IDs
tags:
type: array
items:
type: string
example:
- eco
- sale
variants:
type: array
description: Array of variant payloads. Variants can declare multiple option pairs via `options:` and per-currency prices via `prices:`. Stock counts go in `stock_items:` (per stock location).
items:
type: object
properties:
sku:
type: string
options:
type: array
description: One pair per option type the variant participates in (e.g. size + color). Option types and values are auto-created if missing.
items:
type: object
required:
- name
- value
properties:
name:
type: string
example: size
value:
type: string
example: Small
prices:
type: array
description: Per-currency prices. Upserted by currency.
items:
type: object
required:
- currency
- amount
properties:
currency:
type: string
example: USD
amount:
type: number
example: 29.99
compare_at_amount:
type: number
example: 39.99
stock_items:
type: array
description: Per-stock-location inventory. Upserted by stock_location_id.
items:
type: object
required:
- stock_location_id
- count_on_hand
properties:
stock_location_id:
type: string
description: Stock location ID (e.g. sloc_xxx)
count_on_hand:
type: integer
example: 50
backorderable:
type: boolean
required:
- name
/api/v3/admin/products/{id}:
get:
summary: Get a product
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Returns a single product by ID.
**Required scope:** `read_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst product = await client.products.get('prod_86Rf07xd4z', {\n expand: ['variants', 'option_types'],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
description: Bearer token for admin authentication
schema:
type: string
- name: id
in: path
required: true
description: Product ID (e.g., prod_xxx)
schema:
type: string
- name: expand
in: query
required: false
description: Comma-separated associations to expand (e.g., variants, media, option_types, categories). Use dot notation for nested expand (max 4 levels).
schema:
type: string
- name: fields
in: query
required: false
description: Comma-separated list of fields to include (e.g., name,slug,price,status). id is always included.
schema:
type: string
responses:
'200':
description: product found
content:
application/json:
example:
id: prod_UkLWZg9DAJ
name: Product 431256
slug: product-431256
meta_title: null
meta_description: null
meta_keywords: null
variant_count: 0
available_on: '2025-05-24T17:37:35.110Z'
purchasable: true
in_stock: false
backorderable: true
available: true
description: Nemo quam et hic libero natus qui. Saepe repudiandae eaque autem provident omnis at quia. Porro fugiat voluptatibus vero dolore beatae minus id. Molestias est illo unde quis. Velit quis at voluptatem ut quam mollitia saepe nam. Eligendi laudantium quis aspernatur ea incidunt culpa. Cupiditate sequi reiciendis aspernatur maiores. Tempore blanditiis sint libero odit veniam.
description_html: 'Nemo quam et hic libero natus qui. Saepe repudiandae eaque autem provident omnis at quia. Porro fugiat voluptatibus vero dolore beatae minus id. Molestias est illo unde quis.
Velit quis at voluptatem ut quam mollitia saepe nam. Eligendi laudantium quis aspernatur ea incidunt culpa. Cupiditate sequi reiciendis aspernatur maiores. Tempore blanditiis sint libero odit veniam.'
default_variant_id: variant_UkLWZg9DAJ
thumbnail_url: null
tags: []
price:
id: price_UkLWZg9DAJ
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
variant_id: variant_UkLWZg9DAJ
created_at: '2026-05-24T17:37:35.137Z'
updated_at: '2026-05-24T17:37:35.137Z'
original_price: null
status: active
make_active_at: '2025-05-24T17:37:35.110Z'
discontinue_on: null
metadata: {}
deleted_at: null
created_at: '2026-05-24T17:37:35.121Z'
updated_at: '2026-05-24T17:37:35.138Z'
tax_category_id: taxcat_UkLWZg9DAJ
schema:
$ref: '#/components/schemas/Product'
'404':
description: product not found
content:
application/json:
example:
error:
code: record_not_found
message: Product not found
schema:
$ref: '#/components/schemas/ErrorResponse'
patch:
summary: Update a product
tags:
- Product Catalog
security:
- api_key: []
bearer_auth: []
description: 'Updates a product. Only provided fields are updated.
**Required scope:** `write_products` (for API-key a
# --- truncated at 32 KB (124 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/spree-commerce/refs/heads/main/openapi/spree-commerce-product-catalog-api-openapi.yml