> ## 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 Images API — Attach Product and Variant Images

> Attach, reorder, and delete images for products and variants. Exactly one primary image is maintained per product. Upload images to ImageKit first, then register the URL here.

The Admin Images API manages the visual media for your catalog. Because the platform uses ImageKit as its CDN, your workflow is always the same: first upload the image directly to ImageKit using a signed authentication token from the API, then register the resulting URL against a product or variant. The API controls display ordering, primary image promotion, and clean-up on deletion so you never need to manage those constraints manually.

<Note>
  All image endpoints require an authenticated session with the `ADMIN` or `SUPER_ADMIN` role. Include the `x-csrf-token` header on all POST, PATCH, and DELETE requests.
</Note>

***

## ImageKit Upload Workflow

Before attaching any image, you must upload the file to ImageKit and obtain a hosted URL. The API provides a dedicated endpoint that issues short-lived signed credentials for direct browser or server-side uploads.

<Steps>
  <Step title="Get signed ImageKit credentials">
    Call `GET /admin/products/uploads/imagekit-auth` to retrieve a set of signed upload parameters. No query parameters are needed — the folder is fixed server-side for admin uploads.

    ```bash theme={null}
    curl -X GET https://api.example.com/api/v1/admin/products/uploads/imagekit-auth \
      -H "Cookie: session=<your-session-cookie>"
    ```

    **Response:**

    ```json theme={null}
    {
      "success": true,
      "data": {
        "token": "ik_upload_token_abc123",
        "expire": 1717228800,
        "signature": "sha1_signature_string",
        "publicKey": "public_ik_key",
        "urlEndpoint": "https://ik.imagekit.io/yourstore",
        "folder": "/products"
      }
    }
    ```
  </Step>

  <Step title="Upload the image to ImageKit">
    Use the ImageKit SDK or a direct multipart form POST to upload the file to ImageKit using the credentials from the previous step. ImageKit returns a hosted URL for your uploaded file.

    ```javascript theme={null}
    import ImageKit from 'imagekit';

    const imagekit = new ImageKit({
      publicKey: data.publicKey,
      urlEndpoint: data.urlEndpoint,
    });

    const uploadResult = await imagekit.upload({
      file: fileBuffer,           // Buffer or base64 string
      fileName: 'runner-sneaker-front.jpg',
      folder: data.folder,
      token: data.token,
      expire: data.expire,
      signature: data.signature,
    });

    const hostedUrl = uploadResult.url;
    // e.g. "https://ik.imagekit.io/yourstore/products/runner-sneaker-front.jpg"
    ```
  </Step>

  <Step title="Register the URL against a product or variant">
    Once you have the hosted ImageKit URL, call `POST /admin/products/{product_public_id}/images` (or the variant images endpoint) to attach it to the catalog record.

    ```bash theme={null}
    curl -X POST \
      https://api.example.com/api/v1/admin/products/prd_01H/images \
      -H "Content-Type: application/json" \
      -H "Cookie: session=<your-session-cookie>" \
      -H "x-csrf-token: $TOKEN" \
      -d '{
        "image_url": "https://ik.imagekit.io/yourstore/products/runner-sneaker-front.jpg",
        "alt_text": "Runner Sneaker – front view",
        "display_order": 0,
        "is_primary": true
      }'
    ```
  </Step>
</Steps>

<Tip>
  `display_order` controls the sort position in the product gallery. Lower values appear first. Set `display_order: 0` for the hero image and increment subsequent images by 10 to leave room for future inserts without a full re-order.
</Tip>

***

## Product Images

### List Product Images

Retrieve a paginated list of images attached to a product, ordered by `display_order` ascending.

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

#### Path Parameters

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

#### Query Parameters

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

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

#### Response

<ResponseField name="data" type="array">
  Ordered array of product image objects.

  <Expandable title="Product image fields">
    <ResponseField name="public_id" type="string">Image public ID, prefixed `pimg_`.</ResponseField>
    <ResponseField name="image_url" type="string">Fully qualified ImageKit URL.</ResponseField>
    <ResponseField name="alt_text" type="string | null">Accessibility alt text, or `null`.</ResponseField>
    <ResponseField name="display_order" type="integer">Zero-based sort index within the product gallery.</ResponseField>
    <ResponseField name="is_primary" type="boolean">Whether this image is the product's primary (thumbnail) image.</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>

***

### Attach Product Image

