Skip to main content

Categories

Categories group the products of a store. They are two levels deep: a top-level category can hold subcategories, and a subcategory cannot hold any. These six endpoints list, read, create, change, delete and reorder them.

A product joins a category through its category_id field, see Products. A category-products section of the home page takes a category id too, see Home page sections.

Before you start​

  • Reads need products:read and writes need products:write, the same scopes as products. Merchant keys carry both.
  • POST, PATCH and DELETE need an Idempotency-Key. See Idempotency.
  • The category image is added in the dashboard. The API returns its URL but cannot upload or change it.

The category object​

FieldTypeNotes
idintThe category id.
namestring1 to 100 characters.
slugstringMade from the name and unique in the store. The category's page on the store uses it in its address.
descriptionstring or nullFree text.
imagestring or nullFull URL of the image, or null.
parent_idint or nullThe parent category, or null for a top-level category.
show_subcategoriesboolShows the subcategories as tiles on the category's page in the store (Show subcategories section on the category form). Stored as true for a subcategory. While Subcategories only inside the parent category is on (/dashboard/categories), the store shows the tiles on every category's page whatever this field says.
sort_orderintPosition in the store's category lists, lowest first.
statusstringactive or inactive.
created_atstringYYYY-MM-DD HH:MM:SS, server time.

The list and the single read add product_count, the number of products in the category. The single read also adds children, its subcategories.

GET /v1/categories​

The store's categories, newest first, as a cursor list. Sort them by sort_order to get the store's order.

Auth: platform key with products:read.

Query parameters​

ParamTypeDefaultNotes
parent_idid, 0 or nullnoneA category id returns its subcategories. 0, null or an empty value returns the top-level categories. Anything else returns 400 bad_request.
statusactive or inactivenoneOnly the categories with this status. Any other value is ignored.
limitint501 to 200.
cursorstringnonenext_cursor of the previous page. See Pagination.

Request​

curl 'https://api.dzbuild.app/v1/categories?parent_id=0' \
-H "Authorization: Bearer $DZ_KEY"

Response 200​

{
"data": {
"items": [
{
"id": 14,
"name": "Montres",
"slug": "montres",
"description": null,
"image": null,
"parent_id": null,
"show_subcategories": true,
"sort_order": 4,
"status": "active",
"created_at": "2026-09-30 11:20:05",
"product_count": 0
},
{
"id": 10,
"name": "Parfums",
"slug": "parfums",
"description": "Eaux de parfum et coffrets",
"image": null,
"parent_id": null,
"show_subcategories": true,
"sort_order": 1,
"status": "active",
"created_at": "2026-09-12 09:41:37",
"product_count": 18
}
],
"next_cursor": null,
"has_more": false
}
}

GET /v1/categories/{id}​

One category with product_count and children, its subcategories ordered by sort_order.

Auth: platform key with products:read.

An id that is not all digits answers 400 bad_request. A category of another store answers 404 not_found, like one that does not exist.

Request​

curl 'https://api.dzbuild.app/v1/categories/10' \
-H "Authorization: Bearer $DZ_KEY"

Response 200​

{
"data": {
"id": 10,
"name": "Parfums",
"slug": "parfums",
"description": "Eaux de parfum et coffrets",
"image": null,
"parent_id": null,
"show_subcategories": true,
"sort_order": 1,
"status": "active",
"created_at": "2026-09-12 09:41:37",
"children": [
{"id": 11, "name": "Parfums femme", "slug": "parfums-femme", "sort_order": 2, "status": "active"},
{"id": 12, "name": "Parfums homme", "slug": "parfums-homme", "sort_order": 3, "status": "active"}
],
"product_count": 18
}
}

POST /v1/categories​

Creates a category and places it last: its sort_order is one more than the highest in the store.

Auth: platform key with products:write. Requires Idempotency-Key.

Body​

FieldTypeRequiredNotes
namestringyesTrimmed, 1 to 100 characters.
descriptionstring or nullnoTrimmed. An empty string is stored as null.
parent_idint or nullnoA top-level category of this store: the new category becomes its subcategory. null or 0 makes a top-level category.
statusactive or inactivenoDefaults to active.
show_subcategoriesboolnoDefaults to true. Ignored, and stored as true, for a subcategory or while Subcategories only inside the parent category is on.

The slug is made from the name: lowercase, letters and digits kept, and any run of other characters turned into one -. An Arabic name keeps its Arabic letters. When another category of the store already has that slug, -2, -3 and so on is added.

Request​

curl -X POST 'https://api.dzbuild.app/v1/categories' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cat-create-coffrets-1" \
-d '{"name": "Coffrets cadeaux", "parent_id": 10}'

Response 201​

The answer is the category object, without product_count and children.

{
"data": {
"id": 15,
"name": "Coffrets cadeaux",
"slug": "coffrets-cadeaux",
"description": null,
"image": null,
"parent_id": 10,
"show_subcategories": true,
"sort_order": 5,
"status": "active",
"created_at": "2026-10-06 14:02:11"
}
}

PATCH /v1/categories/{id}​

Changes only the fields you send. The body takes the fields of POST, all optional.

Auth: platform key with products:write. Requires Idempotency-Key.

  • A new name gives a new slug, so the address of the category's page on the store changes and links to the old address stop working. Sending the same name keeps the slug.
  • parent_id moves the category. A top-level category id makes it a subcategory, and null or 0 makes it top-level. A category that has subcategories cannot become a subcategory, and a category cannot be its own parent.
  • An empty body changes nothing and answers the category as it is.

Request​

