Home
Integrations

Preorder API for developers

Manage preorder offers, products, and shipping updates from your own code.

The STOQ preorder API lets developers build custom integrations: create and update offers, attach or remove variants, enable and disable offers, manage per-variant settings, and send bulk shipping-update emails, all without touching the STOQ dashboard. Actions you take through the API show up for customers right away, for example enabling an offer makes the preorder button appear on your storefront.

Use this article if you're integrating STOQ preorders with your own tools, scripts, or a Shopify Flow setup that goes beyond the built-in flow actions.

Before you begin

RequirementDetails
API keyGenerated automatically per shop. Find it in the Integrations sub-tab (Preorders in the left menu → Settings tab → Integrations); see Set it up below.
Content typeAll requests and responses are JSON. Include Content-Type: application/json on write requests.
Shopify variant IDsUse the numeric Shopify variant ID (not a GID string) when the API asks for shopify_variant_id or variant_ids.

Choose your API version

STOQ exposes two external API versions. All paths in this article are relative to the base path for the version you use.

  • v2 (/api/v2/external): the current version. Use it for preorder offer management, creating, configuring, and managing the lifecycle of offers, attaching variants, and configuring widget and shipping settings. Recommended for all new integrations. See the API v2 guide and API v2 reference.
  • v1 (/api/v1/external): use it to maintain an existing integration built on the older selling-plan model for preorder offers. See the API v1 reference.

Every endpoint below is listed in those references with full parameters, response schemas, and a Test it button you can call with your own key. Start at the developer docs home to pick an integration path.

What customers will see

Enabling or disabling an offer through the API changes whether the preorder button appears on your storefront, but it doesn't send customers any emails or SMS. Customer notifications, like shipping updates, are sent separately: from the order management endpoints, Shopify Flow, or the send_shipping_update action, which emails customers directly with the subject, header, and description you provide.

Set it up

1

Open Integrations

  1. In the left menu bar of the STOQ app, click Preorders.
  2. Click the Settings tab.
  3. Open the Integrations sub-tab.
2

Locate the API section

Scroll to the API section. Your API key is displayed in the Access key field (shown as a password field).

3

Copy the key

Click Copy to copy the key to your clipboard. Keep this key secure: it grants full access to your shop's STOQ data. See API Key in the developer docs.

4

Reset the key if needed

If you suspect the key has been exposed, click Reset. Existing integrations using the old key will stop working immediately; update them with the new key.

Authentication

Pass your API key in the X-Auth-Token request header on every call:

X-Auth-Token: <your-api-key>

The MCP endpoint (see below) also accepts Authorization: Bearer <your-api-key> for clients that only support standard Authorization headers.

A missing or invalid key returns 401 Unauthorized.

v2 API: preorder offers

The v2 API is action-driven. Each action declares a path, HTTP verb, human-readable description, and a set of natural-language aliases. You can discover the full live manifest at any time:

GET /api/v2/external/help

Scope the manifest to preorder actions only:

GET /api/v2/external/help?prefix=/preorders/offers

You can also fetch a machine-readable skill document (no auth required) that AI agents can ingest:

GET /api/v2/external/preorders/skill.md

Offer lifecycle

MethodPathWhat it does
GET/preorders/offersList all offers (paginated, up to 200 per page).
GET/preorders/offers/:idFull offer representation including all capability blocks.
POST/preorders/offersCreate a new offer. Pass name and optionally internal_name.
POST/preorders/offers/:id/enableActivate the offer. Pass update_inventory_policy: true to also flip attached variants to CONTINUE in Shopify.
POST/preorders/offers/:id/disablePause the offer. Pass update_inventory_policy: true to flip variants back to DENY.
POST/preorders/offers/:id/discardSoft-delete the offer. Recoverable via restore.
POST/preorders/offers/:id/restoreRestore a discarded offer to paused state.
POST/preorders/offers/:id/scheduleSet a launch window. Pass start and/or end as ISO 8601 timestamps. Scheduling does not enable the offer automatically.

Lifecycle state machine. The enable, disable, discard, restore, and schedule actions validate the transition. An invalid transition returns 409 Conflict with a message naming the current state and what would unblock it.

Capabilities

Each capability has a GET to read its current state and a PATCH for catch-all updates (deep partial, only the fields you send are written). Some capabilities also expose named POST actions for specific, high-frequency operations.

