# الفئات

الفئات تجمع منتجات المتجر، وهي على مستويين: الفئة الرئيسية يمكن أن تضم فئات فرعية، والفئة الفرعية لا تضم أي فئة. هذه النقاط الست تعرض الفئات وتقرأها وتنشئها وتعدّلها وتحذفها وترتّبها.

ينضم المنتج إلى فئة عبر حقله `category_id`، انظر [المنتجات](https://dzbuild.com/ar/ar/api-docs/resources/products.md). وقسم `category-products` في الصفحة الرئيسية يأخذ رقم فئة أيضاً، انظر [أقسام الصفحة الرئيسية](https://dzbuild.com/ar/ar/api-docs/resources/home-layout.md).

## قبل أن تبدأ[​](#قبل-أن-تبدأ "رابط مباشر إلى قبل أن تبدأ")

* تحتاج القراءة إلى `products:read` وتحتاج الكتابات إلى `products:write`، وهما صلاحيتا المنتجات نفسهما. مفاتيح التاجر تحمل الاثنتين.
* تحتاج `POST` و`PATCH` و`DELETE` إلى `Idempotency-Key`. انظر [Idempotency](https://dzbuild.com/ar/ar/api-docs/idempotency.md).
* صورة الفئة تُضاف من لوحة التحكم. الواجهة البرمجية تُرجع رابطها ولا تستطيع رفعها أو تغييرها.

## كائن الفئة[​](#كائن-الفئة "رابط مباشر إلى كائن الفئة")

| الحقل                | النوع          | ملاحظات                                                                                                                                                                                                                                                                           |
| -------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                 | int            | رقم الفئة.                                                                                                                                                                                                                                                                        |
| `name`               | string         | من 1 إلى 100 حرف.                                                                                                                                                                                                                                                                 |
| `slug`               | string         | يُصنع من الاسم وهو فريد في المتجر. صفحة الفئة في المتجر تستعمله في عنوانها.                                                                                                                                                                                                       |
| `description`        | string أو null | نص حر.                                                                                                                                                                                                                                                                            |
| `image`              | string أو null | الرابط الكامل للصورة، أو `null`.                                                                                                                                                                                                                                                  |
| `parent_id`          | int أو null    | الفئة الأم، أو `null` للفئة الرئيسية.                                                                                                                                                                                                                                             |
| `show_subcategories` | bool           | يعرض الفئات الفرعية كبطاقات في صفحة الفئة على المتجر (**عرض قسم الفئات الفرعية** في نموذج الفئة). يُحفظ `true` للفئة الفرعية. وما دام **الفئات الفرعية داخل الفئة الرئيسية فقط** مفعّلاً (`/dashboard/categories`)، يعرض المتجر البطاقات في صفحة كل فئة مهما كانت قيمة هذا الحقل. |
| `sort_order`         | int            | الموضع في قوائم الفئات بالمتجر، الأصغر أولاً.                                                                                                                                                                                                                                     |
| `status`             | string         | `active` أو `inactive`.                                                                                                                                                                                                                                                           |
| `created_at`         | string         | بصيغة `YYYY-MM-DD HH:MM:SS` بتوقيت الخادم.                                                                                                                                                                                                                                        |

تضيف القائمة والقراءة الفردية `product_count`، وهو عدد المنتجات في الفئة. وتضيف القراءة الفردية أيضاً `children`، أي فئاتها الفرعية.

## `GET /v1/categories`[​](#get-v1categories "رابط مباشر إلى get-v1categories")

فئات المتجر، الأحدث أولاً، في قائمة بمؤشر. رتّبها حسب `sort_order` لتحصل على ترتيب المتجر.

**المصادقة:** مفتاح منصة بصلاحية `products:read`.

### معاملات الاستعلام[​](#معاملات-الاستعلام "رابط مباشر إلى معاملات الاستعلام")

| المعامل     | النوع                  | الافتراضي | ملاحظات                                                                                                                  |
| ----------- | ---------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------ |
| `parent_id` | رقم أو `0` أو `null`   | لا شيء    | رقم فئة يُرجع فئاتها الفرعية. و`0` أو `null` أو قيمة فارغة تُرجع الفئات الرئيسية. وأي قيمة أخرى تُرجع `400 bad_request`. |
| `status`    | `active` أو `inactive` | لا شيء    | الفئات التي لها هذه الحالة فقط. وأي قيمة أخرى تُتجاهل.                                                                   |
| `limit`     | int                    | 50        | من 1 إلى 200.                                                                                                            |
| `cursor`    | string                 | لا شيء    | قيمة `next_cursor` من الصفحة السابقة. انظر [الترقيم](https://dzbuild.com/ar/ar/api-docs/pagination.md).                  |

### الطلب[​](#الطلب "رابط مباشر إلى الطلب")

```
curl 'https://api.dzbuild.app/v1/categories?parent_id=0' \

  -H "Authorization: Bearer $DZ_KEY"
```

### الاستجابة 200[​](#الاستجابة-200 "رابط مباشر إلى الاستجابة 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}`[​](#get-v1categoriesid "رابط مباشر إلى get-v1categoriesid")

فئة واحدة مع `product_count` و`children`، أي فئاتها الفرعية مرتبة حسب `sort_order`.

**المصادقة:** مفتاح منصة بصلاحية `products:read`.

إذا لم يكن رقم الفئة في المسار أرقاماً فقط، يُرجع النداء `400 bad_request`. وفئة متجر آخر تُرجع `404 not_found`، مثل فئة غير موجودة.

### الطلب[​](#الطلب-1 "رابط مباشر إلى الطلب")

```
curl 'https://api.dzbuild.app/v1/categories/10' \

  -H "Authorization: Bearer $DZ_KEY"
```

### الاستجابة 200[​](#الاستجابة-200-1 "رابط مباشر إلى الاستجابة 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`[​](#post-v1categories "رابط مباشر إلى post-v1categories")

تنشئ فئة وتضعها في الأخير: قيمة `sort_order` لها تزيد بواحد على أكبر قيمة في المتجر.

**المصادقة:** مفتاح منصة بصلاحية `products:write`. **يتطلب `Idempotency-Key`.**

### الجسم[​](#الجسم "رابط مباشر إلى الجسم")

| الحقل                | النوع                  | إلزامي | ملاحظات                                                                                                                      |
| -------------------- | ---------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `name`               | string                 | نعم    | تُحذف المسافات من الطرفين، من 1 إلى 100 حرف.                                                                                 |
| `description`        | string أو null         | لا     | تُحذف المسافات من الطرفين. النص الفارغ يُحفظ `null`.                                                                         |
| `parent_id`          | int أو null            | لا     | فئة رئيسية من هذا المتجر: تصبح الفئة الجديدة فئة فرعية لها. و`null` أو `0` تُنشئ فئة رئيسية.                                 |
| `status`             | `active` أو `inactive` | لا     | القيمة الافتراضية `active`.                                                                                                  |
| `show_subcategories` | bool                   | لا     | القيمة الافتراضية `true`. تُتجاهل وتُحفظ `true` للفئة الفرعية، أو ما دام **الفئات الفرعية داخل الفئة الرئيسية فقط** مفعّلاً. |

يُصنع الـ `slug` من الاسم: بأحرف صغيرة، مع الإبقاء على الحروف والأرقام، وتتحول كل سلسلة من الرموز الأخرى إلى `-` واحدة. الاسم العربي يحتفظ بحروفه العربية. وإذا كان لفئة أخرى في المتجر الـ `slug` نفسه، تُضاف `-2` ثم `-3` وهكذا.

### الطلب[​](#الطلب-2 "رابط مباشر إلى الطلب")

```
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}'
```

### الاستجابة 201[​](#الاستجابة-201 "رابط مباشر إلى الاستجابة 201")

الاستجابة هي كائن الفئة، دون `product_count` و`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}`[​](#patch-v1categoriesid "رابط مباشر إلى patch-v1categoriesid")

تغيّر الحقول التي ترسلها فقط. يأخذ الجسم حقول `POST` نفسها، وكلها اختيارية.

**المصادقة:** مفتاح منصة بصلاحية `products:write`. **يتطلب `Idempotency-Key`.**

* `name` جديد يعطي `slug` جديداً، فيتغير عنوان صفحة الفئة في المتجر وتتوقف الروابط إلى العنوان القديم عن العمل. وإرسال الاسم نفسه يُبقي الـ `slug`.
* `parent_id` ينقل الفئة. رقم فئة رئيسية يجعلها فئة فرعية، و`null` أو `0` يجعلها فئة رئيسية. الفئة التي لها فئات فرعية لا يمكن أن تصبح فئة فرعية، ولا يمكن أن تكون الفئة أُمّاً لنفسها.
* الجسم الفارغ لا يغيّر شيئاً ويُرجع الفئة كما هي.

### الطلب[​](#الطلب-3 "رابط مباشر إلى الطلب")

```
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"}'
```

### الاستجابة 200[​](#الاستجابة-200-2 "رابط مباشر إلى الاستجابة 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}`[​](#delete-v1categoriesid "رابط مباشر إلى delete-v1categoriesid")

تحذف فئة فارغة مع صورتها.

**المصادقة:** مفتاح منصة بصلاحية `products:write`. **يتطلب `Idempotency-Key`.**

الفئة التي ما زالت تضم منتجات أو فئات فرعية تُرجع `409 category_not_empty` ولا يُحذف شيء. رسالة الخطأ تذكر العدد. لتفريغ الفئة:

* الفئات الفرعية: اجعل كل واحدة فئة رئيسية بـ `PATCH` و`"parent_id": null`، أو احذفها أولاً.
* المنتجات: الواجهة البرمجية لا تستطيع إخراج منتج من فئة. تحديد `category_id` لمنتج يضيف فئة ويُبقي الفئات التي ينتمي إليها من قبل، انظر [المنتجات](https://dzbuild.com/ar/ar/api-docs/resources/products.md). غيّر فئات تلك المنتجات من لوحة التحكم.

### الطلب[​](#الطلب-4 "رابط مباشر إلى الطلب")

```
curl -X DELETE 'https://api.dzbuild.app/v1/categories/14' \

  -H "Authorization: Bearer $DZ_KEY" \

  -H "Idempotency-Key: cat-14-delete-1"
```

### الاستجابة 200[​](#الاستجابة-200-3 "رابط مباشر إلى الاستجابة 200")

```
{

  "data": {

    "deleted": true,

    "id": 14

  }

}
```

## `POST /v1/categories/reorder`[​](#post-v1categoriesreorder "رابط مباشر إلى post-v1categoriesreorder")

تضبط `sort_order` للفئات التي تذكرها دفعة واحدة: إما أن تتغير كلها أو لا يتغير أي منها.

**المصادقة:** مفتاح منصة بصلاحية `products:write`. **يتطلب `Idempotency-Key`.**

### الجسم[​](#الجسم-1 "رابط مباشر إلى الجسم")

| الحقل        | النوع | إلزامي | ملاحظات                                                                                                |
| ------------ | ----- | ------ | ------------------------------------------------------------------------------------------------------ |
| `categories` | array | نعم    | الفئات بترتيبها الجديد. كل عنصر هو `{"id": 10}` أو `{"id": 10, "sort_order": 7}` أو رقم وحده مثل `10`. |

* العنصر الذي ليس فيه `sort_order` يأخذ موضعه في المصفوفة: 1 ثم 2 ثم 3 وهكذا. والعنصر الذي فيه `sort_order` يأخذ ذلك الرقم.
* كل رقم يجب أن يكون من المتجر وأن يظهر مرة واحدة.
* الفئات التي لا تذكرها تحتفظ بقيمة `sort_order` الخاصة بها.
* إعادة الترتيب لا تُسجَّل في سجل التغييرات، فلا يمكن التراجع عنها. اقرأ القائمة أولاً إن كنت قد تحتاج إلى الترتيب القديم.

### الطلب[​](#الطلب-5 "رابط مباشر إلى الطلب")

```
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}]}'
```

### الاستجابة 200[​](#الاستجابة-200-4 "رابط مباشر إلى الاستجابة 200")

```
{

  "data": {

    "reordered": 2,

    "categories": [

      {"id": 14, "sort_order": 1},

      {"id": 10, "sort_order": 2}

    ]

  }

}
```

## التراجع[​](#التراجع "رابط مباشر إلى التراجع")

تُسجَّل `POST` و`PATCH` و`DELETE` في سجل تغييرات المتجر. استجاباتها لا تحمل رقم التغيير: `GET /v1/changes?entity=category` تعرض تغييرات الفئات، الأحدث أولاً، بصلاحية `store:read`. والحقل `entity_id` هو رقم الفئة.

### الطلب[​](#الطلب-6 "رابط مباشر إلى الطلب")

```
curl 'https://api.dzbuild.app/v1/changes?entity=category&limit=1' \

  -H "Authorization: Bearer $DZ_KEY"
```

### الاستجابة 200[​](#الاستجابة-200-5 "رابط مباشر إلى الاستجابة 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` عن تغيير واحد. ويحتاج إلى `products:write` و`Idempotency-Key`.

* التراجع عن `PATCH` يُرجع القيم السابقة للحقول التي غيّرها ذلك النداء، مع فحوص `PATCH` نفسها.
* التراجع عن `DELETE` يُنشئ الفئة من جديد برقمها القديم، دون صورتها. وإذا حُذفت فئتها الأم القديمة أو صارت فئة فرعية، يُرجع التراجع `422 invalid_parent`.
* لا يمكن التراجع عن الإنشاء: يُرجع التراجع `422 nothing_to_restore`. احذف الفئة بدلاً من ذلك.
* إذا لم تعد الفئة موجودة، أو أُخذ رقمها القديم، يُرجع التراجع `422 restore_target_missing`.
* التغيير الذي يُتراجع عنه مرة ثانية يُرجع `409 already_undone`.
* التغييرات المحفوظة من لوحة التحكم لا تُسجَّل، فلا يمكن التراجع عنها عبر الواجهة البرمجية.
* لا يستطيع رمز التطبيق المثبّت قراءة التغييرات ولا التراجع عنها: كلاهما يُرجع `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

  }

}
```

## الأخطاء[​](#الأخطاء "رابط مباشر إلى الأخطاء")

| HTTP | الرمز                    | السبب                                                                                                                                                                                                                                                                                        |
| ---- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400  | `bad_request`            | الرقم في المسار ليس أرقاماً فقط، أو الجسم ليس JSON صالحاً، أو مرشّح `parent_id` ليس رقماً ولا `0` ولا `null` ولا قيمة فارغة، أو `categories` غائب أو ليس مصفوفة، أو `Idempotency-Key` غائب أو غير صالح.                                                                                      |
| 401  | `unauthorized`           | مفتاح خاطئ أو غائب.                                                                                                                                                                                                                                                                          |
| 402  | `quota_exceeded`         | انتهت حصة الطلبات الشهرية للمتجر. انظر [حدود المعدل](https://dzbuild.com/ar/ar/api-docs/rate-limits.md).                                                                                                                                                                                     |
| 403  | `forbidden`              | `Missing scope: products:read` أو `Missing scope: products:write` أو `Missing scope: store:read`، أو `API access requires an active Enterprise plan` لمفتاح تاجر متجره ليس على خطة Enterprise سارية، أو `Apps cannot use this endpoint` عندما يقرأ رمز تطبيق مثبّت التغييرات أو يتراجع عنها. |
| 404  | `not_found`              | لا توجد فئة بهذا الرقم في المتجر، أو تذكر إعادة الترتيب رقماً ليس في المتجر، أو يذكر التراجع تغييراً ليس في سجل تغييرات المتجر.                                                                                                                                                              |
| 409  | `category_not_empty`     | الفئة ما زالت تضم منتجات أو فئات فرعية.                                                                                                                                                                                                                                                      |
| 409  | `already_undone`         | للتراجع فقط: سبق التراجع عن هذا التغيير.                                                                                                                                                                                                                                                     |
| 413  | `payload_too_large`      | الجسم أكبر من 1 ميغابايت.                                                                                                                                                                                                                                                                    |
| 422  | `validation_error`       | `name` غائب أو فارغ أو أطول من 100 حرف، أو `status` ليست `active` ولا `inactive`، أو `parent_id` ليس رقماً، أو إعادة الترتيب فارغة أو تكرر رقماً أو فيها رقم ليس عدداً صحيحاً موجباً أو `sort_order` ليس عدداً صحيحاً.                                                                       |
| 422  | `invalid_parent`         | `parent_id` ليس فئة من هذا المتجر، أو هو نفسه فئة فرعية، أو هو الفئة نفسها، أو للفئة فئات فرعية فلا يمكن نقلها تحت فئة أخرى.                                                                                                                                                                 |
| 422  | `nothing_to_restore`     | للتراجع فقط: التغيير أنشأ الفئة.                                                                                                                                                                                                                                                             |
| 422  | `restore_target_missing` | للتراجع فقط: الفئة لم تعد موجودة، أو أُخذ رقمها القديم.                                                                                                                                                                                                                                      |
| 422  | `idempotency_key_reuse`  | استُعمل `Idempotency-Key` نفسه مع جسم آخر.                                                                                                                                                                                                                                                   |
| 429  | `rate_limited`           | نداءات كثيرة في الدقيقة الحالية. انتظر الثواني المذكورة في `Retry-After`. انظر [حدود المعدل](https://dzbuild.com/ar/ar/api-docs/rate-limits.md).                                                                                                                                             |
| 500  | `server_error`           | فشل الطلب. أعد المحاولة بالـ `Idempotency-Key` نفسه.                                                                                                                                                                                                                                         |
