> ## Documentation Index
> Fetch the complete documentation index at: https://codebyahmed.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Admin Products API — Create and Manage Products

> Create, update, list, and soft-delete products in the catalog. Admin-only endpoint with support for search, brand filter, and deleted item visibility.

The Admin Products API gives you full control over the product catalog. Use it to create new products, update existing ones, search and filter across all catalog entries — including soft-deleted items — and perform soft-deletes that preserve order history without exposing discontinued items to customers. All write operations require an active admin session and a CSRF token.

<Note>
  All endpoints under `/admin/products` require an authenticated session with the `ADMIN` or `SUPER_ADMIN` role. Always call `GET /auth/csrf-token` first and pass the returned token in the `x-csrf-token` header for every POST, PATCH, and DELETE request.
</Note>

***

## List Products

Retrieve a paginated list of products from the admin catalog view. Unlike the public catalog endpoint, this view lets you filter by deleted status, search across all products regardless of active variant availability, and sort by any supported field.

```
GET /admin/products
```

### Query Parameters

<ParamField query="page" type="integer" default="1">
  Page number (1-based).
</ParamField>

<ParamField query="limit" type="integer" default="20">
  Number of results per page. Accepted range: 1–100.
</ParamField>

<ParamField query="search" type="string">
  Free-text search across product name, brand, and description.
</ParamField>

<ParamField query="brand" type="string">
  Filter by exact brand name (trimmed). Maximum 255 characters.
</ParamField>

<ParamField query="sort" type="string" default="-created_at">
  Sort field. Prefix with `-` for descending order. Accepted values: `name`, `-name`, `created_at`, `-created_at`, `updated_at`, `-updated_at`.
</ParamField>

<ParamField query="include_deleted" type="string" default="false">
  Pass `"true"` to include soft-deleted products in the response. Must be the string `"true"` or `"false"`.
</ParamField>

<Tip>
  Pass `include_deleted=true` when auditing removed catalog items — for example, when reconciling order history against products that are no longer actively sold.
</Tip>

### Response

Returns a paginated envelope with a `data` array of product summaries and a `meta` pagination object.

<ResponseField name="success" type="boolean">
  Always `true` on a 200 response.
</ResponseField>

<ResponseField name="data" type="array">
  Array of product summary objects.

  <Expandable title="Product summary fields">
    <ResponseField name="public_id" type="string">
      Unique product identifier, prefixed `prd_`.
    </ResponseField>

    <ResponseField name="name" type="string">
      Product display name.
    </ResponseField>

    <ResponseField name="slug" type="string">
      URL-safe slug. Pattern: `^[a-z0-9]+(?:-[a-z0-9]+)*$`.
    </ResponseField>

    <ResponseField name="brand" type="string | null">
      Brand name, or `null` if not set.
    </ResponseField>

    <ResponseField name="description" type="string | null">
      Product description, or `null` if not set.
    </ResponseField>

    <ResponseField name="deleted_at" type="string | null">
      ISO 8601 UTC timestamp of soft-deletion, or `null` if the product is active.
    </ResponseField>

    <ResponseField name="created_at" type="string">
      ISO 8601 UTC creation timestamp.
    </ResponseField>

    <ResponseField name="updated_at" type="string">
      ISO 8601 UTC last-updated timestamp.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta" type="object">
  Pagination metadata.

  <Expandable title="Pagination fields">
    <ResponseField name="total" type="integer">Total matching records.</ResponseField>
    <ResponseField name="totalPages" type="integer">Total number of pages.</ResponseField>
    <ResponseField name="hasNext" type="boolean">Whether a next page exists.</ResponseField>
    <ResponseField name="hasPrev" type="boolean">Whether a previous page exists.</ResponseField>
  </Expandable>
</ResponseField>

***

## Create Product

Create a new product in the catalog. Only `name` is required; provide a `slug` to control the URL path or let the API auto-generate one from the name. A 409 is returned if the slug already exists.

```
POST /admin/products
```

### Request Body

<ParamField body="name" type="string" required>
  Product display name. Between 1 and 255 characters.
</ParamField>

<ParamField body="slug" type="string">
  URL slug for the product. Must match `^[a-z0-9]+(?:-[a-z0-9]+)*$` and be unique. Auto-generated from `name` if omitted. Maximum 255 characters.
</ParamField>

<ParamField body="description" type="string">
  Long-form product description. Maximum 10,000 characters. Optional.
</ParamField>

