# Update a product

PATCH https://api.oletapp.com/your-store/v1/products/{id}

Updates a product, and replaces any nested collection included in the body.

- Omitting `codes`, `tags`, `suppliers`, `variant_attributes` or `components` leaves that collection untouched; sending `[]` clears it.
- tags are referenced by tag id; an unknown id is a 400.
- supply_price is never accepted, neither on the product nor on a nested supplier.
- variant_options is read-only and ignored if sent.
- variant_attributes can only change while the product has no live variant children; once it does, a change is a 409.
- The `version` a GET response returned can be sent back as the concurrency check, same as today's `expected_version`; sending both requires they agree.
- The write is atomic: an invalid entry in any collection applies nothing, not even a scalar change.
- image_url fetches the image once and imports it through the same pipeline as an admin upload; a fetch failure does not undo the update: it answers 422 IMAGE_FETCH_FAILED with the updated product in data. Gift card products can't be changed through this endpoint.

- `400 VALIDATION_FAILED`: a field failed validation, `version` and `expected_version` disagree, or a variant_attributes rule (more than 3 entries, a position outside 1 to 3, or a duplicate position).
- `404 NOT_FOUND`: the id is foreign, unknown, or already deleted, or a referenced brand or category was not found.
- `409 CONFLICT`: variant_attributes changed while the product has live variant children, or a nested code or supplier link conflicts with an existing one.
- `409 STALE_VERSION`: `version`/`expected_version` did not match the product's current version.
- `422 UNPROCESSABLE`: it is a gift card product, or a codes, tags, suppliers or components cap was exceeded.
- `422 IMAGE_FETCH_FAILED`: the product was updated but the image could not be imported.

**Requires scope:** `catalog:write`

Required scope: `catalog:write`

## Parameters

| Name | In | Type | Required |
|---|---|---|---|
| `id` | path | string | yes |

## Request body

```
{
  active?: boolean
  brand_id?: string | null
  category_id?: string | string | null
  codes?: {
  code: string
  type: "upc" | "isbn" | "itf" | "ean" | "jan" | "custom"
}[]
  components?: {
  product_id: string
  quantity: integer
}[]
  created_at?: unknown
  description?: string | null
  dimension_unit?: "mm" | "cm" | "in" | "ft" | null
  expected_version?: integer | string
  has_inventory?: boolean
  has_variants?: unknown
  height?: number | null
  id?: unknown
  image_thumb_url?: unknown
  image_url?: string | null
  is_composite?: unknown
  is_gift_card?: unknown
  length?: number | null
  loyalty_amount?: integer | null
  name?: string
  purchase_account_code?: string | null
  retail_price?: integer
  sales_account_code?: string | null
  sku?: unknown
  suppliers?: {
  id?: string
  is_primary?: boolean
  supplier_code?: string | null
  supplier_id: string
  supplier_name?: string
}[]
  tags?: {
  id: string
  name?: string
}[]
  updated_at?: unknown
  variant_attributes?: {
  id?: string
  name: string
  position: integer
}[]
  variant_options?: unknown[]
  variant_parent_id?: unknown
  version?: integer | string
  weight?: number | null
  weight_unit?: "g" | "kg" | "lb" | "oz" | null
  width?: number | null
}
```

## Response (200)

```
{
  data: {
  active: boolean
  brand_id: string | null
  category_id: string
  codes: ProductCode[]
  components: ProductComponent[]
  created_at: string
  description: string | null
  dimension_unit: string | null
  has_inventory: boolean
  has_variants: boolean
  height: number | null
  id: string
  image_thumb_url: string | null
  image_url: string | null
  is_composite: boolean
  is_gift_card: boolean
  length: number | null
  loyalty_amount: integer | null
  name: string
  purchase_account_code: string | null
  retail_price: integer
  sales_account_code: string | null
  sku: string
  suppliers: ProductSupplier[]
  tags: ProductTag[]
  updated_at: string
  variant_attributes: ProductAttribute[]
  variant_options: ProductVariantOption[]
  variant_parent_id: string | null
  version: string
  weight: number | null
  weight_unit: string | null
  width: number | null
}
}
```

Errors follow the standard error envelope: https://getolet.com/resources/developers/errors.md

## Example

```bash
curl -X PATCH "https://api.oletapp.com/your-store/v1/products/{id}" \
  -H "Authorization: Bearer olet_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```