Register a hosted ImageKit URL as a product image. `image_url` is the only required field. The API validates the URL host against an allowlist and enforces the one-primary constraint.

```
POST /admin/products/{product_public_id}/images
```

#### Path Parameters

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

#### Request Body

<ParamField body="image_url" type="string" required>
  Fully qualified `https://` URL to an image hosted on ImageKit. Maximum 2048 characters.
</ParamField>

<ParamField body="alt_text" type="string">
  Descriptive alt text for accessibility. Maximum 255 characters. Optional.
</ParamField>

<ParamField body="display_order" type="integer">
  Zero-based gallery sort position. Defaults to appending at the end. Must be ≥ 0.
</ParamField>

<ParamField body="is_primary" type="boolean">
  If `true`, this image becomes the product's primary image and any existing primary is demoted. If this is the first image attached to the product, it is automatically set as primary regardless of this value.
</ParamField>

<Note>
  Every product maintains exactly one primary image at all times. Setting `is_primary: true` on a new or existing image atomically promotes it and demotes the previously primary image. You cannot have zero primary images on a product that has at least one image attached.
</Note>

#### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    https://api.example.com/api/v1/admin/products/prd_01H/images \
    -H "Content-Type: application/json" \
    -H "Cookie: session=<your-session-cookie>" \
    -H "x-csrf-token: $TOKEN" \
    -d '{
      "image_url": "https://ik.imagekit.io/yourstore/products/runner-front.jpg",
      "alt_text": "Runner Sneaker front view",
      "display_order": 0,
      "is_primary": true
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    'https://api.example.com/api/v1/admin/products/prd_01H/images',
    {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
        'x-csrf-token': csrfToken,
      },
      body: JSON.stringify({
        image_url: 'https://ik.imagekit.io/yourstore/products/runner-front.jpg',
        alt_text: 'Runner Sneaker front view',
        display_order: 0,
        is_primary: true,
      }),
    }
  );
  ```
</CodeGroup>

#### 201 Response

```json theme={null}
{
  "success": true,
  "data": {
    "public_id": "pimg_01H",
    "image_url": "https://ik.imagekit.io/yourstore/products/runner-front.jpg",
    "alt_text": "Runner Sneaker front view",
    "display_order": 0,
    "is_primary": true,
    "created_at": "2024-06-01T09:10:00.000Z",
    "updated_at": "2024-06-01T09:10:00.000Z"
  }
}
```

***

### Get Product Image

Fetch a single product image by its public ID.

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

#### Path Parameters

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

<ParamField path="image_public_id" type="string" required>
  The image's public ID, prefixed `pimg_`.
</ParamField>

***

### Update Product Image

Partially update a product image. Supply any combination of `image_url`, `alt_text`, `display_order`, and `is_primary`. At least one field is required.

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

#### Path Parameters

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

<ParamField path="image_public_id" type="string" required>
  The image's public ID, prefixed `pimg_`.
</ParamField>

#### Request Body

<ParamField body="image_url" type="string">
  Replacement ImageKit URL. Maximum 2048 characters.
</ParamField>

<ParamField body="alt_text" type="string | null">
  Updated alt text. Pass `null` to clear.
</ParamField>

<ParamField body="display_order" type="integer">
  Updated sort position. Must be ≥ 0.
</ParamField>

<ParamField body="is_primary" type="boolean">
  Promote this image to primary. The current primary is automatically demoted.
</ParamField>

***

### Delete Product Image

Delete a product image. If the deleted image was the primary, the API automatically promotes the next-lowest `display_order` image to primary so the constraint is never violated. Returns `204 No Content` on success.

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

#### Path Parameters

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

<ParamField path="image_public_id" type="string" required>
  The image's public ID, prefixed `pimg_`.
</ParamField>

#### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE \
    https://api.example.com/api/v1/admin/products/prd_01H/images/pimg_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/images/pimg_01H',
    {
      method: 'DELETE',
      credentials: 'include',
      headers: { 'x-csrf-token': csrfToken },
    }
  );
  ```
</CodeGroup>

***

## Variant Images

Variant images show color- or configuration-specific photos. They follow the same upload workflow as product images but have no `is_primary` field — the full set of a variant's images is displayed in order.

### List Variant Images

Retrieve paginated images for a specific variant.

```
GET /admin/products/{product_public_id}/variants/{variant_public_id}/images
```

#### Path Parameters

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

