Product variants
The product-variants resource is the variant-centric inverse of the offer's Products capability — instead of asking "which variants does this offer contain?" you ask "which offers is this variant in?", plus run administrative actions that compose with the variant's Shopify metafields.
URLs use the Shopify variant ID, not Stoq's internal UUID. This is intentional — most callers already have the Shopify variant ID and don't carry Stoq IDs around.
Path prefix: /preorders/product_variants
Reads
| Method | Path | Description |
|---|---|---|
GET | /preorders/product_variants | List Shopify variants attached to any preorder offer in this shop. Paginated. |
GET | /preorders/product_variants/:variant_id | Read a Shopify variant's preorder context: every offer it's in, aggregate counts, and current metafield state. |
GET | /preorders/product_variants/:variant_id/offers | List the preorder offers a Shopify variant is attached to. Accepts a state filter. |
The list endpoint supports page / per_page pagination plus offer_id, product_id, and variant_ids filters. The response key is product_variants:
{
"product_variants": [ { "shopify_variant_id": 123456, "...": "..." } ],
"meta": { "total_count": 80, "page": 1, "per_page": 25, "total_pages": 4 }
}The detail endpoint returns the same context the admin UI uses to render the variant detail page — current counts, metafield drift detection, etc.
Per-offer variant settings (shipping-text and max-count overrides) live on the offer side: see the products/variants sub-resource.
Single-variant administration
These actions compose with Shopify metafield writes. They're the same operations the merchant admin offers behind the "Reset" / "Recalculate" buttons on the variant detail page.
| Method | Path | Description |
|---|---|---|
POST | /preorders/product_variants/:variant_id/recalculate_preorder_count | Recompute preorder_count from active order line items, then push to Shopify. |
POST | /preorders/product_variants/:variant_id/reset_preorder_count | Hard-reset preorder_count metafield + DB columns to zero. |
POST | /preorders/product_variants/:variant_id/sync_metafields | Force-resync every preorder metafield from local DB state to Shopify. Idempotent. |
POST | /preorders/product_variants/:variant_id/reset_metafields | Clear all preorder-related Shopify metafields on a variant. Doesn't detach from offers. |
POST | /preorders/product_variants/:variant_id/detach_from_all_offers | Remove a variant from every preorder offer it's attached to. Customer orders are unaffected. |
When to use which
recalculate_preorder_count— use when count has drifted from reality. Recomputes from real orders.reset_preorder_count— destructive zero. Use only when you need to manually start counting from scratch.sync_metafields— use after a transient Shopify outage to push DB state back into Shopify metafields.reset_metafields— clears Shopify metafields but keeps the offer attachment. The next offer-settings change rewrites them from current state.detach_from_all_offers— destructive; equivalent to the offer'sproducts/remove_variantsagainst every offer the variant is in.
Bulk administration
The bulk variants of the above actions submit a background job and return 202 Accepted with a job_id / status_url / variant_count. Cap is 5000 variants per submission.
| Method | Path | Description |
|---|---|---|
POST | /preorders/product_variants/bulk_recalculate_preorder_counts | Recompute preorder_count for many variants in one job. |
POST | /preorders/product_variants/bulk_reset_metafields | Reset preorder metafields for many variants in one job. |
POST | /preorders/product_variants/bulk_sync_metafields | Force-resync preorder metafields for many variants in one job. |
Request body
{ "variant_ids": [123456, 789012, 345678] }variant_ids are Shopify variant IDs.
Polling
GET /preorders/product_variants/jobs/:job_idReturns the standard async-job envelope. See Bulk & Async Jobs for the full polling pattern.
