Update a product
API v2 is in public beta: endpoints and fields can change before general availability. See the changelog
/back_in_stock/productsCosts 2 rate-limit points · Rate limits
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 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 "always_show" (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), "when_out_of_stock" (the default: button appears only when out of stock and alerts send automatically) or "never_show" (button hidden on the storefront). restock_message is the free-text note shown under the button (e.g. "Back in stock March 15"); send an empty string to clear it. widget_template_id is a widget template id from GET /back_in_stock/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 "Enable/Disable alerts" button, independent of button_visibility, and the field GET /back_in_stock/products reads back. Send false to fix a variant showing "Disabled" for automatic alerts, not button_visibility. If both button_visibility: "always_show" 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 "Disable per-product overrides").
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.
Also searched ashide the notify me button on these productsalways show the notify me buttonshow the notify me button even when in stockhide notify me on discontinued productsadd a restock message to a productset a back in stock date on a productclear the restock noteuse a custom back in stock template on this productassign a template to a productper product notify me settingsper product back in stock settingsbutton visibility for a productdisable per product overridesturn off per product customization
Headers
Idempotency-KeystringA 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 (422idempotency_key_reused); a retry while the first is still running gets 409idempotency_key_in_progress. Keys are per store. Server errors and rate limits are not kept, so their retry runs.
Request body
widget_template_idinteger, nullableExample:stringtargetsstringExample:stringbutton_visibilitystringExample:stringrestock_messagestringExample:stringenable_customizationstringExample:stringautomatic_alerts_disabledstringExample:stringper_product_customization_enabledbooleanExample:true
Errors
Missing or invalid X-Auth-Token.
An earlier request with the same Idempotency-Key is still running. Wait, then resend the same request with the same key.
The request failed validation.
Rate limited: cost-weighted per token (read 1, write 2; 360 points / 60s). Check Retry-After.
Every error has the same shape: see Errors.
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
}'const res = await fetch("https://app.stoqapp.com/api/v2/external/back_in_stock/products", {
method: "PATCH",
headers: {
"X-Auth-Token": process.env.STOQ_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
"widget_template_id": "string",
"targets": "string",
"button_visibility": "string",
"restock_message": "string",
"enable_customization": "string",
"automatic_alerts_disabled": "string",
"per_product_customization_enabled": true
}),
});
const data = await res.json();import os
import requests
res = requests.patch(
"https://app.stoqapp.com/api/v2/external/back_in_stock/products",
headers={"X-Auth-Token": os.environ["STOQ_API_KEY"]},
json={
"widget_template_id": "string",
"targets": "string",
"button_visibility": "string",
"restock_message": "string",
"enable_customization": "string",
"automatic_alerts_disabled": "string",
"per_product_customization_enabled": True
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://app.stoqapp.com/api/v2/external/back_in_stock/products")
req = Net::HTTP::Patch.new(uri, "X-Auth-Token" => ENV["STOQ_API_KEY"], "Content-Type" => "application/json")
req.body = {
widget_template_id: "string",
targets: "string",
button_visibility: "string",
restock_message: "string",
enable_customization: "string",
automatic_alerts_disabled: "string",
per_product_customization_enabled: true
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){
"job_id": "job_a1b2c3",
"status_url": "/api/v2/external/jobs/job_a1b2c3"
}{
"errors": [
"Invalid API key"
],
"code": "auth_invalid",
"details": [
{
"code": "auth_invalid",
"message": "Invalid API key",
"path": null,
"value": null,
"expected": null
}
]
}{
"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
}
]
}{
"errors": [
"name is required"
],
"code": "validation_failed",
"details": [
{
"code": "validation_failed",
"message": "name is required",
"path": null,
"value": null,
"expected": null
}
]
}{
"errors": [
"Rate limit exceeded"
],
"code": "rate_limited",
"details": [
{
"code": "rate_limited",
"message": "Rate limit exceeded",
"path": null,
"value": null,
"expected": null
}
]
}