curl -X PATCH 'https://api.dzbuild.app/v1/categories/15' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cat-15-move-1" \
-d '{"parent_id": null, "status": "inactive"}'

Response 200​

{
"data": {
"id": 15,
"name": "Coffrets cadeaux",
"slug": "coffrets-cadeaux",
"description": null,
"image": null,
"parent_id": null,
"show_subcategories": true,
"sort_order": 5,
"status": "inactive",
"created_at": "2026-10-06 14:02:11"
}
}

DELETE /v1/categories/{id}​

Deletes an empty category and its image.

Auth: platform key with products:write. Requires Idempotency-Key.

A category that still holds products or subcategories answers 409 category_not_empty, and nothing is deleted. The error message gives the counts. To empty the category:

  • Subcategories: move each one to the top level with PATCH and "parent_id": null, or delete it first.
  • Products: the API cannot take a product out of a category. Setting a product's category_id adds a category and keeps the ones the product already has, see Products. Change the categories of those products in the dashboard.

Request​

curl -X DELETE 'https://api.dzbuild.app/v1/categories/14' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: cat-14-delete-1"

Response 200​

{
"data": {
"deleted": true,
"id": 14
}
}

POST /v1/categories/reorder​

Sets the sort_order of the categories you list, all in one step: either every one of them changes or none does.

Auth: platform key with products:write. Requires Idempotency-Key.

Body​

FieldTypeRequiredNotes
categoriesarrayyesThe categories in their new order. Each item is {"id": 10}, {"id": 10, "sort_order": 7} or a bare id such as 10.
  • An item without sort_order gets its position in the array: 1, 2, 3 and so on. An item with sort_order gets that number.
  • Each id must belong to the store and appear once.
  • The categories you leave out keep their sort_order.
  • A reorder is not recorded in the change history, so it cannot be undone. Read the list first if you may want the old order back.

Request​

curl -X POST 'https://api.dzbuild.app/v1/categories/reorder' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cat-reorder-1" \
-d '{"categories": [{"id": 14}, {"id": 10}]}'

Response 200​

{
"data": {
"reordered": 2,
"categories": [
{"id": 14, "sort_order": 1},
{"id": 10, "sort_order": 2}
]
}
}

Undo​

POST, PATCH and DELETE are recorded in the store's change history. Their answer does not carry the change id: GET /v1/changes?entity=category lists the category changes, newest first, with store:read. entity_id is the category id.

Request​

curl 'https://api.dzbuild.app/v1/changes?entity=category&limit=1' \
-H "Authorization: Bearer $DZ_KEY"

Response 200​

{
"data": {
"items": [
{
"id": 120,
"entity": "category",
"entity_id": "15",
"action": "update",
"summary": "Updated category #15 (parent_id, status, show_subcategories)",
"undone_at": null,
"created_at": "2026-10-06 14:05:48",
"undone": false
}
],
"next_cursor": "MTIw",
"has_more": true
}
}

POST /v1/changes/{id}/undo reverts one change. It needs products:write and an Idempotency-Key.

  • Undoing a PATCH puts back the earlier values of the fields that call changed, with the same checks as PATCH.
  • Undoing a DELETE creates the category again under its old id, without its image. When its old parent is gone or has become a subcategory, the undo answers 422 invalid_parent.
  • A create cannot be undone: the undo answers 422 nothing_to_restore. Delete the category instead.
  • When the category is gone, or its old id is taken, the undo answers 422 restore_target_missing.
  • A change undone a second time answers 409 already_undone.
  • Changes made in the dashboard are not recorded, so they cannot be undone through the API.
  • An installed app's token cannot read or undo changes: both answer 403 forbidden (Apps cannot use this endpoint).
curl -X POST 'https://api.dzbuild.app/v1/changes/120/undo' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: undo-120"
{
"data": {
"undone": true,
"change_id": 120,
"entity": "category",
"undo_change_id": 121
}
}

Errors​

HTTPCodeCause
400bad_requestThe id in the path is not all digits, the body is not valid JSON, the parent_id filter is not an id, 0, null or empty, categories is missing or not an array, or Idempotency-Key is missing or malformed.
401unauthorizedBad or missing key.
402quota_exceededThe store's monthly request quota is used up. See Rate limits.
403forbiddenMissing scope: products:read, Missing scope: products:write or Missing scope: store:read, or API access requires an active Enterprise plan for a merchant key whose store is not on an active Enterprise plan, or Apps cannot use this endpoint when an installed app's token reads or undoes changes.
404not_foundNo category with this id in the store, a reorder lists an id that is not in the store, or an undo names a change that is not in the store's history.
409category_not_emptyThe category still holds products or subcategories.
409already_undoneUndo only: the change was already undone.
413payload_too_largeThe body is over 1 MB.
422validation_errorname is missing, empty or over 100 characters, status is not active or inactive, parent_id is not an id, or a reorder is empty, repeats an id, has an id that is not a positive whole number or a sort_order that is not a whole number.
422invalid_parentparent_id is not a category of this store, is itself a subcategory, is the category itself, or the category has subcategories and cannot move under another one.
422nothing_to_restoreUndo only: the change created the category.
422restore_target_missingUndo only: the category is gone, or its old id is taken.
422idempotency_key_reuseThe same Idempotency-Key was used with a different body.
429rate_limitedToo many calls in the current minute. Wait for the Retry-After seconds. See Rate limits.
500server_errorThe request failed. Retry with the same Idempotency-Key.
This page for AI toolsView as MarkdownOpen in ChatGPTOpen in Claude