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

# Working with products

> How product pricing, descriptions, images, variants and options behave in the Merchant API V2 — the field rules the endpoint reference can't show.

The [product endpoints](/api-reference/v2/endpoint/list-products) cover the shape of a product. This page covers the behaviour behind four fields merchants ask about most.

## `original_price` is a compare-at price

`price` is what the customer pays. `original_price` is the crossed-out price shown next to it, so shoppers can see the markdown.

```json theme={null}
{
  "price": 800,           // $8.00 — what is charged
  "original_price": 1000  // $10.00 — struck through beside it
}
```

It defaults to **`0`**, which means *no strike-through* — the same as leaving the compare-at field empty in the admin. That is deliberate: a product with no promotion should show one price, not a price crossed out beside an identical price.

<Warning>
  The storefront does not check that `original_price` is higher than `price` —
  it renders whatever you send. Setting it equal to `price` prints the same
  amount twice, once struck through, which reads to a shopper as a mistake.
  Send `0` when there is no promotion.
</Warning>

Variants carry their own `price` and `original_price`, and follow the same rule. When a product has variants, the storefront shows the first variant's pair.

<Note>
  Products created through the API before September 2026 defaulted
  `original_price` to `price`, which showed exactly that duplicated,
  struck-through price. Send `"original_price": 0` on those products to clear
  it.
</Note>

## `description` supports a small Markdown subset

Product descriptions are **not** full Markdown or HTML. The storefront renders this much:

| Syntax                              | Result          |
| ----------------------------------- | --------------- |
| `**bold**`                          | **bold**        |
| `_italic_` or `*italic*`            | *italic*        |
| `~strike~`                          | ~~strike~~      |
| `[text](https://example.com)`       | a link          |
| `![alt](https://example.com/a.jpg)` | an inline image |
| `https://example.com`               | auto-linked     |

Line breaks are preserved as written. Everything else — headings, lists, tables, block quotes, code fences, raw HTML — is **not parsed** and appears literally, `#` and `-` characters included. A line starting with `- ` reads as a bullet only because the dash is visible, not because a list is rendered.

## `images` is the whole image list

`images` is an array of URLs in display order, and an update **replaces** the list:

```json theme={null}
PATCH /api/v2/products/prod_123
{
  "images": [
    "https://storage.googleapis.com/take-app/media/abc.jpg",
    "https://supplier.example.com/new-photo.jpg"
  ]
}
```

Every image is copied onto Take App storage, because a supplier URL can rot or start blocking hotlinks while the storefront still has to render. A URL that is already ours is recognised and kept in place; any other URL is fetched and stored, and the response returns the new URL.

* Omit `images` and the current images are left alone.
* Send `[]` and all images are removed.
* Send back the URLs the API returned to reorder without re-uploading. Sending an external URL again fetches it again.
* Up to 10 images per request. A URL we can't fetch fails the whole request with `400 invalid_request_error` on `images`.

## `variants` and `options` replace their list

Both are lists the product owns outright, so `PATCH` treats them the same way as `images` — the array you send becomes the list, in the order you send it.

```json theme={null}
PATCH /api/v2/products/prod_123
{
  "variants": [
    { "id": "var_existing", "name": "Small", "price": 800 },
    { "name": "Large", "price": 1200, "inventory_quantity": 5 }
  ]
}
```

* An entry **with** `id` edits that variant. The id has to belong to this product.
* An entry **without** `id` creates one.
* A variant that already exists and is **not** in the array is **deleted**, along with its stock and any back-in-stock requests. Past orders keep their line items.
* Omit the field entirely and the list is untouched. `"variants": []` removes them all.

Options follow the same rules, except that an option's `type` is fixed once created — it is only read on entries without an `id`.

Changing `inventory_quantity` on an existing variant is recorded in stock history exactly as [`/api/v2/inventory_items`](/api-reference/v2/endpoint/update-inventory-item) would record it, so counts stay auditable however you set them.

## Deleting a product

[`DELETE /api/v2/products/{product_id}`](/api-reference/v2/endpoint/delete-product) removes the product, its variants, options and images, and drops it from every category.

```json theme={null}
{ "id": "prod_123", "object": "product", "deleted": true }
```

Orders that already contain the product keep their line items, so revenue history stays intact. There is no undo — set `"status": "HIDDEN"` instead if you may want the product back.

<Note>
  Delete is REST-only. The MCP server exposes reads, creates and updates but
  not delete, so an AI agent connected to a store cannot remove a catalog.
</Note>
