# Variants

> The versions of a product a shopper buys, and their options.

Source: https://storeware.ai/docs/api-reference/rest/variants

## Read what every variant of a product holds

`GET /api/v1/stores/{storeId}/products/{productId}/variant-extras`

Stock, downloads, own pictures and custom data for each variant of one product, in one call. A variant with nothing recorded comes back with empty lists, so each variant has an entry, in the order the product lists them.

Needs `products:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `productId` | path | string | The product. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [VariantExtras](https://storeware.ai/docs/api-reference/rest/objects#variantextras) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/products/$PRODUCT_ID/variant-extras" \
  -H "Authorization: Bearer $TOKEN"
```

## Update a variant

`PATCH /api/v1/stores/{storeId}/variants/{variantId}`

Changes only the fields sent, and a null clears a field that may be empty. A digital variant cannot also require shipping, so send `isDigital` true with `requiresShipping` false. Refused with `variant_sku_taken` for a SKU another variant has. Price, cost and back orders have calls of their own.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | string | No | 1 to 300 characters. |
| `sku` | string or null | No | 1 to 120 characters. |
| `barcode` | string or null | No | 1 to 120 characters. |
| `lowStockThreshold` | integer or null | No | From 0 to 100,000. |
| `weightGrams` | integer or null | No | From 0 to 1,000,000. |
| `hsCode` | string or null | No | Matches `^\d{4}(\.?\d{2}){1,3}$`. |
| `countryOfOrigin` | string or null | No | Matches `^[A-Za-z]{2}$`. |
| `requiresShipping` | boolean | No |  |
| `isDigital` | boolean | No |  |
| `position` | integer | No | From 0 to 10,000. |
| `orderMinimum` | integer or null | No | From 1 to 10,000. |
| `orderMaximum` | integer or null | No | From 1 to 10,000. |
| `orderIncrement` | integer or null | No | From 1 to 10,000. |
| `taxClassId` | string (uuid) or null | No |  |
| `shippingClassId` | string (uuid) or null | No |  |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [Variant](https://storeware.ai/docs/api-reference/rest/objects#variant) |  |

```bash
curl -X PATCH "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Set a variant’s price

`POST /api/v1/stores/{storeId}/variants/{variantId}/price`

Sets or replaces the price in one currency, in minor units. A `compareAtAmount` is shown struck through and must be higher than the price (`invalid_compare_at_price`); leaving it out removes one already set. Every change is kept in the price history.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount` | integer | Yes | At least 0. |
| `currency` | string | Yes | Matches `^[A-Z]{3}$`. |
| `compareAtAmount` | integer | No |  |

**Answers** `204`: Done, with nothing to send back.

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/price" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List a variant’s price changes

`GET /api/v1/stores/{storeId}/variants/{variantId}/price/history`

Every price the variant has had, newest first, each with the one before it.

Needs `products:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |
| `limit` | query | integer | How many to return, up to 200. Twenty when not given. From 1 to 200. |
| `currency` | query | string | Only changes in this currency, such as `GBP`. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [PriceChange](https://storeware.ai/docs/api-reference/rest/objects#pricechange) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/price/history" \
  -H "Authorization: Bearer $TOKEN"
```

## Put a price back

`POST /api/v1/stores/{storeId}/variants/{variantId}/price/revert`

Restores the price, and its compare-at price, from before one change in the price history. The revert is a change of its own and appears in the history. Refused with `nothing_to_revert` for a variant’s first price.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `historyId` | integer | Yes | The `id` of the change to undo, from the history. |

**Answers** `204`: Done, with nothing to send back.

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/price/revert" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Set what a variant cost to buy

`POST /api/v1/stores/{storeId}/variants/{variantId}/cost`

Sets the cost of one, in minor units, or clears it with a body of `null`, which means unknown. Shoppers never see it and no webhook carries it; it is read back as each variant’s `cost` on the product.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Body**

Any JSON.

**Answers** `204`: Done, with nothing to send back.

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/cost" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Set whether a variant sells with no stock

`POST /api/v1/stores/{storeId}/variants/{variantId}/backorder`

With `allow` true the variant keeps selling once its stock reaches zero; with false it stops there.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `allow` | boolean | Yes |  |

**Answers** `204`: Done, with nothing to send back.

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/backorder" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List a variant’s images

`GET /api/v1/stores/{storeId}/variants/{variantId}/media`

The images shown when a shopper picks this variant, in order. A variant with none shows the product’s gallery.

Needs `products:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [ProductMedia](https://storeware.ai/docs/api-reference/rest/objects#productmedia) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/media" \
  -H "Authorization: Bearer $TOKEN"
```

## Show an image for a variant

`POST /api/v1/stores/{storeId}/variants/{variantId}/media`

Adds an image already uploaded to the shop, usually one in the product’s gallery, to the variant’s own images. It is not added to the gallery. Adding one already there changes nothing.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `mediaId` | string (uuid) | Yes | An image uploaded to the shop. |

**Answers** `204`: Done, with nothing to send back.

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/media" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## Take an image off a variant

`DELETE /api/v1/stores/{storeId}/variants/{variantId}/media`

Takes the image off the variant’s own images and leaves the image itself alone. The id goes in the query string.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |
| `mediaId` | query | string (uuid) | Required. The image to take off. |

**Answers** `204`: Done, with nothing to send back.

```bash
curl -X DELETE "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/media" \
  -H "Authorization: Bearer $TOKEN"
```

## List a variant’s downloads

`GET /api/v1/stores/{storeId}/variants/{variantId}/digital-assets`

The files a buyer of this variant may download, oldest first.

Needs `products:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of [DigitalAsset](https://storeware.ai/docs/api-reference/rest/objects#digitalasset) |  |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/digital-assets" \
  -H "Authorization: Bearer $TOKEN"
```

## Attach a download to a variant

`POST /api/v1/stores/{storeId}/variants/{variantId}/digital-assets`

Records a file uploaded with a ticket from `POST /digital-assets/upload-url`, by its `storagePath`, so every later buyer of the variant may download it. It does not make the variant digital: a book with a bonus file is still posted. When the file is all a buyer gets, set `isDigital` with `updateVariant`.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `storagePath` | string | Yes | 1 to 500 characters. |
| `fileName` | string | Yes | 1 to 255 characters. |
| `sizeBytes` | integer | No | At most 524,288,000. |
| `downloadLimit` | integer | No | At most 1,000. |

**Answers** `201`

| Field | Type | Description |
| --- | --- | --- |
| `data` | [DigitalAsset](https://storeware.ai/docs/api-reference/rest/objects#digitalasset) |  |

```bash
curl -X POST "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/digital-assets" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```

## List a variant’s metafields

`GET /api/v1/stores/{storeId}/variants/{variantId}/metafields`

Every metafield value set on a variant, each with its definition.

Needs `products:read`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of object |  |
| `data[].definitionId` | string (uuid) |  |
| `data[].namespace` | string |  |
| `data[].key` | string |  |
| `data[].label` | string | The merchant’s name for the field. |
| `data[].type` | string | single_line_text, multi_line_text, integer, decimal, boolean, date, url and the other definition types. |
| `data[].isList` | boolean |  |
| `data[].storefrontVisible` | boolean |  |
| `data[].searchFacet` | boolean |  |
| `data[].position` | integer |  |
| `data[].value` | any | The value, or a list of values when `isList`. |
| `data[].displayValue` | string | The value as one line of text, lists joined. |
| `data[].source` | string | Who set it: the merchant, an import, or an app’s slug. |

```bash
curl "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/metafields" \
  -H "Authorization: Bearer $TOKEN"
```

## Set a variant’s metafields

`PUT /api/v1/stores/{storeId}/variants/{variantId}/metafields`

Sets up to 25 values in one call. A null value clears its field; keys not named are left alone. An app may set only the fields its manifest declares.

Needs `products:write`.

**Parameters**

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `storeId` | path | string | The shop. |
| `variantId` | path | string | The variant. |

**Body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `metafields` | array of object | Yes | Up to 25 items. At least 1 item. |
| `metafields[].namespace` | string | No | Default `custom`. |
| `metafields[].key` | string | Yes |  |
| `metafields[].value` | any | No |  |

**Answers** `200`

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of object |  |
| `data[].definitionId` | string (uuid) |  |
| `data[].namespace` | string |  |
| `data[].key` | string |  |
| `data[].label` | string | The merchant’s name for the field. |
| `data[].type` | string | single_line_text, multi_line_text, integer, decimal, boolean, date, url and the other definition types. |
| `data[].isList` | boolean |  |
| `data[].storefrontVisible` | boolean |  |
| `data[].searchFacet` | boolean |  |
| `data[].position` | integer |  |
| `data[].value` | any | The value, or a list of values when `isList`. |
| `data[].displayValue` | string | The value as one line of text, lists joined. |
| `data[].source` | string | Who set it: the merchant, an import, or an app’s slug. |

```bash
curl -X PUT "https://api.storeware.ai/api/v1/stores/$STORE_ID/variants/$VARIANT_ID/metafields" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @body.json
```