CapabilityGETPATCHNamed POST actions
Widget/preorders/offers/:id/widget/preorders/offers/:id/widgetset_button_text, set_badge_text, set_badge_colors, set_button_colors, set_disclaimer_text, set_disclaimer_style, set_billing_widget_text
Shipping/preorders/offers/:id/shipping/preorders/offers/:id/shippingset_delivery_asap, set_delivery_date, set_delivery_window, set_shipping_text, set_split_order_tag, set_split_transaction_gateway
Payments/preorders/offers/:id/payments/preorders/offers/:id/paymentsset_mode, set_deposit_percent, set_discount, set_discount_text, set_option_copy, enable_option, disable_option
Limits/preorders/offers/:id/limits/preorders/offers/:id/limitsset_min_per_order, set_max_per_order, set_max_per_customer, set_total_max
Checkout/preorders/offers/:id/checkout/preorders/offers/:id/checkoutset_terms_text, set_mixed_cart_error_message, set_order_tags, add_order_tag, remove_order_tag
Inventory/preorders/offers/:id/inventory/preorders/offers/:id/inventoryset_provider, set_reservation_timing
Markets/preorders/offers/:id/markets/preorders/offers/:id/marketsadd_market, remove_market, set_markets
Countdown/preorders/offers/:id/countdown/preorders/offers/:id/countdownset_mode, set_style, set_custom_end_date, set_starts_text, set_ends_text, set_unit_labels
Integrations/preorders/offers/:id/integrations/preorders/offers/:id/integrations(boolean toggles only, use PATCH)
Translations/preorders/offers/:id/translationsn/aset, set_many, unset, clear_locale
Advanced/preorders/offers/:id/advanced/preorders/offers/:id/advancedset_button_text_override, set_custom_css, set_custom_line_item_property, attach_to_shopify_selling_plan, detach_from_shopify_selling_plan
Products/preorders/offers/:id/productsn/aadd_variants, remove_variants, bulk_toggle_inventory_policy

Products and variant management

MethodPathWhat it does
GET/preorders/offers/:id/productsRead the product type rule and a variant count summary.
POST/preorders/offers/:id/products/add_variantsAttach variants to a custom-source offer. Pass variant_ids as an array of Shopify variant IDs. Custom-source offers only.
POST/preorders/offers/:id/products/remove_variantsRemove variants from a custom-source offer.
POST/preorders/offers/:id/products/bulk_toggle_inventory_policyFlip attached variants' Shopify inventory policy to CONTINUE or DENY. Async, runs in a background job. Omit variant_ids to apply to all attached variants.
GET/preorders/offers/:id/products/variantsPaginated list of attached variants with their per-variant settings.
GET/preorders/offers/:id/products/variants/:variant_idPer-variant settings for one attached variant.
PATCH/preorders/offers/:id/products/variants/:variant_idUpdate per-variant overrides: shipping_text, max_count, and market_overrides (market-scoped shipping text and max count). Send null to clear an override and fall back to the offer-level default.

MCP server

If you prefer tool calling over raw HTTP, the v2 API is also available as an MCP server. Every registered action is exposed as an MCP tool:

POST /api/v2/external/mcp

Authenticate with X-Auth-Token: <key> or Authorization: Bearer <key>. See the MCP server docs.

v1 API: preorder offers (selling plans)

The v1 external API also exposes a selling-plan-level CRUD surface for preorder offers. Use the v2 API for new integrations; this surface remains available for existing integrations.

MethodPathWhat it does
GET/api/v1/external/preordersList enabled offers. Filter by name. Paginated.
GET/api/v1/external/preorders/:idSingle offer. Includes shopify_variant_ids.
POST/api/v1/external/preordersCreate an offer. product_variants_source is restricted to "custom" via the API.
PUT/PATCH/api/v1/external/preorders/:idUpdate offer settings. product_variants_source is restricted to "custom" via the API.
DELETE/api/v1/external/preorders/:idDisable and soft-delete the offer.
POST/api/v1/external/preorders/:id/add_variantAdd variants by Shopify variant ID array.
DELETE/api/v1/external/preorders/:id/remove_variantRemove variants. Optionally pass inventory_policy (CONTINUE or DENY) to update Shopify inventory policy on removal.
PATCH/api/v1/external/preorders/:id/update_variant_settingsSet shipping_text and/or preorder_max_count for one variant on this offer.
POST/api/v1/external/preorders/:id/bulk_toggle_inventory_policyBulk-update Shopify inventory policy for a list of variants. Async (batches of 1,000 variant IDs).
GET/api/v1/external/preorders/:id/product_variantsVariant list for this offer. Filter by shopify_product_id or shopify_variant_id.
GET/api/v1/external/preorders/:id/variant_countCount of variants attached to this offer.
GET/api/v1/external/preorders/:id/job_statusStatus of any running background job for this offer (e.g. bulk inventory policy update).
DELETE/api/v1/external/preorders/:id/cancel_jobCancel a running background job.
GET/api/v1/external/preorders/jobsAll active background jobs for this shop.
POST/api/v1/external/preorders/send_shipping_updateSend a custom shipping update email to a list of preorder orders. Requires order_ids or shopify_order_ids, plus subject, header, and description. Async.