<ParamField body="brand" type="string">
  Brand name. Maximum 255 characters. Optional.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  # Step 1: obtain a CSRF token
  TOKEN=$(curl -s -X GET https://api.example.com/api/v1/auth/csrf-token \
    -H "Cookie: session=<your-session-cookie>" \
    | jq -r '.data.csrf_token')

  # Step 2: create the product
  curl -X POST https://api.example.com/api/v1/admin/products \
    -H "Content-Type: application/json" \
    -H "Cookie: session=<your-session-cookie>" \
    -H "x-csrf-token: $TOKEN" \
    -d '{
      "name": "Runner Sneaker",
      "slug": "runner-sneaker",
      "description": "Lightweight daily trainer built for speed and comfort.",
      "brand": "Nike"
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.example.com/api/v1/admin/products', {
    method: 'POST',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
      'x-csrf-token': csrfToken,
    },
    body: JSON.stringify({
      name: 'Runner Sneaker',
      slug: 'runner-sneaker',
      description: 'Lightweight daily trainer built for speed and comfort.',
      brand: 'Nike',
    }),
  });
  const data = await response.json();
  ```
</CodeGroup>

### 201 Response

```json theme={null}
{
  "success": true,
  "data": {
    "public_id": "prd_01H",
    "name": "Runner Sneaker",
    "slug": "runner-sneaker",
    "description": "Lightweight daily trainer built for speed and comfort.",
    "brand": "Nike",
    "images": [],
    "variants": [],
    "deleted_at": null,
    "created_at": "2024-06-01T09:00:00.000Z",
    "updated_at": "2024-06-01T09:00:00.000Z"
  }
}
```

### Error Responses

| Status | Meaning                                                           |
| ------ | ----------------------------------------------------------------- |
| `400`  | Validation error — invalid slug format or missing required field. |
| `401`  | No active session.                                                |
| `403`  | Session does not have ADMIN or SUPER\_ADMIN role.                 |
| `409`  | A product with the given slug already exists.                     |

***

## Get Product

Fetch the full admin projection of a product by its public ID. Pass `include_deleted_variants=true` to include any soft-deleted variants in the response.

```
GET /admin/products/{product_public_id}
```

### Path Parameters

<ParamField path="product_public_id" type="string" required>
  The product's public ID, prefixed `prd_`.
</ParamField>

### Query Parameters

<ParamField query="include_deleted_variants" type="string" default="false">
  Pass `"true"` to include soft-deleted variants in the variants array. Must be the string `"true"` or `"false"`.
</ParamField>

### Response

Returns the full `ProductDetail` object including `images` (ordered by `display_order`) and `variants` (all statuses when `include_deleted_variants=true`).

***

## Update Product

Partially update a product's fields. Only the fields you supply are modified. Pass `null` for `description` or `brand` to explicitly clear those values.

```
PATCH /admin/products/{product_public_id}
```

### Path Parameters

<ParamField path="product_public_id" type="string" required>
  The product's public ID, prefixed `prd_`.
</ParamField>

### Request Body

At least one field is required.

<ParamField body="name" type="string">
  Updated display name. Between 1 and 255 characters.
</ParamField>

<ParamField body="slug" type="string">
  Updated URL slug. Must match `^[a-z0-9]+(?:-[a-z0-9]+)*$`. Returns 409 on conflict.
</ParamField>

<ParamField body="description" type="string | null">
  Updated description. Pass `null` to clear the existing value.
</ParamField>

<ParamField body="brand" type="string | null">
  Updated brand. Pass `null` to clear the existing value.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://api.example.com/api/v1/admin/products/prd_01H \
    -H "Content-Type: application/json" \
    -H "Cookie: session=<your-session-cookie>" \
    -H "x-csrf-token: $TOKEN" \
    -d '{
      "name": "Runner Sneaker Pro",
      "brand": null
    }'
  ```

  ```javascript Node.js theme={null}
  await fetch('https://api.example.com/api/v1/admin/products/prd_01H', {
    method: 'PATCH',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
      'x-csrf-token': csrfToken,
    },
    body: JSON.stringify({ name: 'Runner Sneaker Pro', brand: null }),
  });
  ```
</CodeGroup>

***

## Delete Product

Soft-delete a product and all of its variants. The product is immediately hidden from all customer-facing endpoints but remains in the database with `deleted_at` set. Returns `204 No Content` on success.

```
DELETE /admin/products/{product_public_id}
```

### Path Parameters

<ParamField path="product_public_id" type="string" required>
  The product's public ID, prefixed `prd_`.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE https://api.example.com/api/v1/admin/products/prd_01H \
    -H "Cookie: session=<your-session-cookie>" \
    -H "x-csrf-token: $TOKEN"
  ```

  ```javascript Node.js theme={null}
  await fetch('https://api.example.com/api/v1/admin/products/prd_01H', {
    method: 'DELETE',
    credentials: 'include',
    headers: { 'x-csrf-token': csrfToken },
  });
  ```
</CodeGroup>

<Note>
  Soft-deleted products — and all their variants — are hidden from customers but are permanently retained in the database. This ensures that historical order line items continue to reference the correct product name, slug, and brand without data loss.
</Note>

### Error Responses

| Status | Meaning            |
| ------ | ------------------ |
| `401`  | No active session. |
| `403`  | Insufficient role. |
| `404`  | Product not found. |
