Endpoints
How requests and responses work for every Store API resource.
Every endpoint is scoped to the custom store ID in the path. The API key must belong to that store. A
resource ID from another store returns 404.
| Endpoint | What it does |
|---|---|
GET /status | Confirms the API is up and your key works for this store. |
GET /catalog-products | Lists the blanks available to the store. |
GET /catalog-products/:catalogProductId | Reads one blank, with its decoration methods and print areas. |
GET /products | Lists the store's products, filtered, sorted and paged. |
GET /products/:productId | Reads one product, with its variants, colors, sizes, and images. |
POST /artworks | Uploads artwork from an HTTPS URL. |
GET /artworks | Lists the store's artworks. |
GET /artworks/:artworkId | Reads one artwork. |
POST /orders | Creates an order from line items and a shipping address. |
GET /orders | Lists the store's orders, filtered, sorted and paged. |
GET /orders/:orderId | Reads one order, with its costs, statuses and tracking. |
Paths are relative to https://api.pengine.io/store-api/2026-08/stores/:storeId.
Making a request
| Header | When |
|---|---|
X-API-Key | Every request. See Authentication. |
Content-Type: application/json | Requests with a body. |
curl https://api.pengine.io/store-api/2026-08/stores/$STORE_ID/products/saryo4m9p6ki3u1558m6h56j \
-H "X-API-Key: $PENGINE_API_KEY"Unrecognized request fields are ignored and are not stored.
Responses
A single-resource endpoint returns the object with no response wrapper. Empty fields are omitted
instead of returned as null, so handle missing fields as empty values.
A list endpoint returns the rows in items alongside the counts and the query that produced them:
{
"items": [],
"totalItems": 16,
"startItem": 1,
"endItem": 2,
"totalPages": 8,
"query": { "page": 1, "limit": 2, "filter": {}, "sort": { "createdAt": "desc" } }
}Pagination uses page numbers. limit defaults to 50 and has a maximum of 100. Write filters and sorts as
filter[field] and sort[field]. See Lists for the full syntax.
Errors
Failures share one envelope. message is a string, or an array of strings when several fields fail
validation at once.
{
"message": "Order not found",
"error": "Not Found",
"statusCode": 404
}| Code | Means |
|---|---|
400 | Malformed, failed validation, or asked for something that cannot be ordered. |
401 | The key is missing or wrong for this store, or the store is inactive. |
404 | No such route, or no such resource in this store. |
409 | An order already exists with this externalOrderId. |
429 | Past the rate limit. |
500 | A failure on Pengine's side. |
Use statusCode, rather than message, for programmatic error handling. See
Errors for what is safe to retry.
Rate limits
Each response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. A 429
carries Retry-After instead, in seconds, and nothing in that request was processed. See
Rate limits.
Status
Check that your key works for this store.
Catalog products
The blanks, their decoration methods and their print areas.
Products
Every field on a product, variant and image, and what the statuses mean.
Artworks
Upload artwork by URL and read it back.
Orders
The create body, the status ladder, and reading an order back.