Pengine Store API
Endpoints

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.

EndpointWhat it does
GET /statusConfirms the API is up and your key works for this store.
GET /catalog-productsLists the blanks available to the store.
GET /catalog-products/:catalogProductIdReads one blank, with its decoration methods and print areas.
GET /productsLists the store's products, filtered, sorted and paged.
GET /products/:productIdReads one product, with its variants, colors, sizes, and images.
POST /artworksUploads artwork from an HTTPS URL.
GET /artworksLists the store's artworks.
GET /artworks/:artworkIdReads one artwork.
POST /ordersCreates an order from line items and a shipping address.
GET /ordersLists the store's orders, filtered, sorted and paged.
GET /orders/:orderIdReads 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

HeaderWhen
X-API-KeyEvery request. See Authentication.
Content-Type: application/jsonRequests 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:

200 OK
{
  "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.

404 Not Found
{
  "message": "Order not found",
  "error": "Not Found",
  "statusCode": 404
}
CodeMeans
400Malformed, failed validation, or asked for something that cannot be ordered.
401The key is missing or wrong for this store, or the store is inactive.
404No such route, or no such resource in this store.
409An order already exists with this externalOrderId.
429Past the rate limit.
500A 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.

On this page