Update a product on an offer
API v2 is in public beta: endpoints and fields can change before general availability. See the changelog
/preorders/offers/{id}/productsCosts 2 rate-limit points · Rate limits
Update any subset of the products configuration on a preorder offer: the selection rule, its collection or tags, the exclusions and which inventory policy newly attached variants get.
Deep partial: absent fields are unchanged. Switching selection removes the current variants on Shopify before anything is saved, then refills from the new rule asynchronously; a refused Shopify reset leaves the offer as it was. Custom variants are added and removed with add_variants and remove_variants.
Also searched asedit productschange product selectionchange variant sourceset productsmodify products on offerchange collectionswap collectionuse a different collectionupdate the collection on the offerset excluded variantsexclude variants listblock variants from offerset exclusion tagexclude by tagset exclude tagset source to allapply to all productsauto-sourceevery variantshop-wide preorderset source to collectionuse a collectionlink to collectionset products from collectiondrive variants from collectionset source to customuse custom variantscustom product listswitch to customset source to tagsuse product tagslink to tagsset products from tagsdrive variants from tagschange product tagsset offer tagsedit tags on offer
Path parameters
idstringRequired
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
selectionstringWhich rule puts variants on the offer: all (every variant in the shop, minus exclusions), collection, custom (variants added by hand with add_variants) or tags. Changing it removes the current variants from the offer and refills them from the new rule; the previous rule's collection, tags and exclusions are cleared.
One ofallcollectioncustomtagsshopify_collection_idstringThe Shopify collection driving the variant list: its Shopify collection gid from resolve_entities (kind collection); never a title. Required when switching selection to collection.
tagsarray of stringsProduct tags; products carrying any of them are on the offer. Replaces the list. Required when switching selection to tags.
eligible_inventory_policystringcontinue_selling: STOQ turns on 'Continue selling when out of stock' for the rule's out-of-stock variants so all of them take preorders (editor: Track all products). all: only variants that already have 'Continue selling' on take preorders (editor: Only track products with 'Continue selling' enabled). Not for custom selection.
One ofcontinue_sellingallexcluded_shopify_variant_idsarray of anys, nullableVariants held back: Shopify variant gids. Replaces the list; [] or null clears it. selection all or tags only.
exclusion_tagstring, nullableVariants of products carrying this tag are held back. null clears it. selection all only.
inventory_policy_for_removed_variantsstring, nullableWhen selection changes: CONTINUE or DENY sets the inventory policy of the variants coming off the offer first; omit or null leaves it unchanged.
One ofCONTINUEDENYnull
Returns
selectionstringExample:collectionshopify_collection_idstringExample:gid://shopify/Collection/482865234104tagsarray of anyseligible_inventory_policystringExample:allexcluded_shopify_variant_idsarray of anysexclusion_tagany
Errors
Missing or invalid X-Auth-Token.
The resource does not exist for this shop, or the action path is not registered.
An earlier request with the same Idempotency-Key is still running. Wait, then resend the same request with the same key.
Invalid fields: one details entry per field error, its path the canonical section path. Nothing was saved.
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/preorders/offers/73a74948-2b57-4cc3-971f-2b441828c977/products" \
-H "X-Auth-Token: $STOQ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"selection": "collection",
"shopify_collection_id": "gid://shopify/Collection/482865234104"
}'const res = await fetch("https://app.stoqapp.com/api/v2/external/preorders/offers/73a74948-2b57-4cc3-971f-2b441828c977/products", {
method: "PATCH",
headers: {
"X-Auth-Token": process.env.STOQ_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
"selection": "collection",
"shopify_collection_id": "gid://shopify/Collection/482865234104"
}),
});
const data = await res.json();import os
import requests
res = requests.patch(
"https://app.stoqapp.com/api/v2/external/preorders/offers/73a74948-2b57-4cc3-971f-2b441828c977/products",
headers={"X-Auth-Token": os.environ["STOQ_API_KEY"]},
json={
"selection": "collection",
"shopify_collection_id": "gid://shopify/Collection/482865234104"
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://app.stoqapp.com/api/v2/external/preorders/offers/73a74948-2b57-4cc3-971f-2b441828c977/products")
req = Net::HTTP::Patch.new(uri, "X-Auth-Token" => ENV["STOQ_API_KEY"], "Content-Type" => "application/json")
req.body = {
selection: "collection",
shopify_collection_id: "gid://shopify/Collection/482865234104"
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body){
"selection": "collection",
"shopify_collection_id": "gid://shopify/Collection/482865234104",
"tags": [],
"eligible_inventory_policy": "all",
"excluded_shopify_variant_ids": [],
"exclusion_tag": null
}{
"errors": [
"Invalid API key"
],
"code": "auth_invalid",
"details": [
{
"code": "auth_invalid",
"message": "Invalid API key",
"path": null,
"value": null,
"expected": null
}
]
}{
"errors": [
"Offer not found"
],
"code": "not_found",
"details": [
{
"code": "not_found",
"message": "Offer not found",
"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": [
"widget.button.text_color must be a hex color"
],
"code": "invalid_params",
"details": [
{
"code": "invalid_params",
"message": "must be a hex color",
"path": "widget.button.text_color",
"value": "blue",
"expected": {
"type": "string"
}
}
]
}{
"errors": [
"Rate limit exceeded"
],
"code": "rate_limited",
"details": [
{
"code": "rate_limited",
"message": "Rate limit exceeded",
"path": null,
"value": null,
"expected": null
}
]
}