---
title: "Update a product"
description: "Update per-product back-in-stock configuration for one or more products or variants (the \"Back in stock alerts -> Products\" tab). targets is a list; a target with shopify_variant_id changes just that VARIANT, a target wi"
url: https://docs.stoqapp.com/v2-reference/back-in-stock-products/update-a-product/
api: "API v2 reference"
method: PATCH
path: "/back_in_stock/products"
---

# Update a product

`PATCH https://app.stoqapp.com/api/v2/external/back_in_stock/products`

Update per-product back-in-stock configuration for one or more products or variants (the &quot;Back in stock alerts -&gt; Products&quot; tab). `targets` is a list; a target with shopify_variant_id changes just that VARIANT, a target with only shopify_product_id changes EVERY variant of that product. The same field values are applied to every target, so send one call per distinct set of values. button_visibility is one of &quot;always_show&quot; (notify-me button shown even while the item is IN stock -- WARNING: this also DISABLES automatic back-in-stock alerts for that variant, so the merchant has to notify signups manually), &quot;when_out_of_stock&quot; (the default: button appears only when out of stock and alerts send automatically) or &quot;never_show&quot; (button hidden on the storefront). restock_message is the free-text note shown under the button (e.g. &quot;Back in stock March 15&quot;); send an empty string to clear it. widget_template_id is a widget template id from [`GET /back_in_stock/widget_templates`](https://docs.stoqapp.com/v2-reference/back-in-stock-widget-templates/list-widget-templates/), or null to fall back to the global widget design. Target ids are Shopify gids. automatic_alerts_disabled (true/false) directly turns this variant's automatic back-in-stock alert on restock off (true) or on (false) -- the same control as the variant's own &quot;Enable/Disable alerts&quot; button, independent of button_visibility, and the field [`GET /back_in_stock/products`](https://docs.stoqapp.com/v2-reference/back-in-stock-products/list-products/) reads back. Send false to fix a variant showing &quot;Disabled&quot; for automatic alerts, not button_visibility. If both button_visibility: &quot;always_show&quot; and automatic_alerts_disabled are sent in the same call, automatic_alerts_disabled wins. Per-product visibility/message/template values only take effect while the shop-level toggle configure_notify_me_enabled is on; when it is off this action refuses rather than store values that would not apply, unless enable_customization: true is passed, which turns the toggle on as part of this change (a no-op when already on). automatic_alerts_disabled is NOT gated by configure_notify_me_enabled -- it applies regardless. per_product_customization_enabled (true/false), sent on its own with no targets, turns that shop-level toggle on or off (off = the tab's &quot;Disable per-product overrides&quot;).

**Note:** Bulk-safe: a single call may address up to 100 targets and writes them in one transaction with the Shopify metafield syncs fired once for the whole batch. A target STOQ has no product_variants row for is looked up in the shop's Shopify catalog; when it is there, its rows are created (one per variant for a product target) and the change is applied to them. Ids in neither are reported in `unmatched`. Deleting a back-in-stock template clears it from every product/variant it was assigned to (they fall back to the global design); template CRUD lives on the templates capability. automatic_alerts_enabled is the deprecated, inverted name for automatic_alerts_disabled: it is still applied, and the response carries a warning.

## Headers

- `Idempotency-Key` (string): A fresh unique key for every write (a new UUID, say); reuse it only to retry that write. Keys are remembered for 1 hour: the same key and the same request within that hour returns the first result, with `Idempotent-Replayed: true`, without running again; the same key with a different method, path or body is refused (422 `idempotency_key_reused`); a retry while the first is still running gets 409 `idempotency_key_in_progress`. Keys are per store. Server errors and rate limits are not kept, so their retry runs.

## Request body

- `widget_template_id` (integer, nullable): e.g. `string`
- `targets` (string): e.g. `string`
- `button_visibility` (string): e.g. `string`
- `restock_message` (string): e.g. `string`
- `enable_customization` (string): e.g. `string`
- `automatic_alerts_disabled` (string): e.g. `string`
- `per_product_customization_enabled` (boolean): e.g. `true`

Rate limit: this call costs 2 points ([rate limits](https://docs.stoqapp.com/docs/rate-limits/)).

## Example request

```sh
curl -X PATCH "https://app.stoqapp.com/api/v2/external/back_in_stock/products" \
  -H "X-Auth-Token: $STOQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "widget_template_id": "string",
    "targets": "string",
    "button_visibility": "string",
    "restock_message": "string",
    "enable_customization": "string",
    "automatic_alerts_disabled": "string",
    "per_product_customization_enabled": true
  }'
```

## 200

Success: the updated resource.

## 202

Accepted: async/bulk work was queued. Poll the returned job.

```json
{
  "job_id": "job_a1b2c3",
  "status_url": "/api/v2/external/jobs/job_a1b2c3"
}
```

## 401

Missing or invalid `X-Auth-Token`.

```json
{
  "errors": [
    "Invalid API key"
  ],
  "code": "auth_invalid",
  "details": [
    {
      "code": "auth_invalid",
      "message": "Invalid API key",
      "path": null,
      "value": null,
      "expected": null
    }
  ]
}
```

## 409

An earlier request with the same Idempotency-Key is still running. Wait, then resend the same request with the same key.

```json
{
  "errors": [
    "A request with this Idempotency-Key is still running"
  ],
  "code": "idempotency_key_in_progress",
  "details": [
    {
      "code": "idempotency_key_in_progress",
      "message": "A request with this Idempotency-Key is still running",
      "path": null,
      "value": null,
      "expected": null
    }
  ]
}
```

## 422

The request failed validation.

```json
{
  "errors": [
    "name is required"
  ],
  "code": "validation_failed",
  "details": [
    {
      "code": "validation_failed",
      "message": "name is required",
      "path": null,
      "value": null,
      "expected": null
    }
  ]
}
```

## 429

Rate limited: cost-weighted per token (read 1, write 2; 360 points / 60s). Check `Retry-After`.

```json
{
  "errors": [
    "Rate limit exceeded"
  ],
  "code": "rate_limited",
  "details": [
    {
      "code": "rate_limited",
      "message": "Rate limit exceeded",
      "path": null,
      "value": null,
      "expected": null
    }
  ]
}
```

