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

# PATCH /v1/catalogue/products/{product_id}

> Change a product — only what you send changes.

Changes a product; only the fields you send change. What actually changed is recorded in the product's history under the key's name (as "API · Shopify sync"), a product on sale is queued for the WhatsApp catalogue again, and webhooks get `product.changed`. Sending what it already has changes nothing.

<Note>Needs the `catalogue.products:write` scope.</Note>

## Path parameters

| Field | Type | Required | Notes |
| :- | :- | :- | :- |
| `product_id` | string | Yes | The product's id. |

## Body

| Field | Type | Required | Notes |
| :- | :- | :- | :- |
| `sku` | string | No | A new product code: up to 100 letters, digits, dots, dashes and underscores, unique in the account. The SKU can't change once WhatsApp has the product. |
| `name` | string | No | What customers see. |
| `description` | string | No | Agents quote it: say what a customer would ask — material, size, care. |
| `price_minor` | integer | No | The price in minor units (paise for `INR`): `249900` is ₹2,499.00. 0 when left out. |
| `sale_price_minor` | integer | No | The sale price in minor units, or `null` when it isn't on sale. While there is one, it is what a customer pays. |
| `currency` | string | No | A three-letter code. The account's catalogue currency when left out. |
| `availability` | string | No | `in_stock` or `out_of_stock`. |
| `stock` | integer | No | How many are left, or `null` to not count them. `0` makes the product `out_of_stock`. |
| `category` | string | No | For finding and grouping products. |
| `brand` | string | No | |
| `url` | string | No | Its page on your website. Without one, customers get its page on your public catalogue. |
| `sets` | array | No | Names of the sets it belongs to — exactly these, up to 20. A name with no set yet makes one. |
| `active` | boolean | No | Whether it is on sale. `true` by default. |

Set `active` to `false` to take a product off sale, and back to `true` to put it on sale again.

## Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH "https://api.fireflo.au/v1/catalogue/products/3f8a2c1e-7b4d-4e6a-9c2f-1a5b8d7e6f40" \
    -H "Authorization: Bearer $OMNI_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "sale_price_minor": null,
    "stock": 0
  }'
  ```

  ```python Python theme={null}
  import os

  import requests

  response = requests.patch(
      "https://api.fireflo.au/v1/catalogue/products/3f8a2c1e-7b4d-4e6a-9c2f-1a5b8d7e6f40",
      headers={
          "Authorization": f"Bearer {os.environ['OMNI_API_KEY']}",
      },
      json={
          "sale_price_minor": None,
          "stock": 0,
      },
  )
  print(response.status_code, response.json())
  ```

  ```javascript Node theme={null}
  const response = await fetch("https://api.fireflo.au/v1/catalogue/products/3f8a2c1e-7b4d-4e6a-9c2f-1a5b8d7e6f40", {
    method: "PATCH",
    headers: {
      Authorization: `Bearer ${process.env.OMNI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      "sale_price_minor": null,
      "stock": 0
    }),
  });
  console.log(response.status, await response.json());
  ```
</CodeGroup>

## Response

`200 OK` — the product as it is now. `stock` of `0` made it `out_of_stock`.

```json theme={null}
{
  "id": "3f8a2c1e-7b4d-4e6a-9c2f-1a5b8d7e6f40",
  "sku": "KUR-COT-IND-M",
  "name": "Cotton kurta — indigo, M",
  "description": "Hand-block printed cotton from Jaipur. Machine wash cold.",
  "price_minor": 249900,
  "sale_price_minor": null,
  "currency": "INR",
  "availability": "out_of_stock",
  "stock": 0,
  "category": "Kurtas",
  "brand": "Neel Weaves",
  "url": "",
  "has_image": true,
  "sets": [
    "Diwali edit"
  ],
  "active": true,
  "whatsapp": {
    "state": "pending",
    "error": ""
  },
  "updated_at": "2026-10-06T12:40:05+05:30"
}
```

## Errors

Every refusal is `{"error": {"code", "message", "field"}}`; `field` is there when one input is at fault.

| Status | Error code | When |
| :- | :- | :- |
| 400 | `invalid_request` | Something in the body can't be saved, and `message` says what: a SKU with other characters or that another product already uses, `availability` that isn't `in_stock` or `out_of_stock`, a `stock` that isn't a whole number, a price that isn't a whole number of minor units or is below zero, or `sets` that isn't a list (`field` names it). |
| 404 | `not_found` | No product of this account's has that id. |
| 404 | `module_off` | The account doesn't have Catalogue on. |
| 403 | `scope_missing` | The key doesn't have the `catalogue.products:write` scope. |

Any request can also be refused for its key, its account or its rate (`key_required`, `invalid_key`, `account_suspended`, `plan_excludes_api`, `address_not_allowed`, `rate_limited`); see [the overview](/api-reference/overview).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.