Updates
API Changelog
Dated list of changes to the STOQ API and MCP server that can affect your integration. Newest first.
API Changelog
Changes to the STOQ API and MCP server that can affect an integration. Newest first. Each entry says what changed, who it affects, and what to send instead.
The API v2 Reference is generated from the code on every deploy, so it always shows the current request fields.
2026-10-01 — Offer schedule and countdown times use the shop's time zone
- What changed:
- Behaviour change:
start/endonPOST /preorders/offers/{id}/schedule,custom_start_date/custom_end_dateonPATCH /preorders/offers/{id}/countdown, anddateonPOST /preorders/offers/{id}/countdown/set_custom_end_dateread a time with noZor offset in the shop's time zone. For a shop in Europe/London,2026-09-30T18:00is now 17:00 UTC. Before, it was 18:00 UTC. A value withZor an offset (2026-09-30T18:00:00Z,2026-09-30T18:00:00-04:00) means that exact moment, as before. A bare date is midnight in the shop's time zone. - Offer reads (
GET /preorders/offers,/preorders/offers/{id},/preorders/offer_lookup,/preorders/offers/{id}/countdown,/preorders/product_variants/{variant_id}) return schedule and countdown times with the shop's offset (2026-09-30T18:00:00+01:00), not in UTC. It is the same moment.status.scheduleaddstime_zone, the shop's IANA time zone.
- Behaviour change:
- Who it affects: callers that send schedule or countdown times with no offset, and callers that compare these times as strings instead of parsing them.
- What to send instead: send the shop's local time with no offset, or keep sending UTC with
Z. - Source: quinn-shop-timezone-schedule
2026-09-29 — Order list search finds a Shopify order id, gid or admin URL
- What changed:
searchonGET /preorders/ordersandGET /back_in_stock/ordersalso matches the Shopify order id. You can send the bare id (7489236566319), the gid (gid://shopify/Order/7489236566319) or an admin order URL (https://admin.shopify.com/store/<store>/orders/7489236566319orhttps://<shop>.myshopify.com/admin/orders/7489236566319).- A bare number of 10 or more digits is read as an order id, and still matches order number, name, email and customer name as before. A shorter number, or one that starts with
#(1001,#1001), is an order number as before.
- Who it affects: nobody has to change anything. Searches that returned nothing for an order id now return the order.
- Source: order-search-by-id
2026-09-29 — List reads return a summary of every matching row; shop time zone dates
- What changed:
GET /back_in_stock/notificationsalways returnssummary(total,sent,blocked,blocked_by_reason), not only withgroup_by=product. It counts every send that matches the filters, not only the page.GET /back_in_stock/signupsreturnssummary(total,pending,notified,blocked,unsubscribed) for every signup that matches the filters. Thestatusfilter acceptsblocked.GET /preorders/offersreturnssummary(total,active,scheduled,paused,ended) for every offer that matches the filters.- Behaviour change: on
GET /back_in_stock/notificationsandGET /back_in_stock/signups,from/togiven as bare dates now mean those whole days in the shop's time zone, as in the reports. Before, both were read as midnight UTC, soto=2026-09-14left out all of 14 September. Afromortothat does not parse now returns an error. Before, it matched no rows. - Behaviour change: on
GET /back_in_stock/signups,status=pendingno longer returns signups whosestatusisblocked. Usestatus=blockedfor those. Each signup now matches exactly the filter of its ownstatus.
- Who it affects: callers of those reads that pass bare dates or
status=pending. Everyone else only sees the newsummarykey. - Source: list-summaries
2026-09-27 — Offer status and preorder mode labels; inventory status rows use API values
- What changed:
GET /preorders/offers,/preorders/offers/{id},/preorders/offer_lookup,/preorders/product_variants/{variant_id}and/preorders/product_variants/{variant_id}/offerslistvalue_labelsin/help: offer states in the dashboard's words (activeLive,scheduledScheduled,pausedDisabled,endedExpired,discardedDeleted) and the inventory provider in the offer editor's words (stoq"Show 'Preorder' when product is out of stock",shopify"Show 'Preorder' for in-stock products"). Responses still return the raw values.- Behaviour change: in
GET /preorders/products/inventory_status, eachoffers[]row now hasoffer_status(the offer state, as elsewhere in the API) andinventory_provider. It no longer hasstatus({ state: "Live"|"Scheduled"|"Off", … }),preorder_typeoroffer_path, which were an assistant's words and an admin link.
- Who it affects: callers of
inventory_statusthat readoffers[].status,offers[].preorder_typeoroffers[].offer_path. - What to send instead: read
offers[].offer_status(activemeans taking preorders) andoffers[].inventory_provider. - Source: quinn-reads-v2-step10
2026-09-27 — Order reads: look up by any order reference; back-in-stock and unsynced orders; payment and fulfillment detail
- What changed:
GET /preorders/orders/{id}and its/payments,/fulfillment,/tagsand/timelinereads accept any order reference as{id}: the order name or number (#1234or1234, URL-encode the#), the Shopify order id orgid://shopify/Order/…, the customer's email or name, or STOQ's order id (idin responses). A bare number is read as a Shopify order id when one matches, else as an order number. When a reference matches several orders the read returns200with{ "ambiguous": true, "query": …, "candidates": [ … ] }; pick one and read it by itsid. No match is still404.GET /preorders/orders/{id}and/timelinealso read back-in-stock orders and orders not yet synced from Shopify (order_idandshopify_gidare thennull).typesays which (preorderorback_in_stock).- The order read adds
id,type,currency,financial_statusandfulfillment_status;payments.balanceaddsdue_amount,due_atandoverdue;paymentsaddslast_payment_attempt(status,shopify_payment_status,triggered_by,error_message,attempted_at); each line item addstitle,variant_titleandfulfillment_status; each attributed offer addsoffer_internal_nameandfulfillment(status,delivery,strategy,hold_reason_note). The timeline addsid.GET /preorders/ordersandGET /back_in_stock/ordersrows addidandtype; preorder rows addcurrency,financial_statusandfulfillment_status. - A path parameter may now contain a dot (for example an email address). Only a trailing
.json,.xml,.csv,.html,.txtor.jsis still refused.
- Who it affects: callers of those reads. Existing requests by Shopify order id work as before and see extra fields.
- Source: quinn-reads-v2-step9
2026-09-27 — New GET /account/setup
- What changed: new read
GET /account/setup. It returns whether STOQ's app embed is on in the published theme (app_embed.enabled, andapp_embed.theme_editor_url, a link that opens the theme editor and turns the embed on, when it is off), and the getting-started steps for back in stock and preorders: each step's state,steps_done,steps_total,next_stepandcompleted. - Who it affects: nobody; it is new.
- Source: quinn-reads-v2-step8
2026-09-27 — Back-in-stock reports: each number counts what its name says, and match the Overview dashboard
- Behaviour changes (existing requests can get different numbers):
GET /back_in_stock/reports/summary:notifiedandnotified_customersnow count signups notified in the window (bynotified_at). Before, they counted signups created in the window that had been notified at any time.pendingis now the waitlist now (not limited to the window; with double opt-in on, unconfirmed signups are left out). Before, it counted signups created in the window that were not notified.conversion_ratefollows the newnotified_customers. On one real shop over 30 days, notified signups went from 114 to 461, notified customers from 77 to 323, and pending from 137 to 661.GET /back_in_stock/reports/signups:notifiedper bucket now counts signups notified in that bucket. Before, it counted signups created in the bucket that had been notified.GET /back_in_stock/reports/products_in_demand:pendingis now signups waiting now, whatever the window; a variant with only older waiting signups now appears.totalis still signups created in the window.- Unscoped requests (no
variant_id,product_id,channelormarket_id) now read the Overview dashboard's sources: summary signups, conversions and revenue from the daily rollups plus today (or the lifetime counters forrange=all_time); the signups, notifications (emails_sent,sms_delivered) and conversions series from the daily rollups. They now match the dashboard. Scoped requests still count live. from/togiven as bare dates now mean those whole days in the shop's time zone. Before, both were read as midnight UTC, soto=2026-09-30left out all of 30 September.- Series buckets are now bucketed in the shop's time zone and
dateis the bucket's first day ("2026-06-01"). Before, every shop was bucketed in UTC anddatewas a timestamp.
- Fix:
channelon the summary and conversions reports never matched an attributed order (orders store the message class,EmailMessage); it now filters them. - New:
range=all_timeon all five reads.sourceon the summary and the three series. Summary addsunique_customers,pending_by_channelandlast_notified_at. The notifications series addsemails_sentandsms_deliveredand acceptsmarket_id. The conversions series addsunique_customers.products_in_demandacceptsgroup_by=product. - Who it affects: every caller of the back-in-stock reports and their CSV exports.
- Source: quinn-reads-v2-step7
2026-09-27 — Account products: search by product
- What changed:
GET /account/productsacceptsmatch(variant, the default, orproduct). Withmatch=product,searchreturns every variant of each product it matched, not only the variants that matched (for example a SKU search returns the product's other sizes too). - Who it affects: callers of that read. Existing requests work as before.
- Source: quinn-reads-v2-step6
2026-09-27 — Offer variants: title sort, live inventory and filters
- What changed:
GET /preorders/offers/{id}/products/variants:- New params:
sort(attached, the default, ortitle),exclude_discarded=true,include_inventory=true, and the filtersinventory(zero_or_beloworpositive),inventory_policy(denyorcontinue),product_tag,exclude_product_tag. - Each row adds
variant_title,sku,discarded,shipping_text_shown(value,source,per_variant_allowed: the text the customer sees) andeffective_max_count(the limit that applies; with batches on it is the sum of the kept batches,nullwhen unlimited). - With
include_inventoryor any filter, each row addsinventory(inventory_quantity,continue_selling_enabled,preorder_button_eligible) andproduct_tags, and the response addsinventory_includedandinventory_note. Filters apply to each page after the Shopify read: the response addsscannedandunreadable, and a page can hold fewer rows thanper_page. If Shopify cannot be read, a filtered page returns no rows andfilter_error. per_pagemaximum is now 250 (was 200).
- New params:
- Who it affects: callers of that read. Only extra fields and params.
- Source: quinn-reads-v2-step5
2026-09-27 — Back-in-stock products: signups, sales and a waitlist filter
- What changed:
GET /back_in_stock/productsadds, per row,pending_signups,total_signups,units_sold_via_back_in_stockandback_in_stock_revenue(lifetime, string money like the reports). The response addsshop_alerts_enabled(the shop's master alert toggle) andcurrency. New filterhas_waitlist=truereturns only rows with a pending signup./helplistsvalue_labelsforbutton_visibility. - Who it affects: callers of that read. Only extra fields.
- Source: quinn-reads-v2-step4
2026-09-27 — Offer list expand, and more detail on a variant's offers
- What changed:
GET /preorders/offersacceptsexpand(inventory,shipping,payments,markets,products, comma-separated or an array). Each row then carries those capability blocks in the same shape as the capability reads. An unknown block returns422.GET /preorders/offers/{id}/shipping(and the expandedshippingblock) addsdisplay.default_variant_shipping_text, read-only: the offer-level shipping text a variant without its own override shows in preorder notification emails.- Each
in_preorder_offersentry fromGET /preorders/product_variants/{variant_id}andGET /preorders/product_variants/{variant_id}/offersaddsoffer_internal_name,inventory_providerandshipping_text(value,source,per_variant_allowed: the text the customer sees).
- Fix:
offer_statusin those entries now follows the same rule as the offer's ownstatus.state. A scheduled offer saved as disabled (the editor saves "Starts on" and "Date range" that way) readpaused; it now readsscheduledbefore its start andactiveinside its dates. Thestatefilter on/offersfollows the same rule. - Who it affects: callers of those reads. Only extra fields, except the
offer_statusfix. - Source: quinn-reads-v2-step3
2026-09-27 — Send log: product names, blocked reasons and counts per product
- What changed:
GET /back_in_stock/notificationsandGET /back_in_stock/notifications/{id}add fields to each entry:product_titleandvariant_title: the product the send was for.blocked_reason_codeandblocked_reason_label: why a blocked send did not go out, as a stable code (for exampleplan_limit,email_bounced,sms_country_not_allowed) and plain words.blocked_reasonstill returns the stored text. The full list of codes is in the send log docs.attempted_at: when STOQ tried to send. A blocked send hassent_at: null, so this is its only time.
- New option:
group_by=productonGET /back_in_stock/notificationsreturnssummary(total, sent, blocked, and blocked counts by reason code) andproducts(the same counts per product, highest total first) instead ofnotifications. The other filters still apply.pageandper_pagepage the products. - /help: actions may now list
value_labels, the plain words for raw enum values in their response. The send log lists them forstatusandchannel. - Who it affects: callers of the send log. Existing requests work as before and only see extra fields.
- Source: quinn-reads-v2-bridge
2026-09-27 — The rest of the dashboard settings: storefront text, alert switch, email look, limits, default payment option
- What changed: settings a merchant can change in the dashboard that the API could not write yet.
- New
GET /preorders/settings/storefront_textandPATCH /preorders/settings/storefront_text: the shop-wide preorder words from the offer editor's Advanced section and the preorder translations page. Fields:currency_format,button.out_of_stock_text,button.add_to_cart_text,modal.continue_button_text,modal.cancel_button_text,modal.error_heading. They apply to every preorder offer. The text fields are translated: pass an optionallocaleto read or write one language;currency_formatis one value for all languages. CSS, JavaScript and selectors are not part of it. PATCH /back_in_stock/settings/channelstakesalerts_enabled, the "Send automatic notification" master switch. The read already returned it.PATCH /back_in_stock/settingstakes it aschannels.alerts_enabled.PATCH /account/sendingtakesemail_style:font_family(one of the editor's fonts),header_font_size,description_font_sizeandlogo_size(whole pixels above 0). They apply to every STOQ email.GET /account/sendingreturns them.PATCH /preorders/offers/{id}/limitstakeslimit_per_product, the editor's "Same limit for every product". Each change is copied onto every variant in the offer and replaces their own limits, as the editor does.nullclears it on every variant. The read returns it.PATCH /preorders/offers/{id}/paymentstakesshow_partial_first(true or false), the editor's "Show partial payment option first". Only for an offer infull_and_partialmode whose options have synced to Shopify; otherwise422. The read shows the choice asoptions[].is_default.GET /account/settings/{section_id}has a new section,preorder_storefront_text.
- New
- Who it affects: callers that change these settings. Existing requests work as before.
- Source: decl/dashboard-settings-gaps
2026-09-27 — Preorder pages settings: read and write
- What changed: new
GET /preorders/settings/pagesandPATCH /preorders/settings/pagesfor the dashboard's Preorders > Settings > Pages: whether the Preorder button shows on the home, collection and search pages, whether a product card shows the preorder badge when any variant is on preorder, and the locations and markets those pages count stock at.- Fields:
home.enabled,collection.enabled,collection.show_badge_if_any_variant_is_preorder,search.enabled,locations.filter_enabled,locations.location_ids,market_setup,market_locations_enabled.locations.location_idsreplaces the stored list.market_setupandmarket_locations_enabledare the same settings as inPATCH /back_in_stock/settings/delivery. market_setup: "multiple_markets"turnslocations.filter_enabledoff, and"single_market"turnsmarket_locations_enabledoff, unless the request sets them too, as the dashboard does.- The page's CSS selectors (Dev mode) are not part of the API. An unknown field returns
422. GET /account/settings/{section_id}has a new section,preorder_pages:pages, pluslocationswhen the location filter is on andmarketswhen market-based locations are on.
- Fields:
- Wording: the description of the offer widget's
badge.enabled(PATCH /preorders/offers/{id}/widgetandset_badge_enabled) no longer says the badge sits on the product image. On product pages it sits after the price by default; on home, collection and search pages it shows on the product card where the Preorder button is on. - Who it affects: callers that want to change where the Preorder button shows. Existing requests work as before.
- Source: decl/preorder-pages
2026-09-24 — Writes for integrations, languages, sending and the rest of the dashboard settings
- What changed: every shop-level setting the dashboard lets a merchant change can now be written through the API.
- New
PATCH /account/integrations:klaviyo.sync_customers,omnisend.api_key,omnisend.sync_customers,mailchimp.api_key,mailchimp.list_id,mailchimp.sync_customers,shopify.sync_customers,sync_customers_tag,meta_pixel.enabled,meta_pixel.pixel_id,flow_triggers.enabled,flow_triggers.triggers,split_order_app.enabled. A Mailchimp key is checked by fetching the account's audiences and is not saved when Mailchimp refuses it;mailchimp.list_idmust be one of those audiences.""removes a saved key. Klaviyo still connects with OAuth from the dashboard. - New
PATCH /account/languages:app_locale,multi_language_enabled,product_translations_enabled. Turning multi-language off also turns translated product titles off. Translated titles need the store translations access the dashboard asks for; the call fails until it is granted. - New
PATCH /account/sending:email_provider,sms_provider,email_from_name,use_custom_email_address,email_reply_to_address,sms_custom_domain_enabled,klaviyo_skip_auto_subscribe,postscript_api_key. The custom address cannot be on a free mail domain.use_custom_email_address: falseputs STOQ's address back as the reply-to. A Postscript key is checked with Postscript and not saved when Postscript refuses it. PATCH /back_in_stock/settings/widgetaddsplacement.selectors(product,collection,collection_insertion,collection_container,home,home_container,search,search_container),placement.hide_by_tag(enabled,tags,hides_button),form.inline(enabled,selector,insertion),optin.app_proxy_path_prefixandcustom(css,javascript).button.positionnow also takes"specific". Reads returncustom_css_presentandcustom_js_present, not the code.PATCH /back_in_stock/settings/deliverynow writesmarket_setup,market_locations_enabledandalert_on_publish.market_setup: "multiple_markets"turnslocations.filter_enabledoff and"single_market"turnsmarket_locations_enabledoff, unless the request sets them too.PATCH /back_in_stock/settings/alertsaddsmerchant_emails.product_updatesandmerchant_emails.theme_published.- New
POST /preorders/offers/{id}/payments/set_deposit_amountwithamount: a fixed deposit charged at checkout on the partial payment option. It clears the deposit percentage.
- New
- Credentials:
omnisend.api_key,mailchimp.api_keyandpostscript_api_keyare write only. No read returns them; reads returnconnectedflags. - Read changes:
GET /account/integrationsaddsmeta_pixel,flow_triggersandsplit_order_app.GET /account/sendingaddsklaviyo_skip_auto_subscribe. - Who it affects: callers that want to change these settings. Existing requests work as before.
- Source: decl/writes-settings
2026-09-24 — Template writes cover every field the template editor changes
- What changed: the template update actions now write every field the dashboard's template editor lets a merchant change. Each is read and written under the same name.
PATCH /back_in_stock/templates/{stage}/{locale}addsemail_section_color,email_border_radius,email_button_border_radius,email_unsubscribe_text,email_reviews_enabled,email_reviews_title,email_reviews_count,email_reviews_min_rating,email_css_configandemail_template(the custom HTML email).email_templateandemail_css_configmay contain HTML; other fields still may not.GETreturnsemail_css_configandemail_templatetoo.PATCH /preorders/templates/{locale}addsemail_background_color,email_section_color,email_text_color,email_border_radius,email_button_background_color,email_button_text_color,email_button_border_radius,email_css_config, the seven social links (email_facebook_link,email_instagram_link,email_pinterest_link,email_twitter_link,email_tiktok_link,email_whatsapp_link,email_youtube_link),email_social_icon_color,email_reviews_enabled,email_reviews_countandemail_reviews_min_rating. The preorder template still does not acceptemail_template_enabled,email_template,email_button_destination,sms_link_destinationoremail_unsubscribe_text: the preorder editor does not show them.- Values are checked the way the editor limits them: colours are hex (
#1A73E8), corner radii are whole pixels,email_reviews_countis 1 to 3,email_reviews_min_ratingis 5, 4 or 3,email_social_icon_coloris#FFFFFFor#000000, andemail_button_destination/sms_link_destinationarecheckoutorproduct. A wrong value returns422. - Social links,
email_css_config,email_template,email_preheaderand the Klaviyo flow names accept an empty string, which clears them. - Shared fields behave as in the editor: on a back-in-stock alert, colours, corner radii and the reviews block are copied to every stage of the alert; social links, the social icon colour and
email_unsubscribe_textare copied to every template of the shop. Plan limits apply as in the editor:email_template_enabled,email_templateand the reviews block need a paid plan.
- Who it affects: callers that manage template styling, custom HTML/CSS or the Judge.me reviews block. Callers that sent an invalid colour, destination or icon colour before now get
422instead of a saved value. - Source: decl/writes-templates
2026-09-24 — enable on a scheduled preorder offer starts it now
- What changed:
POST /preorders/offers/{id}/enableon an offer whosestatus.stateisschedulednow starts the offer now, as the dashboard's Enable does.schedule.startis cleared,schedule.endis kept, and with no end date the schedule is removed. The offer readsactive. Before,enablesetenabledand kept the start date, so the offer still waited for its start date. - Who it affects: callers that enable an offer saved with a future start date ("Starts on" or "Date range").
- What to send instead: to keep the start date, do not call
enable; the offer starts on its own. To move the start date, usePOST /preorders/offers/{id}/schedule. - Source: decl/enable-scheduled
2026-09-23 — GET /account/settings/{section_id} returns the API read shapes; new account reads
- What changed:
settingsinGET /account/settings/{section_id}is now the output of the v2 read actions behind the section, keyed by block, with the same field names as the write actions. Before, it was a hand-built summary with renamed keys and words such as"Yes","Enabled"or"Active". Booleans are nowtrue/false, providers and locales are their stored codes (for example"twilio","fi"), and prose summaries are gone. Each section also returnscolumns(field path to model column) andraw(stored value), andunavailablewhen one of its reads failed. The blocks per section:delivery_settings:delivery, pluslocationsandmarketswhen in use.signup_widget:widget,compliance,channels.notification_channels:channels,sending,alerts,tagging.notification_templates:locale,initial,reminder,signup_thank_you.preorder_notifications:locale,notifications,template.integrations:integrations.languages:languages.delivery_settingsno longer has adefault_languageline; it showed the dashboard language, not the alert language. Uselanguages.default_locale.
- New read-only actions:
GET /account/sending(email and SMS providers, sender, custom sending domain),GET /account/integrations(Klaviyo, Postscript, Omnisend, Mailchimp connection flags and customer sync; never keys or tokens),GET /account/languages(default_locale,app_locale, multi-language),GET /account/locations(Shopify location ids and names). - New read-only fields:
delivery.market_setup,delivery.market_locations_enabled,delivery.alert_on_publish;channels.alerts_enabled;widget.advanced(inline form, button selector, product-tag rule,custom_css_present,custom_js_present); back-in-stock templates addafter_n_days,email_section_color,email_border_radius,email_button_border_radius,email_unsubscribe_text,email_custom_css_present,klaviyo_flow_verified,klaviyo_sms_flow_verified,postscript_sms_flow_verified; the preorder template adds its style, social links, destinations,email_template_enabled, unsubscribe text and the same flow flags; preorder notifications addklaviyo_shipping_update_flow_verifiedandklaviyo_payment_failed_flow_verified. Writes do not accept these fields. - Whose account sends:
GET /account/sendingaddssms_accountandemail_account("stoq"or"merchant") andemail_sender_domain("stoq"or"merchant").sms_provider: "twilio"is STOQ's own SMS service, so itssms_accountis"stoq", as are Telnyx and Infobip. Klaviyo or Postscript is"merchant"only when the merchant saved working credentials; otherwise STOQ sends. Shopify Flow email is"merchant".email_sender_domainis"merchant"when mail goes out from the merchant's DKIM-verified address.GET /preorders/notifications/{locale}addsemail_accountandsms_accounttoo. No credentials are returned. - Editor labels: each section may also return
labels, keyed likecolumns: the editor's label andlabel_id,menu_path,tab,section,group, andvalue_label/value_label_idfor option controls. Only fields of settings areas in the settings catalog have entries; today that is the signup widget. - Preorder offer limits:
PATCH /preorders/offers/{id}/limitswithdisplay.progress_bar.enabled: truenow also turnsdisplay.show_remainingoff, as the editor's "Customer display" radio andset_progress_baralready did. Sendshow_remainingin the same request to keep it. - Preorder offer reads: new fields, read-only:
inventoryaddscontinue_selling.in_effect(STOQ turns on "Continue selling when out of stock" for the offer's variants now; false for a manually picked variant list) andcontinue_selling.pending_until_offer_starts;shippingaddsdelivery.fulfillment_status(unfulfilled,scheduledoron_hold);limitsaddsbatches_enabled;productsaddscollection.namefrom Shopify for a collection-sourced offer. New actionGET /preorders/developer_settings: dev mode and the placement selectors (shop-wide). The offerstatus.stateof a scheduled offer now follows its dates: an offer saved as "Starts on" or "Date range" readsscheduledbefore its start, notpaused. - Back-in-stock products:
PATCH /back_in_stock/productsacceptsper_product_customization_enabled(trueorfalse). Sent on its own, with notargets, it turns the shop's per-product customization on or off. Off is the Products tab's "Disable per-product overrides": products follow the global settings again, and their saved values are kept. Per-product values still needtargets, andfalsecannot be sent with them. - Who it affects: callers of
GET /account/settings/{section_id}. Callers of the other reads only see extra fields, except the scheduled-offerstatus.statechange. - What to send instead: read the new paths. For example the
signup_widgetfieldnotify_me_button_textis nowsettings.widget.button.text. - Source: feat/quinn-setup-from-apiv2
2026-09-24 — market_overrides removed from variant settings
- What changed:
PATCH /preorders/offers/{id}/products/variants/{variant_id}no longer acceptsmarket_overrides. A request that sends it now returns422and changes nothing, even when it also sendsshipping_textormax_count. Before, it wrote the variant's per-market metafields directly. Nothing was saved in STOQ, so the admin could not show the values and later metafield updates overwrote them. - Who it affects: callers that set per-market shipping text or limits on one variant.
- What to send instead: drop
market_overrides. Sendshipping_textandmax_counton their own. To use different values per market, set them on an offer scoped to that market (/preorders/offers/{id}/markets). - Source: this PR
2026-09-22 — Discount value no longer clears the discount
- What changed:
PATCH /preorders/offers/{id}/paymentswith{ "discount": { "value": 25 } }and notypenow keeps the offer's current discount type and changes the amount. Before, it removed the discount and still returned202. - Who it affects: callers that change the discount amount without sending
type. - What to send: nothing new. To remove a discount, send
{ "discount": { "type": "no_discount" } }. - Source: #3955
2026-09-21 — display.show_shipping_timeline removed from preorder shipping
- What changed:
display.show_shipping_timelineis gone fromPATCH /preorders/offers/{id}/shippingand from theshipping.displayblock in offer and shipping reads. The setting has done nothing since the simplified shipping text was retired (#3308). Every offer shows the detailed shipping timeline. - Who it affects: callers that send or read
display.show_shipping_timeline. - What to send instead: drop the field. To change the text shoppers see, use
POST /preorders/offers/{id}/shipping/set_shipping_textordisplay.shipping_text. - Source: commit
7038fe23a
2026-09-21 — Writes with no known setting now fail
- What changed: a v2 write where none of the sent keys is a setting the action can change now returns
422with codeunmapped_settings. The error lists the keys the action accepts. Before, it returned202and changed nothing. - Who it affects: callers that send misspelled or removed field names, for example
display.show_shipping_timeline. - What to send instead: the keys named in the error, or the fields in the action's request schema.
- Source: commit
7038fe23a
2026-09-19 — email_template_enabled on back-in-stock templates
- What changed:
PATCH /back_in_stock/templates/{stage}/{locale}acceptsemail_template_enabled.falsegoes back to the standard STOQ email.trueuses the custom HTML email template. - Who it affects: callers that manage custom HTML alert emails.
- Source: commit
298f0f5fa
2026-09-18 — Back-in-stock templates: stage path segment and new fields
- What changed: the template path is now
/back_in_stock/templates/{stage}/{locale}.stageisinitial,reminderorsignup_thank_you. The old values0and1still work forinitialandreminder. The update action addsenabled(turns the reminder or the signup thank-you on or off), flat colour fields (email_background_color,email_text_color,email_button_background_color,email_button_text_color,email_social_icon_color) and social link fields (email_facebook_link,email_instagram_link,email_pinterest_link,email_twitter_link,email_tiktok_link,email_whatsapp_link,email_youtube_link). - Who it affects: callers of the back-in-stock template actions. Existing
0/1calls keep working. - What to send instead: use the stage names in new code.
- Source: #3894
2026-09-16 — Hold back-in-stock alerts while the store is password protected
- What changed:
PATCH /back_in_stock/settings/deliveryacceptssend_while_password_protected. The default isfalse: alerts are held while the storefront password is on, and signups stay pending for the next restock. - Who it affects: stores with a storefront password. Alerts that used to send during a password period are now held unless you set
send_while_password_protected: true. - Source: #3858
2026-09-16 — Progress bar box background colour
- What changed:
POST /preorders/offers/{id}/limits/set_progress_baracceptsbox_background_color. When it is not set, the box keeps using the disclaimer background colour. - Who it affects: callers that style the preorder progress bar.
- Source: #3874
2026-09-16 — Default-locale writes update the main text
- What changed: a template or translation write for the store's default locale now updates the main text. Before, it was saved as a translation for that locale and the shopper-facing text did not change.
- Who it affects: callers that pass the store's own default locale to template, notification, offer translation or signup widget writes.
- Source: #3861
2026-09-08 — "In-stock" preorder mode only on Specific-products offers
- What changed:
POST /preorders/offers/{id}/inventory/set_providerwithshopify(show Preorder for in-stock products) now returns422on All-products and Collection offers. Before, it silently turned the offer into a Specific-products offer. - Who it affects: callers that set the provider on All-products or Collection offers.
- What to send instead: switch the offer to Specific products first, or keep the
stoqprovider. - Source: #3709
2026-09-02 — Split orders need an explicit split.type
- What changed:
PATCH /preorders/offers/{id}/shippingwithfulfillment.split.enabled: trueand nofulfillment.split.typenow returns422.enabled: falsestill turns splitting off. OnPATCH /preorders/offers/{id}/payments, sendingremaining_balance.auto_collectandremaining_balance.auto_collect_on_fulfillmentboth astruenow returns422. - Who it affects: callers that turn on splitting with
enabledalone, or that set both auto-collect options. - What to send instead:
{ "fulfillment": { "split": { "enabled": true, "type": "orders" } } }(or"fulfillments"). Pick one auto-collect option. - Source: #3628
2026-09-01 — fulfillment.split.type on preorder shipping
- What changed: shipping reads and writes carry
fulfillment.split.type(fulfillments,ordersornull). Split fulfillments and split orders are two separate settings. Before, reads reported one combinedenabledflag. - Who it affects: callers that read or write order splitting.
- Source: #3588
2026-09-01 — New filters and read actions
- What changed:
GET /preorders/offerstakessearch,status,market_idsandsort(#3571).GET /preorders/orderstakessearch(#3576).GET /back_in_stock/signupstakesquery(#3494).- Back-in-stock reports (
summary,signups,conversions,products_in_demand) andGET /preorders/reports/by_datetakemarket_id(#3557). - New reads:
GET /back_in_stock/orders(#3576),GET /back_in_stock/products/{variant_id}/notification_status(#3577),GET /preorders/offer_lookup(#3566),GET /account/settings/{section_id}(#3573),GET /account/templates/{campaign_type}/translations(#3597),GET /account/translations(#3598). GET /preorders/offers/{id}/translationsalso returns each field's default-locale source text and the store's published locales (#3596).
- Who it affects: nobody has to change anything. These are additions.
2026-08-31 — OAuth for the MCP server
- What changed: the MCP server at
/api/v2/external/mcpsupports OAuth 2.1, so MCP clients such as Claude and ChatGPT connectors can connect without pasting an API key (#3551). Clients can register themselves automatically (#3641). OAuth grants now expire, can be revoked, and are bound to the client that got them (#3731). - Who it affects: MCP clients. The
X-Auth-Tokenheader still works. - Source: #3551, #3641, #3731
2026-08-28 — New read actions
- What changed: new reads:
GET /back_in_stock/reports/wait_duration(#3493),GET /account/marketsandGET /account/products(#3494),GET /preorders/orders/{id}/timeline(#3497),GET /account/plan_usage(#3498),GET /back_in_stock/reports/engagementandGET /preorders/reports/engagement(#3501),GET /preorders/products/inventory_status(#3502),GET /account/help_docs(#3503). - Who it affects: nobody has to change anything. These are additions.
2026-08-25 — Paid-plan features enforced on the server
- What changed: v2 and MCP writes now check the store's plan. On a free plan, a write that changes a paid-only setting fails with
422and an error that names the paid feature. Nothing in that write is saved. - Who it affects: integrations on free-plan stores that change paid-only settings.
- What to send instead: leave the paid-only fields out, or upgrade the plan.
- Source: #3372

