Products API
Customer products are your own SKUs built on top of a base product — a base garment plus your designs, tags, and retail configuration. Each customer product is scoped to your account and invisible to other customers.
Base URL: https://developer.allthe.com/api/v1
Required scopes: products:read, products:write
Endpoints
| Method | Path | Scope | Status |
| ------ | ------------------ | ---------------- | ------ |
| GET | /products | products:read | Stable |
| GET | /products/{id} | products:read | Stable |
| POST | /products | products:write | Stable |
| PATCH | /products/{id} | products:write | Stable |
| DELETE | /products/{id} | products:write | Stable |
Products you create here behave exactly like ones built in the allthe customer portal: order them by id, and each line is decorated from the designs stored on the product.
Personalised products are portal-only. Products whose buyer fills in text or photos at checkout carry a template that only the builder can author. The API creates fixed-design products, and
PATCHon a personalised product returns409 conflict.
List products
GET /products
Lists all your non-archived customer products.
| Name | Type | Description |
| -------- | ------- | ------------------------------------------------ |
| limit | integer | Page size, 1–100. Default 25. |
| cursor | string | Product id from previous next_cursor. |
curl "https://developer.allthe.com/api/v1/products?limit=5" \
-H "Authorization: Bearer pat_live_..."
{
"data": [
{
"id": "ckcp1...",
"name": "Summer '26 Heavyweight Tee",
"description": "Our signature heavyweight, new print for summer.",
"status": "active",
"tags": ["summer-26", "tees"],
"retail_price": 34.00,
"base_product": {
"id": "ckl9...",
"master_sku": "G6400-BLK",
"title": "Gildan Softstyle T-Shirt"
},
"mockup_url": "https://cdn.allthe.com/mockups/summer26-tee.jpg",
"print_areas": [
{ "print_area": "Front", "method": "DTG", "position": { "x": 0.5, "y": 0.5, "scale": 1, "rotation": 0 } }
],
"variants": [
{ "id": "ckv1...", "blank_variant_sku": "G6400-BLK-M", "colour": "Black", "colour_hex": "#0A0A0A", "active": true }
],
"created_at": "2026-03-10T09:00:00.000Z",
"updated_at": "2026-04-02T14:23:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}
Fetch a product
GET /products/{id}
Returns one customer product in the same shape as the list response data[]. Returns 404 not_found if the product does not exist or belongs to another account.
mockup_url expires
Product images live in private storage, so mockup_url is a signed URL valid
for one hour from the moment the response was generated. Render it or copy the
bytes; don't persist the URL itself and expect it to work tomorrow — fetch the
product again for a fresh one. It is null when the product has no mockup.
Create a product
POST /products
Builds a product on a base product from the catalogue. Artwork is supplied as URLs we fetch server-side — the same model as order intake, so you never upload file bytes to us.
| Field | Type | Required | Description |
| --------------------- | -------- | -------- | --------------------------------------------------------------------------- |
| base_product_id | string | Yes | Catalogue product id (see Catalog). |
| name | string | Yes | Product name, max 200 chars. |
| variant_skus | string[] | Yes | Blank variant SKUs to offer. Must belong to the base product and be active. |
| description | string | No | Max 5000 chars. |
| retail_price | number | No | Your retail price. Recorded for your own reporting; it does not affect what allthe charges you. |
| tags | string[] | No | Up to 30 tags. |
| mockup_url | string | No | Product image URL. Fetched and stored; becomes the line mock on orders. |
| decoration.areas | array | No | Up to 10 { print_area, method, artwork_url, position? } — the designs printed on this product. |
| status | string | No | active (default) or draft. |
position is optional per area: { x, y, scale, rotation }. x/y are
fractions of the print area (0.5, 0.5 = centred, the default), scale is a
multiplier, rotation is degrees.
curl -X POST "https://developer.allthe.com/api/v1/products" \
-H "Authorization: Bearer pat_live_..." \
-H "Content-Type: application/json" \
-d '{
"base_product_id": "ckl9...",
"name": "Summer '\''26 Heavyweight Tee",
"retail_price": 34.00,
"tags": ["summer-26"],
"variant_skus": ["G6400-BLK-M", "G6400-BLK-L"],
"decoration": {
"areas": [
{
"print_area": "Front",
"method": "DTG",
"artwork_url": "https://cdn.example.com/summer26-front.png"
}
]
}
}'
Returns 201 with the product in the same shape as GET /products/{id}.
Validation is all-or-nothing. Every artwork URL is fetched and checked, and
every SKU and (print_area × method) pair resolved, before anything is
written. A single bad URL or unknown area returns 422 validation_failed and no
product is created — a half-decorated product would look correct in the response
and print wrong.
Artwork must pass the same checks as order intake — https only, public hosts, PNG/JPEG/WebP, size and dimension caps. See Artwork & decoration for the full table.
Re-using the same artwork is free. Designs are deduplicated by content: post the same bytes twice and the second product references the existing design rather than creating a new one. That also means you are not re-charged the one-off setup fee for artwork you have already printed.
Update a product
PATCH /products/{id}
Every field is optional; send only what you're changing. Accepts the same fields
as create, except base_product_id — a product cannot be moved to a different
base garment (create a new product instead).
curl -X PATCH "https://developer.allthe.com/api/v1/products/ckcp1..." \
-H "Authorization: Bearer pat_live_..." \
-H "Content-Type: application/json" \
-d '{ "retail_price": 36.00, "tags": ["summer-26", "restock"] }'
Two behaviours worth knowing:
decoration.areasreplaces the decoration wholesale. The areas you send are the product's decoration afterwards; any area you omit is removed. This matches the builder — a partial merge would leave a stale area printing artwork you thought you had deleted.variant_skusreplaces the offered variants, but SKUs you drop are deactivated, not deleted, because placed orders still reference them. Re-sending a dropped SKU reactivates it.
Returns 404 not_found for a product that isn't yours, 409 conflict for an
archived or personalised product.
Archive a product
DELETE /products/{id}
Returns 204 with no body. The product stops appearing in GET /products and
can no longer be edited or ordered.
This is an archive, not a delete — placed orders reference the product for
their artwork and naming, so the record is retained. The call is idempotent:
archiving an already-archived product also returns 204.
Ordering a product you created
Pass the product id, a variant SKU, and the areas you want printed:
{
"items": [
{
"product_id": "ckcp1...",
"variant_sku": "G6400-BLK-M",
"quantity": 2,
"print_jobs": [{ "area": "Front", "method": "DTG" }]
}
]
}
The artwork stored on the product is snapshotted onto the order line — you do
not re-send artwork_url at order time.
Always send
print_jobslisting every area you want printed. It is what the line is billed for, and it's how production enumerates the areas. Omitting it on a decorated product bills a single area, which will not match a multi-area product. Sendprint_jobs: []only for a deliberately blank garment.
Use print_areas from the product to build print_jobs — not the base
product's catalogue areas. GET /catalog/products/{id}/print-areas lists every
area the garment supports; print_areas on the product lists the ones this
product actually has artwork on. Ordering an area that isn't in print_areas
returns 422 validation_failed, because it would bill you for decoration that
doesn't exist and attach the wrong artwork.
See Orders for the full order body.