The WHCC API lets your application choose print products, build them with your customers' photos, check pricing, and submit orders with one API key. Start with the Quick Start to walk through the flow, then use these pages for field details and additional options.
The catalog is the source of truth. GET /v1/catalog/products lists every product your key can see, and each one carries a completion field that says whether this API can build it:
"completion": "api"
Build, price and order it through this API with no editor session. Stock products, and anything the simple editor offers, are here. Filter on this value.
"completion": "editor"
Needs a design finished in the full WHCC editor, so this API can't build it yet.
Filter on completion, not on category or name, because the set grows over time. The Quick Start shows the catalog call, and Response Bodies has the full product shape. For a browsable overview of the range, see Products.
A submission is the request you send. WHCC accepts it, then creates one or more orders. Each order can have more than one fulfillment order for production and shipping.
Create an event subscription and WHCC tells you when each order is accepted, rejected, shipped or cancelled; don't poll for it. Save the submission ID to confirm the submission produced orders and to reconcile, and follow each order's actions.fulfillment link when you need its fulfillment orders, production status and shipments. See Submission Lifecycle for how these fit together.
GET /v1/
Check the request you sent: whether it is still being submitted, whether all or some orders were placed, and any placement failures. This route takes the submission ID returned by POST /v1/commerce/submissions.
GET /v1/
Find the fulfillment orders created from a submission. Follow the submission's actions.fulfillment.url, or an individual orders[] entry's link, to get the right filtered list. One link may return several fulfillment orders.
GET /v1/
Read one fulfillment order's current production status, status history, and shipments. Follow items[].links.self from the fulfillment list; the route also accepts a fulfillment order ID or WHCC order number. See Orders & Fulfillment.
https://apis-stage.whcc.com/api
Sandbox. Our test environment: build and test here first, with a sandbox API key.
https://apis.whcc.com/api
Production. Orders submitted here are real, and you'll be billed for them.
The examples on these pages use the sandbox host, so they run as written with a sandbox key. On production, only the host and the key change. Every request carries your API key as a bearer token:
1
2
curl https://apis-stage.whcc.com/api/v1/catalog/products \
-H "Authorization: Bearer <YOUR_API_KEY>"
Send WHCC-API-Version: 2026-10 on every request to stay on a known API version; see API Versions. Keys are issued by WHCC and bound to one account, and sandbox and production keys are separate. See API Key Security for how to store a key, hand one to an AI agent, and get one revoked if it leaks. A missing or unrecognized key is rejected before your request reaches the API; see Errors & Rate Limits for the exact bodies.
GET /v1/discovery lists the operations your key can reach, including each route's method and path. Use it when you begin an integration and when you change keys. A listed route can still reject a request because of its data or the resource you're trying to use.
1
2
curl https://apis-stage.whcc.com/api/v1/discovery \
-H "Authorization: Bearer <YOUR_API_KEY>"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"contract": "whcc.discovery",
"contractVersion": "1",
"generatedAt": "2026-10-01T14:43:08.164Z",
"grantedScopes": ["apikey"],
"semantics": ["…"],
"sources": [
{ "service": "prospector", "status": "fresh", "generatedAt": "2026-10-01T14:43:08.164Z" },
{ "service": "fulfillment", "status": "fresh", "generatedAt": "…", "fetchedAt": "…" }
],
"links": {
"service-desc": "https://apis.whcc.com/api/swagger.json",
"service-doc": "https://apis.whcc.com/api/documentation"
},
"operations": [
{
"service": "prospector",
"method": "GET",
"path": "/api/v1/catalog/products",
"scopes": { "anyOf": ["apikey"] },
"tags": ["api", "authenticated", "catalog"],
"description": "Lists the products your organization can sell",
"notes": ["…"],
"writes": false
},
{ "…": "trimmed" }
]
}
grantedScopes
The scopes your key carries. A partner key carries apikey.
environment
The environment that answered, such as dev.
semantics[]
Plain sentences on how to read the list. A listed operation only means your key's scopes admit the route. The route can still refuse the call, or need a product or resource your account doesn't have.
sources[]
One entry for each service the list was built from, such as prospector and fulfillment, with its status and when its listing was generated.
links
service-desc (the machine-readable API description) and service-doc (the Swagger UI). These are absolute URLs. Each operation also has its own links.service-desc, which is a path on the host (such as /api/v1/fulfillment/openapi.yaml) for Fulfillment routes and an absolute URL for the others.
operations[]
One entry for each route your key can call. path is the full path on the host, so it starts with /api, unlike the paths on these pages, which are relative to the base URL.
operations[]
The service behind the route (prospector or fulfillment) and labels for grouping. tags is empty on Fulfillment routes.
operations[]
The scopes that open the route. anyOf needs one of the listed scopes. Your key can call the route when apikey is in the list.
operations[]
writes is true when a successful call changes something, such as building a product or placing a submission. irreversible: true marks a change that can't be undone, such as canceling an order. Reads and calculate carry writes: false.
operations[]
What the route does, for people to read. Don't parse them: the wording can change.
Discovery describes access for the key you present; Swagger UI shows request and response schemas. Discovery can list a route these pages don't cover. Build on the routes documented here. If Discovery returns 403 for your key, ask WHCC which routes are available to it.
Explore the API in Swagger UI: production · sandbox.
Many responses include an actions object. Each action names a useful next request and supplies its url and method; some also include instructions. These links carry the right product reference or order filter for the response you're reading, so you can follow them as you move through the API.
actions.detail
From a catalog result to that product's full details.
actions.create
From a product to the call that builds a configured product.
actions.fulfillment
From a submission or one of its orders to the matching fulfillment orders.
When a response carries an action, its URL is absolute, on the host you called, and it is the URL to use: don't join it to the base URL. Catalog, build, poll and submission responses all carry them. Calculate and the submission read have no action pointing at them: for those two, build the URL from the base and the documented path. An action can point at a route your key can't call, and Discovery tells you which ones it can. When an action carries instructions, they tell you what that request needs. To check product readiness, follow actions.poll as shown in Quick Start Step 4. Once the items are ready, calculate with your shipping details to check the price and validate the submission. See the product detail and submission examples.
Discover products in the catalog, build each one as a configured product, poll until it's ready, check the price with calculate, then submit. After that, the fulfillment routes read, cancel and re-address the orders, and an event subscription tells you when each one changes.
GET /v1/
List the product catalog for your account. Quick Start Step 1 · response body
GET /v1/
One product's full details, including configuration.fields[] for choosing options. Quick Start Step 2 · response body
POST /v1/
Build. Create a priced product from your selections and images. It is a draft, not an order. Once every item is ready, its actions.submit points at the submit route below.
GET /v1/
Poll. Check whether the configured products you built are ready to submit. Polling never creates anything; up to 50 ids per call. Quick Start Step 4
POST /v1/
Get a link that opens a configured product in a WHCC editor. The link is a credential. Quick Start Step 4
POST /v1/
Submit. Starts a real, billable order. A submission that validates answers 202. See Submission Lifecycle. shipping options · metadata & reference
POST /v1/
Price and check a basket without submitting it. Calculate doesn't create anything or place an order. response body
GET /v1/
List your submissions as short rows for lookups and reconciliation. response body
GET /v1/
One submission in full, the same shape POST /v1/commerce/submissions also returns. response body
GET /v1/
List the orders a submission produced. Follow the submission's actions.fulfillment link to get the right filtered list. Orders & Fulfillment
GET /v1/
One order's production status, status history and shipments. Orders & Fulfillment
POST /v1/
Begin canceling an order; a second call completes it with the token this one returns. Canceling an order
PUT /v1/
Change where an order ships and which option carries it, with a preview route to price the change first. Changing where an order ships
POST /v1/
Register a URL that WHCC calls when an order is accepted, rejected, shipped or canceled. This is how you follow order status. Event Subscriptions
metadata map's limits and the 32-character reference that reaches fulfillment paperwork and the shipping label.submission.* code with what to do about it, and how to handle throttling.Idempotency-Key header, following order status, and what each submission status means.