Multiple placements
How it works
A product is configured on two axes, and you pass a value for each. You pick a variant — size, color — with one opt.<attr> param per attribute (opt.size=m); see Options. And you supply a design — your artwork — by filling each printable placement with asset.<key> (asset.front=…).
Both are readable params any language builds by hand — no ids to look up, no SDK required. The server resolves price, print geometry, and composites the design into one mockup. One design is one buyable item; a single-image product (get started) is just one placement with bare asset=.
Discover the product
Read the product once from the catalog. Placements are the design axis — each has an addressable key — a URL-safe slug of the label (lowercase, non-alphanumerics collapsed to _, so “Left sleeve” → left_sleeve) — a human label, and a type (image or color). Attributes and attributesList are the variant axis — build pickers from them; combinations carries each price. You pass your picks as opt.<attr>; you never pass an internal id.
{
"id": "KMYKUK",
"name": "Women's Tee",
"placements": [
{ "key": "front", "label": "Front", "type": "image" },
{ "key": "back", "label": "Back", "type": "image" },
{ "key": "left_sleeve", "label": "Left sleeve", "type": "image" },
{ "key": "right_sleeve", "label": "Right sleeve", "type": "image" },
{ "key": "collar", "label": "Collar", "type": "image" }
],
"options": {
"attributesList": ["Size"],
"attributes": {
"Size": { "choices": [{ "label": "S" }, { "label": "M" }, { "label": "L" }] }
},
"combinations": [
{ "Size": "S", "price": 2799 },
{ "Size": "M", "price": 2799 },
{ "Size": "L", "price": 2799 }
]
}
}Render a mockup
It’s a URL: a product code, your opt.<attr> picks, one asset.<key> per placement, and your shop. Build it in any language — it’s just a query string.
<img src="https://img.snowcone.app/KMYKUK?opt.size=m&asset.front=https%3A%2F%2Fcdn.example.com%2Ffront.png&asset.back=https%3A%2F%2Fcdn.example.com%2Fback.png&shop=YOUR_SHOP_ID" />const params = new URLSearchParams({
"opt.size": "m",
"asset.front": "https://cdn.example.com/front.png",
"asset.back": "https://cdn.example.com/back.png",
shop: "YOUR_SHOP_ID",
});
const src = `https://img.snowcone.app/KMYKUK?${params}`;curl -L "https://img.snowcone.app/KMYKUK?opt.size=m&asset.front=https%3A%2F%2Fcdn.example.com%2Ffront.png&asset.back=https%3A%2F%2Fcdn.example.com%2Fback.png&shop=YOUR_SHOP_ID" -o mockup.pngfrom urllib.parse import urlencode
params = {
"opt.size": "m",
"asset.front": "https://cdn.example.com/front.png",
"asset.back": "https://cdn.example.com/back.png",
"shop": "YOUR_SHOP_ID",
}
src = "https://img.snowcone.app/KMYKUK?" + urlencode(params)<?php
$params = [
'opt.size' => 'm',
'asset.front' => 'https://cdn.example.com/front.png',
'asset.back' => 'https://cdn.example.com/back.png',
'shop' => 'YOUR_SHOP_ID',
];
$src = 'https://img.snowcone.app/KMYKUK?' . http_build_query($params);require "uri"
params = {
"opt.size" => "m",
"asset.front" => "https://cdn.example.com/front.png",
"asset.back" => "https://cdn.example.com/back.png",
"shop" => "YOUR_SHOP_ID",
}
src = "https://img.snowcone.app/KMYKUK?" + URI.encode_www_form(params)import "net/url"
q := url.Values{}
q.Set("opt.size", "m")
q.Set("asset.front", "https://cdn.example.com/front.png")
q.Set("asset.back", "https://cdn.example.com/back.png")
q.Set("shop", "YOUR_SHOP_ID")
src := "https://img.snowcone.app/KMYKUK?" + q.Encode()Prefer JavaScript? The optional SDK builds the same URL:
import { getMockupUrl } from "@snowcone-app/sdk";
const src = getMockupUrl("KMYKUK", {
shop: "YOUR_SHOP_ID",
options: { size: "m" },
design: { front: "https://…/front.png", back: "https://…/back.png" },
});Adjust each placement
A placement is filled by an image (asset.<key>) or, for a color placement like a cap’s crown, a solid color (color.<key>) — plus optional modifiers, keyed the same way.
<!-- Tile the front, top-align the back — modifiers are more params, same placement key. -->
<img src="https://img.snowcone.app/KMYKUK?opt.size=m&asset.front=…&tile.front=2&asset.back=…&align.back=top&shop=YOUR_SHOP_ID" />asset.<key>urlcolor.<key>hextile.<key>0.25 | 0.5 | 1 | 2 | 4align.<key>center | top | bottom | left | right | far-*Sell it
Swap img → buy and the same params become a hosted checkout for one line item carrying every placement. A self-contained HTML form needs no JavaScript; price resolves server-side from your Shop ID.
<form action="https://buy.snowcone.app/KMYKUK" method="POST">
<input type="hidden" name="asset.front" value="https://cdn.example.com/front.png" />
<input type="hidden" name="asset.back" value="https://cdn.example.com/back.png" />
<input type="hidden" name="shop" value="YOUR_SHOP_ID" />
<select name="opt.size">
<option value="s">S</option>
<option value="m" selected>M</option>
<option value="l">L</option>
</select>
<button type="submit">Buy now — $28</button>
</form><a href="https://buy.snowcone.app/KMYKUK?opt.size=m&asset.front=…&asset.back=…&shop=YOUR_SHOP_ID">
Buy now — $28
</a>Bring your own checkout
Running your own checkout? The Orders API line item takes the same two maps as JSON — options and design, keyed by key. Modifiers become fields on the fill ({ src, tile }) instead of a bare URL.
curl https://api.snowcone.app/orders -X POST \
-H "x-api-key: sk_your_orders_add_key" \
-H "Content-Type: application/json" \
-d '{
"json": {
"reference": "order-123",
"items": [
{
"product": "KMYKUK",
"options": { "size": "m" },
"design": {
"front": "https://cdn.example.com/front.png",
"back": "https://cdn.example.com/back.png"
},
"quantity": 1
}
],
"shipTo": {
"name": "Ada Lovelace", "address1": "5 Bell Yard",
"city": "London", "postalCode": "WC2A", "country": "GB"
}
}
}'
