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:readand writes needproducts:write, the same scopes as products. Merchant keys carry both.POST,PATCHandDELETEneed anIdempotency-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
Field | Type | Notes |
| int | The category id. |
| string | 1 to 100 characters. |
| string | Made from the name and unique in the store. The category's page on the store uses it in its address. |
| string or null | Free text. |
| string or null | Full URL of the image, or |
| int or null | The parent category, or |
| bool | Shows the subcategories as tiles on the category's page in the store (Show subcategories section on the category form). Stored as |
| int | Position in the store's category lists, lowest first. |
| string |
|
| string |
|
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
Param | Type | Default | Notes |
| id, | none | A category id returns its subcategories. |
|
| none | Only the categories with this status. Any other value is ignored. |
| int | 50 | 1 to 200. |
| string | none |
|
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
Field | Type | Required | Notes |
| string | yes | Trimmed, 1 to 100 characters. |
| string or null | no | Trimmed. An empty string is stored as |
| int or null | no | A top-level category of this store: the new category becomes its subcategory. |
|
| no | Defaults to |
| bool | no | Defaults to |
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
namegives a newslug, 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 theslug.parent_idmoves the category. A top-level category id makes it a subcategory, andnullor0makes 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
PATCHand"parent_id": null, or delete it first.Products: the API cannot take a product out of a category. Setting a product's
category_idadds 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
Field | Type | Required | Notes |
| array | yes | The categories in their new order. Each item is |
An item without
sort_ordergets its position in the array: 1, 2, 3 and so on. An item withsort_ordergets 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
PATCHputs back the earlier values of the fields that call changed, with the same checks asPATCH.Undoing a
DELETEcreates the category again under its old id, without its image. When its old parent is gone or has become a subcategory, the undo answers422 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
HTTP | Code | Cause |
400 |
| The id in the path is not all digits, the body is not valid JSON, the |
401 |
| Bad or missing key. |
402 |
| The store's monthly request quota is used up. See Rate limits. |
403 |
|
|
404 |
| No 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. |
409 |
| The category still holds products or subcategories. |
409 |
| Undo only: the change was already undone. |
413 |
| The body is over 1 MB. |
422 |
|
|
422 |
|
|
422 |
| Undo only: the change created the category. |
422 |
| Undo only: the category is gone, or its old id is taken. |
422 |
| The same |
429 |
| Too many calls in the current minute. Wait for the |
500 |
| The request failed. Retry with the same |