Products & SKUs
Create products from catalog products and your designs, give variants your own SKUs, map them to your store and export them to Shopify or WooCommerce.
A product is what you sell: a catalog product + your designs + a set of variants, each with a SKU. When your store sells a variant, you send us its SKU in an order — that is the whole mapping.
product "Company A Logo Tee" (prod_…)
├── catalog_product_id cp_… (Classic Unisex Tee)
├── designs front: [file_… at {x, y, width, height}]
└── variants pvar_… SKU CA-TEE-BLK-M → catalog variant Black / M price 1299
pvar_… SKU CA-TEE-BLK-L → catalog variant Black / L price 1299Products, variants and SKUs are shared between test and live mode.
Create a product
curl -X POST https://swaglists.com/api/v1/products \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"catalog_product_id": "cp_01k8w1a2s3d4f5g6h7j8k9m0n1",
"title": "Company A Logo Tee",
"description": "Our classic logo on a soft tee.",
"designs": [
{ "placement": "front", "layers": [{ "file_id": "file_01k8x3y1q3w5e7r9t1y3u5i7o9" }] }
],
"variants": [
{ "catalog_variant_id": "cv_01k8w1a2s7d9f1g3h5j7k9m1n3", "sku": "CA-TEE-BLK-M", "retail_price": 2900 },
{ "catalog_variant_id": "cv_01k8w1a2s9f1g3h5j7k9m1n3p5", "sku": "CA-TEE-BLK-L", "retail_price": 2900 }
],
"external_id": "shopify-product-9921"
}'const res = await fetch("https://swaglists.com/api/v1/products", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `product-${myProduct.id}`,
},
body: JSON.stringify({
catalog_product_id: "cp_01k8w1a2s3d4f5g6h7j8k9m0n1",
title: "Company A Logo Tee",
designs: [{ placement: "front", layers: [{ file_id: "file_01k8x3y1q3w5e7r9t1y3u5i7o9" }] }],
variants: "all", // every available catalog variant, SKUs generated
retail_price: 2900,
}),
});
const product = await res.json();
for (const v of product.variants) console.log(v.sku, v.title, v.price);product = requests.post(
"https://swaglists.com/api/v1/products",
headers={"Authorization": f"Bearer {os.environ['API_KEY']}", "Idempotency-Key": f"product-{my_product_id}"},
json={
"catalog_product_id": "cp_01k8w1a2s3d4f5g6h7j8k9m0n1",
"title": "Company A Logo Tee",
"designs": [{"placement": "front", "layers": [{"file_id": "file_01k8x3y1q3w5e7r9t1y3u5i7o9"}]}],
"variants": "all",
"retail_price": 2900,
},
).json()
for v in product["variants"]:
print(v["sku"], v["title"], v["price"])| Field | Notes |
|---|---|
catalog_product_id |
From GET /catalog/products. |
designs |
At least one; every required placement must be covered. See Designs. |
variants |
"all" (every available catalog variant) or a list of { catalog_variant_id, sku?, retail_price?, external_id? }. Default "all". |
retail_price |
Your selling price in cents — informational (used in exports), never charged. |
external_id, tags, metadata |
Your references. external_id is unique among your products. |
status |
active (default) or draft. Drafts can still be ordered. |
The response is the product with its variants and a warnings array (resolution or placement issues that did not block creation). Mockups are rendered in the background; product.updated fires when they are ready (mockups[]).
Each variant's price is your wholesale price for one unit of this product with all its designs — what an order line will cost before shipping and tax.
Availability
Each variant's available says whether it can be ordered right now: the variant is enabled, the product isn't archived, and the item is in stock. Variants can become temporarily unavailable (out of stock, discontinued); orders that include them are rejected with 400 item_unavailable until they are back. Whenever available changes we send product.updated (with the previous values in previous_attributes.variants) and notify you in the dashboard (plus an email when variants become unavailable) — mark those variants sold out in your store, and back in stock when the next product.updated says so.
Mockup images
mockups[].url links don't expire, so you can use them directly as product images in your store or headless storefront. Each URL belongs to one rendering: when you change the designs or the enabled colors, the mockups are re-rendered, the new URLs arrive with product.updated, and the previous URLs stop working (404). Update your listing from that event (or re-fetch the product) instead of keeping old URLs — or download the images into your store, as the Shopify and WooCommerce imports do.
SKUs
SKUs connect your store to us. Rules:
- 1–64 printable ASCII characters (no leading/trailing spaces), unique in your account across all products.
- Omit
skuand we generate a readable one from the product title and the variant options (e.g.QUICKSTART-BLACK-M; a-2,-3, … suffix is added on collisions). - Disabled variants and archived products keep their SKUs reserved.
Already have SKUs in your store? Use them — set sku when creating the product, or change it later:
curl -X PATCH https://swaglists.com/api/v1/products/prod_01k8x3z7r1t3y5u7i9o1p3a5s7/variants/pvar_01k8x3z7r4f6g8h0j2k4l6z8x0 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "sku": "STORE-TEE-BLACK-M", "external_id": "shopify-variant-4410" }'Look a variant up by SKU with GET /products/by_sku/{sku} (URL-encode special characters).
Mapping your store's products
| Your store has… | Do this |
|---|---|
| No products yet | Create products here (API or designer), then export them to your store. The CSV carries our SKUs. |
| Products with SKUs | Create matching products here with variants: [{ catalog_variant_id, sku: "<store SKU>" }]. |
| Products without SKUs | Add our generated SKUs to your store's variants (Shopify and WooCommerce have a SKU field per variant). |
Order line items whose SKU is not one of your products return 400 resource_missing with param = items[i].sku; skip non-print items (gift cards, digital goods) before sending the order.
Update, add variants, archive
| Request | What it does |
|---|---|
PATCH /products/{id} |
Change title, description, tags, metadata, external_id, retail_price, status, or replace all designs (mockups are re-rendered). |
POST /products/{id}/variants |
Add catalog variants (or re-enable disabled ones): { "variants": "all" } or a list. |
PATCH /products/{id}/variants/{variant_id} |
Change a variant's sku, retail_price, external_id or enabled. |
DELETE /products/{id} |
Archive: it can no longer be ordered; SKUs stay reserved; existing orders are unaffected. Restore with PATCH { "status": "active" }. |
Archived products cannot be edited (409 product_archived — product fields, designs and variants) until restored: send PATCH /products/{id} with a status (other changes may be included in that same request). Orders keep a snapshot of the designs and prices at order time, so later product changes never affect orders already placed.
Exports
Export products as a CSV ready to import into your store: in the dashboard open Products → Export and choose:
| Format | Import it in | Notes |
|---|---|---|
| Shopify CSV | Shopify admin → Products → Import | One row per variant; SKU = our SKU; Fulfillment service = manual; mockups as images; prices = your retail prices. |
| WooCommerce CSV | WooCommerce → Products → Import | Variable products with one variation per variant; SKU = our SKU; weights in your chosen unit. |
| CSV | Spreadsheets, other platforms | One row per variant with ids, SKU, options, retail price and image URL. |
Select products in the list to export only those. See Product catalog export & import for the column mapping and how to keep your store in sync.