<ParamField path="variant_public_id" type="string" required>
  The variant's public ID, prefixed `var_`.
</ParamField>

#### Query Parameters

<ParamField query="page" type="integer" default="1">Page number.</ParamField>
<ParamField query="limit" type="integer" default="20">Items per page. Accepted range: 1–100.</ParamField>

***

### Attach Variant Image

Register a hosted ImageKit URL as a variant image.

```
POST /admin/products/{product_public_id}/variants/{variant_public_id}/images
```

#### Path Parameters

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

<ParamField path="variant_public_id" type="string" required>
  The variant's public ID, prefixed `var_`.
</ParamField>

#### Request Body

<ParamField body="image_url" type="string" required>
  Fully qualified ImageKit URL. Maximum 2048 characters.
</ParamField>

<ParamField body="alt_text" type="string">
  Accessibility alt text. Maximum 255 characters. Optional.
</ParamField>

<ParamField body="display_order" type="integer">
  Zero-based gallery sort position. Must be ≥ 0. Optional.
</ParamField>

#### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    https://api.example.com/api/v1/admin/products/prd_01H/variants/var_01H/images \
    -H "Content-Type: application/json" \
    -H "Cookie: session=<your-session-cookie>" \
    -H "x-csrf-token: $TOKEN" \
    -d '{
      "image_url": "https://ik.imagekit.io/yourstore/variants/runner-black-42.jpg",
      "alt_text": "Runner Sneaker Black size 42 – front view",
      "display_order": 0
    }'
  ```

  ```javascript Node.js theme={null}
  await fetch(
    'https://api.example.com/api/v1/admin/products/prd_01H/variants/var_01H/images',
    {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
        'x-csrf-token': csrfToken,
      },
      body: JSON.stringify({
        image_url: 'https://ik.imagekit.io/yourstore/variants/runner-black-42.jpg',
        alt_text: 'Runner Sneaker Black size 42 – front view',
        display_order: 0,
      }),
    }
  );
  ```
</CodeGroup>

***

### Get Variant Image

Fetch a single variant image by its public ID.

```
GET /admin/products/{product_public_id}/variants/{variant_public_id}/images/{variant_image_public_id}
```

#### Path Parameters

<ParamField path="product_public_id" type="string" required>The parent product's public ID, prefixed `prd_`.</ParamField>
<ParamField path="variant_public_id" type="string" required>The variant's public ID, prefixed `var_`.</ParamField>
<ParamField path="variant_image_public_id" type="string" required>The variant image's public ID, prefixed `vimg_`.</ParamField>

***

### Update Variant Image

Partially update a variant image's URL, alt text, or display order.

```
PATCH /admin/products/{product_public_id}/variants/{variant_public_id}/images/{variant_image_public_id}
```

#### Path Parameters

<ParamField path="product_public_id" type="string" required>The parent product's public ID, prefixed `prd_`.</ParamField>
<ParamField path="variant_public_id" type="string" required>The variant's public ID, prefixed `var_`.</ParamField>
<ParamField path="variant_image_public_id" type="string" required>The variant image's public ID, prefixed `vimg_`.</ParamField>

#### Request Body

At least one field is required.

<ParamField body="image_url" type="string">Replacement ImageKit URL. Maximum 2048 characters.</ParamField>
<ParamField body="alt_text" type="string | null">Updated alt text. Pass `null` to clear.</ParamField>
<ParamField body="display_order" type="integer">Updated sort position. Must be ≥ 0.</ParamField>

***

### Delete Variant Image

Delete a variant image. Returns `204 No Content` on success. Returns `404` if the product, variant, or image does not exist.

```
DELETE /admin/products/{product_public_id}/variants/{variant_public_id}/images/{variant_image_public_id}
```

#### Path Parameters

<ParamField path="product_public_id" type="string" required>The parent product's public ID, prefixed `prd_`.</ParamField>
<ParamField path="variant_public_id" type="string" required>The variant's public ID, prefixed `var_`.</ParamField>
<ParamField path="variant_image_public_id" type="string" required>The variant image's public ID, prefixed `vimg_`.</ParamField>

#### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE \
    https://api.example.com/api/v1/admin/products/prd_01H/variants/var_01H/images/vimg_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/variants/var_01H/images/vimg_01H',
    {
      method: 'DELETE',
      credentials: 'include',
      headers: { 'x-csrf-token': csrfToken },
    }
  );
  ```
</CodeGroup>
