Quickstart
From an API key to your first order in five steps.
Use this guide to verify your credentials, read a product, create an order, and track it through shipping. Each request is part of a typical production integration.
Get your store ID and API key
Open your custom store in the Pengine app, enable the Store API, and copy the store ID and API key. You need both values for every request.
export PENGINE_STORE_ID="ovy3u54frpsl4sd963aja0h1"
export PENGINE_API_KEY="pgn_qfL0dZrwqXYuwvlt4nYlB0ytL78Uv4PU"The key is a server-side secret. It reads every product on the store and creates orders you are billed for, so keep it out of browsers and public repositories.
Check that it works
The status endpoint returns 200 only when the key matches the store in the path. Use it in your
deployment checks.
curl https://api.pengine.io/store-api/2026-08/stores/$PENGINE_STORE_ID/status \
-H "X-API-Key: $PENGINE_API_KEY"{
"status": "ok",
"message": "Pengine Store API is up and running!",
"version": "2026-08",
"store": { "id": "ovy3u54frpsl4sd963aja0h1" },
"checkedAt": "2026-08-19T12:37:20.829Z"
}A wrong key, a wrong store, or a mismatched pair is a 401:
{ "message": "Invalid API key", "error": "Unauthorized", "statusCode": 401 }Read your products
Product responses include the variants, SKUs, prices, colors, sizes, and mockup images needed to render a storefront.
curl "https://api.pengine.io/store-api/2026-08/stores/$PENGINE_STORE_ID/products?filter[status]=active&limit=20" \
-H "X-API-Key: $PENGINE_API_KEY"{
"items": [
{
"id": "saryo4m9p6ki3u1558m6h56j",
"name": "Midweight Hoodie",
"status": "active",
"colors": ["Black", "White"],
"sizes": ["S", "M", "XL"],
"variants": [
{
"id": "ak88bwfjjqn5cjt5jaczmn1n",
"sku": "PGN70872789915240044115",
"color": "Black",
"size": "S",
"price": 166.8,
"cost": 130.7,
"isDiscontinued": false,
"position": 1
}
],
"images": [
{
"id": "zkk2lkpxredez88sw0ljgwia",
"color": "Black",
"image": {
"id": "kl1v18gmjnd420uziof6ecv0",
"url": "https://cdn.pengine.io/files/kl1v18gmjnd420uziof6ecv0/Midweight-Hoodie-Front-Black.png"
},
"position": 1
}
]
}
],
"totalItems": 1,
"startItem": 1,
"endItem": 1,
"totalPages": 1,
"query": { "page": 1, "limit": 20, "filter": { "status": ["active"] }, "sort": { "createdAt": "desc" } }
}Every status is returned
Without a filter you get drafts and archived products too. This API is server to server, so it hands
your backend every product and lets you control what the public sees. Only active and unlisted
products can be ordered.
Create an order
Each line item identifies a variant by productVariantId or sku and sets a quantity. Set
externalOrderId to your order number so retries cannot create duplicate orders.
curl -X POST https://api.pengine.io/store-api/2026-08/stores/$PENGINE_STORE_ID/orders \
-H "X-API-Key: $PENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalOrderId": "web-10482",
"lineItems": [
{ "productVariantId": "ak88bwfjjqn5cjt5jaczmn1n", "quantity": 2 }
],
"shippingAddress": {
"firstName": "Ada",
"lastName": "Lovelace",
"email": "ada@example.com",
"phone": "+12025550176",
"address": {
"streetAddress": "1600 Pennsylvania Avenue NW",
"apartmentOrSuite": "Suite 4",
"cityOrTown": "Washington",
"stateOrProvince": "DC",
"zipOrPostalCode": "20500",
"country": "US"
}
}
}'{
"id": "p948dnb4qbo2d86i2mx67svp",
"name": "#127505",
"externalOrderId": "web-10482",
"status": "pending",
"fulfillmentStatus": "pending",
"paymentStatus": "due",
"totalQuantity": 2,
"subtotalCost": 261.4,
"shippingCost": 17.85,
"totalCost": 279.25,
"orderedAt": "2026-08-19T12:37:20.896Z",
"createdAt": "2026-08-19T12:37:20.897Z"
}Store the returned id with your own order record. Later API responses and webhooks refer to this ID.
A 409 means you already sent this externalOrderId. Fetch the existing order instead of creating
another. See Errors for the retry rules.
The costs are yours, not your buyer's
Everything named cost on an order is what Pengine bills you, shipping included. What your customer
paid belongs to your storefront and does not appear in the Store API.
Track the order
An order starts with a pending status. Fetch it at any time to check its progress:
curl https://api.pengine.io/store-api/2026-08/stores/$PENGINE_STORE_ID/orders/p948dnb4qbo2d86i2mx67svp \
-H "X-API-Key: $PENGINE_API_KEY"Once it ships, trackingInfo appears on the same response:
{
"status": "fulfilled",
"fulfillmentStatus": "fulfilled",
"trackingInfo": {
"carrier": "USPS",
"trackingNumber": "9400100000000000000000",
"trackingUrl": "https://tools.usps.com/go/TrackConfirmAction?tLabels=9400100000000000000000"
},
"fulfilledAt": "2026-08-22T16:11:04.113Z"
}Register an orders_update webhook and refetch only the order that changed. See
Webhooks.
Next
Products
Every field on a product, variant and image, and what the statuses mean.
Orders
The full create body, the status ladder, and what happens after you send one.
Errors
What each failure looks like and which calls are safe to retry.
Rate limits
How much traffic the API takes and how to back off politely.