Rate limits

Both v1 and v2 use a cost-weighted token bucket, scoped per API key. Full details in Rate Limits.

Request typeCost
GET (read)1 point
POST / PATCH / PUT / DELETE (write)2 points
Bucket40 points per 60 seconds (default)

Every response includes rate-limit headers:

X-RateLimit-Limit: 40
X-RateLimit-Remaining: 38
X-RateLimit-Reset: 1751500860

If you exceed the limit you receive 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.

Check your setup

Call GET /api/v2/external/help with your X-Auth-Token header. A 200 response with the action manifest confirms your key and header are set up correctly; a 401 means the header name, key, or format is wrong.

Troubleshooting

You get 401 Unauthorized

Check that you're sending X-Auth-Token (or Authorization: Bearer for the MCP endpoint) with a valid key. Reset and re-copy the key from Integrations if you're not sure it's current.

You get 404 Not Found

The offer, variant, or resource doesn't exist for this shop, or the v2 action path isn't registered. Check GET /api/v2/external/help for the current list of valid paths.

You get 409 Conflict on enable, disable, discard, restore, or schedule

The requested state transition isn't valid from the offer's current state. Read the errors array, it names the current state and what would unblock the transition.

You get 422 Unprocessable Entity

A validation failed. Read the errors array for the specific reason (v2 returns an array; v1 returns a single error string).

You get 429 Too Many Requests

You've exceeded the rate limit. Wait the number of seconds in the Retry-After header before retrying.

You called bulk_toggle_inventory_policy but Shopify's inventory policy hasn't changed yet

The update runs asynchronously and can take several minutes on large variant lists. Poll GET .../job_status (v1) or the jobs endpoint to track progress.

Limitations

Storefront enforcement of the per_customer.max and total.max limit fields may not be fully active. Test in a non-production environment before relying on them as hard caps.

FAQ

Should I use v1 or v2 for a new preorder integration?

Use the v2 API for new integrations. It exposes:

  1. A richer action surface with natural-language aliases.
  2. A self-describing /help manifest.
  3. An MCP server endpoint.
  4. Per-capability read/write endpoints that mirror the dashboard's structure.

The v1 selling-plan endpoints remain available for existing integrations.

Why does my v1 create/update call fail with 'product_variants_source is restricted to custom'?

The v1 API enforces product_variants_source: "custom" only. You cannot create collection-based or all-products offers via the API, use the STOQ dashboard for those offer types. Once the offer exists in the dashboard you can manage its custom-variant list (add/remove) via the API.

Does enabling or disabling an offer via the API notify customers?

No. Enabling or disabling an offer changes whether the preorder button is visible on your storefront; it does not send any notification emails or SMS to customers who have already placed preorders. Customer notifications (confirmation, shipping update) are triggered separately through the order management endpoints or Shopify Flow.

What happens to existing preorder orders when I discard an offer via the API?

Discarding an offer soft-deletes it (sets discarded_at).

  1. STOQ removes the Shopify selling plan group, detaches variants, and resets variant metafields.
  2. Existing customer orders already placed against this offer are not affected: they remain in Shopify and STOQ continues to track them.
  3. You can restore the offer via POST .../restore.
Can I use the API to send a shipping update email to all customers on a preorder?

Yes, via the v1 endpoint POST /api/v1/external/preorders/send_shipping_update.

  1. Pass an array of STOQ order IDs (order_ids) or Shopify order IDs (shopify_order_ids), along with a subject, header, and description.
  2. The emails are sent asynchronously via a background job.
  3. You cannot pass both order_ids and shopify_order_ids in the same call.

See also the preorder shipping update email article for how these emails are templated.