Skip to content

Personalized orders

Send different artwork with every order — names, photos or generated designs — by URL or uploaded file, without creating a product first.

For products that are different for every customer (a name on the back of a jersey, a pet photo on a mug, a generated poster), order a catalog variant with inline designs instead of a SKU. You do not need to create a product first.

JSON
{
  "catalog_variant_id": "cv_01k8w1a2s7d9f1g3h5j7k9m1n3",
  "quantity": 1,
  "designs": [
    { "placement": "front", "layers": [{ "file_id": "file_01k8x3y1q3w5e7r9t1y3u5i7o9" }] },
    { "placement": "back", "layers": [{ "url": "https://cdn.example.com/personalized/order-1042-back.png" }] }
  ]
}
  • catalog_variant_id — the exact color/size from GET /catalog/products/{id} (variants[].id).
  • designs — the same format as product designs: one entry per placement, layers with file_id or url, optional position and opacity. Every required placement must have artwork.

Inline items are validated like products (artwork type, size, resolution) and priced like products — the estimate includes extra placements.

Two ways to send the artwork

Upload first (file_id) Send a URL (url)
How POST /files (multipart or { "url": … }) → use the file_… id Put the public image URL in the layer
When it is downloaded During the upload call While we create the order
Errors surface On upload (file_invalid, file_too_large, download_failed, download_not_an_image, …) On order creation: a 400 (download_failed, download_not_an_image, file_invalid, file_download_failed, …) with param = items[i].designs[j].layers[k].url
Best for Retries, large files, reusing artwork Simplicity — one request

URLs must be publicly reachable over http(s) (no login, no short-lived signed URL that might expire before we fetch it) and return the image itself. A link to a web page — a file host's share page, for example — fails with download_not_an_image: use the host's direct download link instead.

  • We follow up to 5 redirects, and retry once when the host answers with a temporary error (a dropped connection or HTTP 429, 502, 503, 504) — so a single CDN hiccup doesn't fail your order.
  • Images are checked before they are decoded: at most 30,000 px on a side and 250 megapixels in total (file_too_large).
  • Photos with an EXIF rotation are stored upright, and CMYK images are converted to RGB, so what you see in the preview is what is printed.
  • Downloading the same image again (for example when you retry an order) reuses the file we already stored instead of keeping a copy.

Example: a name on the back

Generate the personalized image in your backend (any image library), host it or upload it, then order:

curl -X POST https://swaglists.com/api/v1/orders \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: store-order-2087" \
  -d '{
    "external_id": "2087",
    "recipient": { "name": "Sam Lee", "address1": "9 Elm St", "city": "Austin", "state_code": "TX", "postal_code": "78701", "country_code": "US" },
    "items": [{
      "catalog_variant_id": "cv_01k8w1a2s7d9f1g3h5j7k9m1n3",
      "quantity": 1,
      "external_line_id": "line-1",
      "designs": [
        { "placement": "front", "layers": [{ "file_id": "file_01k8x3y1q3w5e7r9t1y3u5i7o9" }] },
        { "placement": "back",  "layers": [{ "url": "https://cdn.example.com/names/sam-lee-23.png",
                                             "position": { "x": 0.1, "y": 0.1, "width": 0.8, "height": 0.3 } }] }
      ]
    }]
  }'

The position above makes the name box 80% of the area wide and 30% high, near the top — make the image's aspect ratio match the box (see keeping the aspect ratio) or omit position to fit it automatically.

Mixing personalized and regular items

One order can contain both SKU items and inline items:

JSON
"items": [
  { "sku": "CA-TEE-BLK-M", "quantity": 2 },
  { "catalog_variant_id": "cv_01k8w1a2s7d9f1g3h5j7k9m1n3", "quantity": 1, "designs": [ … ] }
]

Tips

  • Price check: send the same body to POST /orders/estimate first, or show your customers a fixed price per product and placement.
  • Resolution: generate images at the placement's recommended_width_px (300 DPI); low-resolution artwork is rejected below 75 DPI.
  • Storefront personalization fields: Shopify line item properties and WooCommerce item meta carry the customer's input — render the image from them in your webhook handler, then send the order. See the Shopify recipe.
  • Inline items show product_id: null and sku: null on the order, and designs is exactly what will be printed.
  • Previews: a few seconds after the order is created, mockup_url shows your design on the product, rendered from the order's own designs (it never changes when you later edit a product). Until it is rendered — and in the order.created webhook, which is built when the order is created — mockup_url is the catalog photo of the variant. Fetch the order again (GET /orders/{id}) to get the rendered image; mockup_url links don't expire.