Categories require product scopes. Reads use read_products; writes use write_products. Categories support parent_id, name, slug, description, seo_title, seo_description, image_id, and template_handle fields. slug is optional on create and generated from the name when omitted; on update, a supplied slug must be non-empty and valid. parent_id builds the category hierarchy. Assigning a non-empty image_id also requires read_files for the referenced upload image. On create, omitting template_handle assigns default_category. Use default_category for the default Category template, or pass an alternate Category template handle configured for the store. Omit template_handle on update to leave the current assignment unchanged; an empty string is invalid. AI translation is not exposed through the Store API.
Endpoints#
Category limits
Fetch Category limits and per-field constraints.
Returns the per-field maximum lengths, hierarchy depth, and request caps that apply to Category endpoints. Useful for clients to validate inputs before sending them and to size UIs.
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
List categories
List categories for the store.
Returns paginated categories. Use tree=true for hierarchical roots with children. Pagination, sorting, tree limits, and search validation are enforced by the product service.
Query parameters
| Name | Type | Description |
|---|---|---|
page
|
integer |
Page number. Defaults to 1. |
limit
|
integer |
Page size. Defaults to 50. Maximum 200. |
tree
|
boolean |
When true and no search or filters are active, return paginated root categories with nested children. Search or filter parameters return the flat paginated list. Accepted values: true, false. |
search
|
string |
Search categories by name or slug. Maximum 200 characters. |
parent_id
|
string |
|
ancestor_id
|
integer |
Filter to descendants of this ancestor category id. Must be a positive integer. |
has_children
|
boolean |
Filter categories by whether they have subcategories. Accepted values: true, false. |
has_images
|
boolean |
Filter categories by whether they have an image. Accepted values: true, false. |
sort_by
|
string |
Sort field. Allowed values: name, product_count, created_at, updated_at.
Allowed values
nameproduct_countcreated_atupdated_at |
sort_direction
|
string |
Sort direction. Allowed values: asc, desc.
Allowed values
ascdesc |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Errors
-
400CATEGORY_LIST_INVALID_PAGEpage must be at least 1.
-
400CATEGORY_LIST_INVALID_LIMITlimit must be within the documented list range.
-
400CATEGORY_SEARCH_TOO_LONGsearch exceeds the configured maximum length.
-
400CATEGORY_LIST_INVALID_SORT_BYsort_by is not one of the documented fields.
-
400CATEGORY_LIST_INVALID_SORT_DIRECTIONsort_direction is not asc or desc.
-
400CATEGORY_LIST_INVALID_PARENT_IDparent_id is neither null nor a positive integer.
-
400CATEGORY_LIST_INVALID_ANCESTOR_IDancestor_id is not a positive integer.
-
400CATEGORY_LIST_INVALID_BOOLEANA boolean list filter is not true or false.
Search categories
Search categories for picker-style suggestions.
Returns lightweight category suggestions by query. This endpoint is intended for pickers and simple selection UIs.
Query parameters
| Name | Type | Description |
|---|---|---|
q
|
string |
Search query. Maximum 200 characters. |
limit
|
integer |
Suggestion count. Defaults to 10. Maximum 50. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Errors
-
400CATEGORY_LIST_INVALID_LIMITlimit must be within the documented suggestion range.
-
400CATEGORY_SEARCH_TOO_LONGq exceeds the configured maximum length.
Get a category
Fetch one category by id.
Returns the category if it belongs to the token's store.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
integer |
Category id. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Errors
-
400STORE_API_INVALID_CATEGORY_IDid is not an integer.
-
404CATEGORY_NOT_FOUNDCategory does not exist in this store.
Create a category
Create a category.
Creates a category. name is required; optional text fields, hierarchy assignment, image assignment, and template assignment follow the Category resource contract. If image_id is non-empty, it must point to an upload image in the same store and the token must also have read_files.
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
name
required
|
string |
Category name. |
slug
|
string |
Optional slug. Omit to generate from name. |
parent_id
|
integer |
Optional parent category id. Omit or send 0 for a root category. |
description
|
string |
Optional plain-text description. |
seo_title
|
string |
Optional SEO title. |
seo_description
|
string |
Optional SEO description. |
image_id
|
string |
Optional upload image file id. Non-empty values require |
template_handle
|
string |
Template assignment. On create, omitting this field or sending an empty string assigns |
Errors
-
400STORE_API_INVALID_REQUEST_BODYRequest body is not valid JSON.
-
400STORE_API_CREATE_REDIRECT_UNSUPPORTEDcreate_redirect is only supported on update.
-
400CATEGORY_NAME_REQUIREDName is empty after normalization.
-
400CATEGORY_NAME_INVALID_CHARSName contains forbidden control characters.
-
400CATEGORY_NAME_TOO_LONGName exceeds the Category contract max length.
-
400CATEGORY_SLUG_INVALID_FORMATSlug is not valid for Category URLs.
-
400CATEGORY_SLUG_TOO_LONGSlug exceeds the Category contract max length.
-
400CATEGORY_SLUG_GENERATION_FAILEDA slug could not be generated from the supplied name.
-
400CATEGORY_UNIQUE_SLUG_GENERATION_FAILEDA unique generated slug could not be allocated.
-
400CATEGORY_DESCRIPTION_INVALID_CHARSDescription contains forbidden control characters.
-
400CATEGORY_DESCRIPTION_TOO_LONGDescription exceeds the Category contract max length.
-
400CATEGORY_SEO_TITLE_INVALID_CHARSSEO title contains forbidden control characters.
-
400CATEGORY_SEO_TITLE_TOO_LONGSEO title exceeds the Category contract max length.
-
400CATEGORY_SEO_DESCRIPTION_INVALID_CHARSSEO description contains forbidden control characters.
-
400CATEGORY_SEO_DESCRIPTION_TOO_LONGSEO description exceeds the Category contract max length.
-
400CATEGORY_DEPTH_EXCEEDEDParent assignment would exceed the category hierarchy depth limit.
-
400CATEGORY_CANNOT_BE_OWN_PARENTA category cannot be its own parent.
-
400CATEGORY_CANNOT_BE_DESCENDANT_PARENTParent assignment would create a cycle.
-
400CATEGORY_PARENT_NOT_FOUNDParent category does not exist in this store.
-
400CATEGORY_LIMIT_REACHEDStore category cap has been reached.
-
400TEMPLATE_HANDLE_INVALIDTemplate handle is not valid for Category templates.
-
400TEMPLATE_HANDLE_NOT_FOUNDTemplate handle does not exist on the store's primary active theme for Category.
-
400INVALID_INPUTimage_id is malformed, is not an image, or is not a merchant upload.
-
403STORE_ADMIN_TOKEN_RUNTIME_FORBIDDENA non-empty image_id requires read_files scope in addition to write_products.
-
404NOT_FOUNDimage_id does not reference a file in this store.
-
404CATEGORY_NOT_FOUNDCategory does not exist in this store.
-
409CATEGORY_DUPLICATEName already exists under the same parent, or slug already exists in this store.
-
409TRANSLATION_SOURCE_FIELD_HAS_TRANSLATIONSA source field cannot be cleared while translations still exist.
Bulk create categories
Create multiple categories by name.
Creates multiple root categories from names. Slugs are generated server-side; existing names are skipped and reported in the response.
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
names
required
|
array<string> |
Category names to create. |
Errors
-
400STORE_API_INVALID_REQUEST_BODYRequest body is not valid JSON.
-
400CATEGORY_BULK_NO_NAMESNo names were provided.
-
400CATEGORY_LIMIT_REACHEDBatch would exceed the Category store cap.
-
400CATEGORY_NAME_INVALID_CHARSOne name contains forbidden control characters.
-
400CATEGORY_NAME_TOO_LONGOne name exceeds the Category contract max length.
-
400CATEGORY_SLUG_GENERATION_FAILEDA slug could not be generated from one name.
-
400CATEGORY_UNIQUE_SLUG_GENERATION_FAILEDA unique generated slug could not be allocated.
-
409CATEGORY_BULK_DUPLICATEOne of the names in the batch was just created by another request.
Update a category
Update a category.
Updates provided Category fields. Omitted fields are left unchanged. Sending parent_id: 0 moves the category to the root. Sending an empty image_id clears the image; sending a non-empty image_id requires read_files and the referenced file must be an upload image in the same store. Sending create_redirect can create a slug redirect when the slug changes.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
integer |
Category id. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
name
|
string |
Category name. |
slug
|
string |
Optional slug. Omit to generate from name. |
parent_id
|
integer |
Optional parent category id. Omit or send 0 for a root category. |
description
|
string |
Optional plain-text description. |
seo_title
|
string |
Optional SEO title. |
seo_description
|
string |
Optional SEO description. |
image_id
|
string |
Optional upload image file id. Non-empty values require |
template_handle
|
string |
Template assignment. On create, omitting this field or sending an empty string assigns |
create_redirect
|
boolean |
When slug changes, create a redirect from the previous slug. |
Errors
-
400STORE_API_INVALID_CATEGORY_IDid is not an integer.
-
400STORE_API_INVALID_REQUEST_BODYRequest body is not valid JSON.
-
400CATEGORY_NAME_REQUIREDName is empty after normalization.
-
400CATEGORY_NAME_INVALID_CHARSName contains forbidden control characters.
-
400CATEGORY_NAME_TOO_LONGName exceeds the Category contract max length.
-
400CATEGORY_SLUG_INVALID_FORMATSlug is not valid for Category URLs.
-
400CATEGORY_SLUG_TOO_LONGSlug exceeds the Category contract max length.
-
400CATEGORY_SLUG_GENERATION_FAILEDA slug could not be generated from the supplied name.
-
400CATEGORY_UNIQUE_SLUG_GENERATION_FAILEDA unique generated slug could not be allocated.
-
400CATEGORY_DESCRIPTION_INVALID_CHARSDescription contains forbidden control characters.
-
400CATEGORY_DESCRIPTION_TOO_LONGDescription exceeds the Category contract max length.
-
400CATEGORY_SEO_TITLE_INVALID_CHARSSEO title contains forbidden control characters.
-
400CATEGORY_SEO_TITLE_TOO_LONGSEO title exceeds the Category contract max length.
-
400CATEGORY_SEO_DESCRIPTION_INVALID_CHARSSEO description contains forbidden control characters.
-
400CATEGORY_SEO_DESCRIPTION_TOO_LONGSEO description exceeds the Category contract max length.
-
400CATEGORY_DEPTH_EXCEEDEDParent assignment would exceed the category hierarchy depth limit.
-
400CATEGORY_CANNOT_BE_OWN_PARENTA category cannot be its own parent.
-
400CATEGORY_CANNOT_BE_DESCENDANT_PARENTParent assignment would create a cycle.
-
400CATEGORY_PARENT_NOT_FOUNDParent category does not exist in this store.
-
400CATEGORY_LIMIT_REACHEDStore category cap has been reached.
-
400TEMPLATE_HANDLE_INVALIDTemplate handle is not valid for Category templates.
-
400TEMPLATE_HANDLE_NOT_FOUNDTemplate handle does not exist on the store's primary active theme for Category.
-
400INVALID_INPUTimage_id is malformed, is not an image, or is not a merchant upload.
-
403STORE_ADMIN_TOKEN_RUNTIME_FORBIDDENA non-empty image_id requires read_files scope in addition to write_products.
-
404NOT_FOUNDimage_id does not reference a file in this store.
-
404CATEGORY_NOT_FOUNDCategory does not exist in this store.
-
409CATEGORY_DUPLICATEName already exists under the same parent, or slug already exists in this store.
-
409TRANSLATION_SOURCE_FIELD_HAS_TRANSLATIONSA source field cannot be cleared while translations still exist.
Delete a category
Delete a category.
Deletes a category. Subcategories and assigned products block deletion. If collection rules, discounts, or menu items require confirmation, retry with the returned confirmation token.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
integer |
Category id. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
confirmed
|
boolean |
On a confirmation retry, set this to true and send the returned confirmation_token. Omit it on the first request. |
confirmation_token
|
string |
Token from the CONFIRMATION_REQUIRED response. Omit it on the first request and resend the exact same id selection when using it. |
Errors
-
400STORE_API_INVALID_CATEGORY_IDid is not an integer.
-
400STORE_API_INVALID_REQUEST_BODYRequest body is not valid JSON.
-
400CATEGORY_CONFIRMATION_INVALIDConfirmation token is missing, expired, or mismatched.
-
400CATEGORY_BULK_DELETE_TOO_MANYBatch exceeds the Category bulk delete cap.
-
404CATEGORY_NOT_FOUNDCategory does not exist in this store.
-
409CONFIRMATION_REQUIREDDelete requires confirmation because dependent records will be changed.
-
409CATEGORY_HAS_PRODUCTSCategory has assigned products and cannot be deleted.
-
409CATEGORY_HAS_SUBCATEGORIESCategory has subcategories and cannot be deleted.
Bulk delete categories
Delete multiple categories.
Send store-scoped integer category ids in category_ids. Categories with subcategories or assigned products are not deleted and appear in failed. If deleting the remaining categories will remove collection rules, discount references, or menu items, the API returns CONFIRMATION_REQUIRED; retry the same ids with confirmed: true and its confirmation_token. The response lists successful ids in deleted. All database changes for those ids commit together.
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
category_ids
required
|
array<integer> |
Store-scoped integer category ids to delete. |
confirmed
|
boolean |
On a confirmation retry, set this to true and send the returned confirmation_token. Omit it on the first request. |
confirmation_token
|
string |
Token from the CONFIRMATION_REQUIRED response. Omit it on the first request and resend the exact same id selection when using it. |
Errors
-
400STORE_API_INVALID_REQUEST_BODYRequest body is not valid JSON.
-
400CATEGORY_BULK_NO_IDSNo category_ids were provided.
-
409CATEGORY_BULK_RACEDA category changed during deletion; retry from fresh data.
-
400CATEGORY_CONFIRMATION_INVALIDConfirmation token is missing, expired, or mismatched.
-
400CATEGORY_BULK_DELETE_TOO_MANYBatch exceeds the Category bulk delete cap.
-
404CATEGORY_NOT_FOUNDCategory does not exist in this store.
-
409CONFIRMATION_REQUIREDDelete requires confirmation because dependent records will be changed.
-
409CATEGORY_HAS_PRODUCTSCategory has assigned products and cannot be deleted.
-
409CATEGORY_HAS_SUBCATEGORIESCategory has subcategories and cannot be deleted.
Fields#
Field |
Kind |
Create |
Update |
Required |
Translatable |
|---|---|---|---|---|---|
|
text |
Yes |
Yes |
create |
Yes |
|
slug |
Yes |
Yes |
Yes |
|
|
integer |
Yes |
Yes |
No |
|
|
textarea |
Yes |
Yes |
Yes |
|
|
text |
Yes |
Yes |
Yes |
|
|
textarea |
Yes |
Yes |
Yes |
|
|
image |
Yes |
Yes |
No |
|
|
template |
Yes |
Yes |
No |
|
|
boolean |
No |
Yes |
No |
|
|
text |
No |
No |
No |
|
|
boolean |
No |
No |
No |
|
|
integer |
No |
No |
No |
|
|
datetime |
No |
No |
No |
|
|
datetime |
No |
No |
No |
|
|
url |
No |
No |
No |
No |
Webhook events#
Category webhook events are declared by the Category resource contract. The event cards below include the topic, subscription scope, store permission, payload schema, and sample payload.
Category created
Fired when a product category is created.
Payload fields
| Field | Type | Description |
|---|---|---|
category.id |
integer |
Always present |
category.parent_id |
integer |
Can be removed/added via the Payload builder |
category.name |
string |
Can be removed/added via the Payload builder |
category.slug |
string |
Can be removed/added via the Payload builder |
category.description |
string |
Can be removed/added via the Payload builder |
category.seo_title |
string |
Can be removed/added via the Payload builder |
category.seo_description |
string |
Can be removed/added via the Payload builder |
category.image_id |
string |
Can be removed/added via the Payload builder |
category.image_url |
string |
Can be removed/added via the Payload builder |
category.image_cdn_url |
string |
Can be removed/added via the Payload builder |
category.product_count |
integer |
Can be removed/added via the Payload builder |
category.template_handle |
string |
Can be removed/added via the Payload builder |
category.parent_name |
string |
Can be removed/added via the Payload builder |
category.has_children |
boolean |
Can be removed/added via the Payload builder |
category.created_at |
string |
Can be removed/added via the Payload builder |
category.updated_at |
string |
Can be removed/added via the Payload builder |
Category updated
Fired when a product category is updated.
Payload fields
| Field | Type | Description |
|---|---|---|
category.id |
integer |
Always present |
category.parent_id |
integer |
Can be removed/added via the Payload builder |
category.name |
string |
Can be removed/added via the Payload builder |
category.slug |
string |
Can be removed/added via the Payload builder |
category.description |
string |
Can be removed/added via the Payload builder |
category.seo_title |
string |
Can be removed/added via the Payload builder |
category.seo_description |
string |
Can be removed/added via the Payload builder |
category.image_id |
string |
Can be removed/added via the Payload builder |
category.image_url |
string |
Can be removed/added via the Payload builder |
category.image_cdn_url |
string |
Can be removed/added via the Payload builder |
category.product_count |
integer |
Can be removed/added via the Payload builder |
category.template_handle |
string |
Can be removed/added via the Payload builder |
category.parent_name |
string |
Can be removed/added via the Payload builder |
category.has_children |
boolean |
Can be removed/added via the Payload builder |
category.created_at |
string |
Can be removed/added via the Payload builder |
category.updated_at |
string |
Can be removed/added via the Payload builder |
Category deleted
Fired when a product category is deleted.
Payload fields
| Field | Type | Description |
|---|---|---|
category.id |
integer |
Always present |
category.name |
string |
Can be removed/added via the Payload builder |
category.slug |
string |
Can be removed/added via the Payload builder |