Response Bodies

Version 1.5 · Updated 2026-10-07

Example responses for the catalog, submissions, and calculate, with what each field means. Use them alongside the Quick Start as you build.

The examples come from real responses, shortened to the fields discussed here. A field marked Ignore exists for WHCC's own tools and may change without notice.

GET /v1/catalog/products

The products your account can order. A framed print from a filtered response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
	"products": [
		{
			"ref": "framed-print",
			"name": "Framed Print",
			"completion": "api",
			"actions": {
				"detail": { "url": "https://apis-stage.whcc.com/api/v1/catalog/products/framed-print", "method": "GET" }
			},
			"description": "Present images in remarkable, handcrafted frames",
			"category": { "ref": "wall-art", "name": "Wall Art" },
			"kind": "wall-art",
			"resolution": 300,
			"sizedBy": ["size"]
		}
	],
	"totalCount": 1
}

ref

The product's identifier. Store it, and pass it as productId everywhere else, including GET /v1/catalog/products/{ref}. A ref is case-sensitive: send it exactly as returned, since Framed-Print or the product's display name returns 404. Use the ref on every route, and don't rely on any other id being accepted.

name, category, kind

What to show your customer and how to group products. category.ref is a token to compare, not to parse. New kind values can appear over time; treat one you don't recognize as a product you don't handle yet.

completion

"api" means you can build this product entirely through the API. "editor" means a person has to finish its design in a WHCC editor before it can be ordered.

width, height, aspect, resolution, bleed

The finished size in inches, its shape (portrait, landscape or square), the image resolution it needs, and the print bleed. These are optional, and most list rows carry none of them. When width and height are missing, the size usually depends on a selection you haven't made yet, and the product's detail names which one in sizedBy. A product with no sizedBy and no resolution, such as an accessory or a sample set, has no printed size at all.

quantity

How many can be ordered at once, when the product lists a quantity range. step counts up from min; a quantity outside the range or step is rejected, not rounded.

actions.detail

The link to this product's full detail. Follow it rather than building the URL yourself. The url is absolute, like every action URL. See Follow links between calls.

totalCount, nextCursor

How many products match, and the cursor for the next page. Pass nextCursor back as the cursor query parameter; it's missing on the last page. Treat the cursor as opaque, and keep category and q the same while you page: a cursor sent with different filters returns 400. The 400 has no code. Its title says what was wrong: cursor is malformed (which is also what you get for a cursor that was edited or cut short) or cursor was issued for a different set of filters. A cursor expires after 24 hours and returns 410. Either way, start again without one. You can change limit between pages.

Optional query parameters: limit (page size, 1 to 200), cursor, category (a category.ref) and q (matches part of the product name, ignoring case, up to 128 characters). q searches name only, not ref or description. A category that matches nothing returns 200 with an empty list. A parameter sent empty, sent twice, or not in this list returns 400.

Which products you can build. Stock products, and any product the simple editor offers, are available now and carry completion: "api". Products that need the full editor carry completion: "editor" and can't be built through this API yet. The catalog tells you which is which, so filter on completion rather than on category.

GET /v1/catalog/products/{ref}

One product in full: the fields above, plus configuration.fields[], every selection this product accepts. Real products have long lists of fields and values, so this example is trimmed:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
{
	"ref": "framed-print",
	"name": "Framed Print",
	"completion": "api",
	"description": "Present images in remarkable, handcrafted frames",
	"category": { "ref": "wall-art", "name": "Wall Art" },
	"kind": "wall-art",
	"resolution": 300,
	"sizedBy": ["size"],
	"configuration": {
		"fields": [
			{
				"field": "size",
				"name": "size",
				"required": true,
				"kind": "enum",
				"values": [
					{ "id": "8x10", "name": "8×10", "enabled": true, "width": 8, "height": 10 },
					{ "id": "11x14", "name": "11×14", "enabled": true, "width": 11, "height": 14 },
					{ "…": "trimmed" }
				],
				"default": "8x10"
			},
			{
				"field": "surface",
				"name": "print surface",
				"required": true,
				"kind": "enum",
				"values": [
					{ "id": "photo_print", "name": "photo print", "enabled": true },
					{ "id": "fine_art_print", "name": "fine art print", "enabled": true },
					{ "id": "fine_art_canvas", "name": "fine art canvas", "enabled": true },
					{ "id": "metal_print", "name": "metal print", "enabled": true }
				],
				"default": "photo_print"
			},
			{
				"field": "photo_print",
				"name": "Paper",
				"required": false,
				"kind": "enum",
				"values": [
					{ "id": "lustre_photo_print", "name": "lustre", "enabled": true },
					{ "id": "fuji_deep_matte_photo_print", "name": "deep matte", "enabled": true }
				],
				"default": "lustre_photo_print"
			},
			{ "…": "trimmed" }
		]
	},
	"actions": {
		"create": {
			"url": "https://apis-stage.whcc.com/api/v1/commerce/configured-products",
			"method": "POST"
		}
	}
}

description

A short marketing line for the product, ready to show your customer. Optional: many products don't have one.

sizedBy

Which field(s) in configuration.fields[] decide the product's physical size. Absent on products with no printed size.

configuration.fields[]

The dictionary for your selections: each entry's field is a key you send, and each values[].id is a value for it. name and an optional description are for display. Key your code on id, never on name: two values in one field can share a name (a frame can list several mouldings called "black"), and the same field can be named differently from one product to the next. default is a suggested value; send every applicable choice rather than assuming a default will be filled in. Values shown here may not work in every combination. Step 4 checks the complete set; choose options in Step 3.

fields[].kind

enum is a list of choices in values[]. Show a picker, and grey out any value with enabled: false. Size values also carry width and height in inches. dimension is a custom size: it carries unit and constraints (a min and max for each of width and height) instead of values[], so show two number inputs. Quick Start Step 3 shows the request. Treat a kind you don't recognize as a field you can't offer yet.

actions.create

Where to build the configured product from your selections and images. The create response includes product pricing.

The submission

POST /v1/commerce/submissions and GET /v1/commerce/submissions/{submissionId} return the same shape, so one parser handles both. The 202 from a fresh submit is the same shape with status: "submitting" and no orders key yet. This example is a submission that has reached placed:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
{
	"id": "<submissionId>",
	"status": "placed",
	"reference": "PO-90188",
	"createdAt": "2026-09-24T20:50:31.518Z",
	"accountId": "<yourAccountId>",
	"items": [
		{
			"configuredProductId": "CF_bCy42bhE4xpJXXNezD2EQSkpAzrqBurvi8ICmrzmBTetrum",
			"label": "Framed Print",
			"primaryDisplayName": "8×10 Framed Print",
			"secondaryDisplayName": "",
			"quantity": 1,
			"unitPrice": { "value": "76.45", "code": "USD" },
			"totalPrice": { "value": "76.45", "code": "USD" },
			"productId": "6aLYwjRKYk8XQ4MZk",
			"previewUrl": "https://…",
			"previews": [
				{
					"key": "main",
					"label": "8×10 Framed Print",
					"previewUrl": "https://…",
					"scale_256": "https://…",
					"scale_512": "https://…",
					"scale_1024": "https://…",
					"scale_2048": "https://…"
				}
			],
			"selectionsSummary": [
				{ "id": "size", "label": "size", "name": "size", "description": "8×10" },
				{ "id": "moulding", "label": "moulding", "name": "Moulding", "description": "Woodland 3/4\" walnut frame" },
				{ "…": "trimmed" }
			],
			"isValidForOrder": true,
			"links": { "designSpaceProof": "https://…", "placementProof": "https://…" },
			"metadata": {}
		}
	],
	"orders": [
		{
			"confirmationId": "<confirmationId>",
			"status": "order_placed",
			"actions": { "fulfillment": { "url": "https://apis-stage.whcc.com/api/v1/fulfillment/orders?sourceConfirmationId=<confirmationId>", "method": "GET" } }
		}
	],
	"totalPrice": {
		"value": "91.26",
		"code": "USD",
		"total": { "value": "84.40", "code": "USD" },
		"subtotals": {
			"product": { "value": "76.45", "code": "USD" },
			"packaging": {
				"label": "",
				"value": "0.00",
				"code": "USD",
				"items": [ { "label": "Framed Print", "value": 0 } ]
			},
			"shipping": {
				"label": "Economy",
				"value": "7.95",
				"code": "USD",
				"items": [
					{
						"label": "Framed Print",
						"name": "Framed Print",
						"value": "7.95",
						"code": "USD"
					}
				]
			},
			"surcharges": { "minimum": null, "value": "0.00", "code": "USD" },
			"discount": null,
			"salesTax": {
				"value": "6.86",
				"code": "USD",
				"isExempt": false,
				"items": [ { "name": "Framed Print", "value": "6.86", "code": "USD" } ]
			},
			"minimumSurcharge": null
		}
	},
	"validation": {
		"isValidForSubmit": true,
		"isValidDiscount": null
	},
	"metadata": {},
	"instructions": "",
	"shippingAddress": {
		"attn": null,
		"name": "John Doe",
		"email": null,
		"country": "US",
		"addr1": "3005 Eagandale Place",
		"addr2": null,
		"city": "Eagan",
		"state": "MN",
		"zip": "55121",
		"zipType": null,
		"phone": null
	},
	"guidance": [
		{ "code": "submission.fanout.multiple_orders", "message": "A single submission can be split into more than one WHCC order; each has its own order number and confirmation id and ships and tracks independently. Do not assume a one-to-one mapping between a submission and a WHCC order." },
		{ "code": "submission.pricing.total_excludes_tax", "message": "totalPrice.value includes tax; totalPrice.total.value is the pre-tax amount. Bill the customer totalPrice.value." },
		{ "…": "trimmed" }
	],
	"actions": { "fulfillment": { "url": "https://apis-stage.whcc.com/api/v1/fulfillment/orders?sourceOrderId=<submissionId>", "method": "GET" } },
	"shippingType": "dropship",
	"shippingPriority": "dropship_lowest_cost",
	"packagingType": null
}

Submission fields

id

The submission ID. Save it to look up this request with GET /v1/commerce/submissions/{submissionId} or share it with WHCC support.

status

Where the submission is on its way to becoming a shipped order. See Submission Lifecycle for all seven values, including partially_failed.

createdAt

When WHCC accepted the submission, as an ISO 8601 timestamp in UTC.

orders[]

Read this, not status alone. One entry for each WHCC order this submission produced. An entry that failed carries a failure object (see Errors). Each entry has confirmationId, orderNumber, status (order_placed, order_received, order_shipped, order_canceled, order_failed and so on), shippingInfo (once it ships), and actions.fulfillment. An entry that failed has only status and failure, with no confirmationId and no actions. The key is missing until the submission reaches WHCC. orderNumber is the WHCC order number. It appears once WHCC has received the order (order_received or later) and the key is absent before that. The fulfillment order carries the same number, and GET /v1/fulfillment/orders/{orderNumber} reads an order by it.

totalPrice

Read the two totals carefully. totalPrice.value is the grand total including tax; totalPrice.total.value is the total before tax. The price isn't locked in, so the amount placed can differ from what you saw at submit. subtotals.salesTax has three states. Normally it is money. When the address is complete but tax can't be worked out yet (a country other than the US or Canada, an APO/FPO address, Canada without a province or zip, or a state or zip WHCC can't place), salesTax.value is the string "TBD" and the two totals match. For Alaska and the US territories it is 0.00. When a US address is missing its state or zip, or either is too short, salesTax is null or absent and totalPrice.value is the pre-tax amount. Check for both before parsing money.

totalPrice.subtotals

The breakdown: product, packaging, shipping, surcharges, discount, salesTax and minimumSurcharge. Each is money or null, apart from the salesTax states above. packaging, shipping and salesTax also carry a label and an items[] split by line item. shipping can carry a description too, such as "Actual Postage". When a minimum-order surcharge applies, minimumSurcharge and surcharges.minimum hold the same object: the amount plus an items[] naming the product group it applies to. It depends on the shipping priority as well as the items, so the same item can carry it at one priority and not at another. It is already included in surcharges.value and the totals.

subtotals.surcharges.delivery

Present when a destination fee applies: the Minnesota or Colorado retail delivery fee, or the Canada import surtax. It is money plus an items[] naming the products it applies to, and it is already included in surcharges.value and the totals. The Canada surtax can be a large share of the order, so show it to your customer. The key is absent when no fee applies. It can also appear on a build response's totalPrice.

money

Money is { "value": "76.45", "code": "USD" }, and value is a string. One exception: subtotals.packaging.items[].value is a number with no code.

validation

isValidForSubmit tells you whether the submission can be placed. On calculate with a partial address, invalidParams[] names each missing field, such as shipping.address.zip. Ignore isValidDiscount.

accountId

Your WHCC account, the one your key belongs to.

reference, metadata, instructions

What you sent, returned as-is. instructions is an optional free-text note that is sent to WHCC with the order. Calculate accepts and returns it, but places nothing. metadata is {} when you didn't send one; instructions and reference come back as "" when unset, and as null if you sent null. Calculate enforces no length limit on instructions. See Metadata & Reference.

shippingAddress

The address the order ships to. Fields you didn't send come back as null. The shipTo on the fulfillment routes is a different shape: it has no email, and if you sent no phone it shows the account's phone number rather than null. See Changing where an order ships.

shippingType, shippingPriority, packagingType

The shipping type and method WHCC is actually using, and the packaging choice (null for the default). Check these to confirm you got the shipping you asked for. An order can ship in more than one package; for what actually ships, follow each orders[] entry's actions.fulfillment. See Shipping.

guidance[]

Plain-language notes about this response that aren't errors, such as a reminder that one submission can become several WHCC orders. Each is { code, message }. The codes you'll see are listed under Guidance codes; new ones can appear, so show or ignore one you don't recognize.

actions.fulfillment

A { url, method: "GET" } link to the fulfillment orders created from this submission. The submission-level link covers every order it produced; each orders[] entry has its own link for just that order. The link returns a list ({ "items": [...], "pagination": {...} }), which can be empty for a little while after placement or contain several fulfillment orders. Follow an item's links.self to read its production status and shipments. See which order endpoint to use.

Item fields

configuredProductId

The configured product you built and submitted. Match response items to what you sent by this ID. productId names the catalog product, so it repeats when two items use the same product. Items can come back in any order.

label, primaryDisplayName, secondaryDisplayName

Names ready to show your customer: the product's name (label), a fuller description such as "8×10 Framed Print", and an optional second line (often "").

quantity, unitPrice, totalPrice

How many, the price of one, and the line total. The unit price can drop as the quantity rises, so the calculate response, not the build response, is the price to show.

productId

WHCC's ID for the underlying product.

previews[]

Rendered previews of the finished item, in order; the first is the primary. Empty until rendering completes. Each has a stable key (such as main, front_cover, or inside) and a human-readable label, plus image URLs at four widths (scale_256 to scale_2048) for thumbnails and full views. Each element also carries its own previewUrl: a convenience alias for that element's scale_1024, null when that width has not rendered.

pricedAs

Custom-size products only. When the dimensions you sent were rounded up to the next priced size, this is the size you were charged for, as { "width", "height" } in inches and in the orientation you submitted, for example { "width": 11, "height": 14 } for a 10.5×13.5 gallery wrap. Absent on an exact match and on products without custom dimensions. It's on the build response. Calculate doesn't repeat it.

selectionsSummary[]

The choices that describe this item, ready to display. For example, name "Moulding" with description "Woodland 3/4" walnut frame". id matches the selection's field in the catalog. It isn't a copy of your selections: fields that don't change how the item is described are left out, and an option you turned off can read "None". A selection you sent that is missing here may have been dropped, so check before you submit. See Quick Start Step 3.

isValidForOrder, validationSummary

Whether this item is ready to order and, when it isn't, a list of what's missing (decoded in Quick Start Step 4).

links

Proof images to show your customer what was ordered: designSpaceProof (the design) and placementProof (the image as placed on the product). A build response can also carry proof, a shareable page showing the product preview. All of these open without your API key or a login, and they don't expire, so anyone who has the URL can view it. Share them with your customer, and don't post them publicly.

metadata

The item's own metadata map, returned as you sent it, or {} when you didn't send one. See Metadata & Reference.

productionMethod, packaging

Appear on some products only: how WHCC produces the item and the packaging chosen for it. Useful for display; safe to ignore.

previewUrl

The primary preview at 1024 px wide: identical to previews[0].scale_1024 (and to previews[0].previewUrl). null until rendered.

If you need a field that isn't here, tell us what you're building. We're happy to help.

GET /v1/commerce/submissions

Your submissions, newest first, as short rows for lookups and reconciliation. Fetch a row's actions.detail when you need its items. Rows and guidance trimmed:

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
29
30
{
	"submissions": [
		{
			"id": "<submissionId>",
			"status": "placed",
			"reference": "",
			"createdAt": "2026-09-24T20:52:00.641Z",
			"itemCount": 1,
			"totalPrice": { "code": "USD", "value": "95.06" },
			"orders": [
				{
					"confirmationId": "<confirmationId>",
					"status": "order_placed",
					"actions": { "fulfillment": { "url": "https://apis-stage.whcc.com/api/v1/fulfillment/orders?sourceConfirmationId=<confirmationId>", "method": "GET" } }
				}
			],
			"actions": {
				"detail": { "url": "https://apis-stage.whcc.com/api/v1/commerce/submissions/<submissionId>", "method": "GET" },
				"fulfillment": { "url": "https://apis-stage.whcc.com/api/v1/fulfillment/orders?sourceOrderId=<submissionId>", "method": "GET" }
			}
		},
		{ "…": "trimmed" }
	],
	"hasMore": true,
	"nextCursor": "<opaque-cursor>",
	"guidance": [
		{ "code": "submission.fanout.multiple_orders", "message": "A single submission can be split into more than one WHCC order; each has its own order number and confirmation id and ships and tracks independently. Do not assume a one-to-one mapping between a submission and a WHCC order." },
		{ "…": "trimmed" }
	]
}

id, status, reference, createdAt, orders[]

The same values as on the full submission. A list row doesn't carry items or metadata. Fetch the submission for those.

itemCount, totalPrice

How many items the submission holds, and its grand total as { value, code }. For the full price breakdown, fetch the submission itself.

orderNumbers[]

Every order number in orders[], as a flat list. Handy when a customer reads you a number off their packing slip. Missing until WHCC has received the first order.

actions.detail, actions.fulfillment

Links to the full submission and to its fulfillment orders.

hasMore, nextCursor

Whether there's another page, and the cursor to fetch it: pass nextCursor back as the cursor query parameter. nextCursor is missing on the last page. Treat it as opaque, and don't change your filters mid-walk: a cursor that was altered returns 400 with code: "CURSOR_INVALID", and one issued for different filters returns 400 with code: "CURSOR_FILTER_MISMATCH". An expired cursor returns 410. Either way, start again from the first page.

guidance[]

Plain-language notes about the list, the same shape as a submission's own guidance[].

Optional query parameters:

hasFailure

true returns only submissions with at least one failed order, so both failed and partially_failed. This is the quickest way to find what needs your attention.

status

Only submissions in one status. On a long history this can be slower than hasFailure.

reference, orderNumber, confirmationId

Only submissions carrying that exact value. These filter the list; they don't look up a single submission, so you get a page back (possibly empty), never a 404. orderNumber matches only once the submission carries that number, which is after WHCC has received the order.

createdAfter, createdBefore

Only submissions created after or before an ISO 8601 timestamp.

limit, cursor

Page size, from 1 to 100 (default 25), and the nextCursor from the previous page.

POST /v1/commerce/submissions/calculate

Prices and checks a submission body without placing anything. The response is the submission shape plus options.shipping[], minus the fields that only exist once something is submitted: id, status, createdAt, orders[] and actions. Calculate creates nothing, so call it as often as you like.

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
	"reference": "",
	"accountId": "<yourAccountId>",
	"items": [
		{ "…": "trimmed, the same item fields as a submission" }
	],
	"totalPrice": {
		"value": "91.26",
		"code": "USD",
		"total": { "value": "84.40", "code": "USD" },
		"subtotals": {
			"product": { "value": "76.45", "code": "USD" },
			"packaging": { "label": "", "value": "0.00", "code": "USD", "items": [ { "…": "trimmed" } ] },
			"shipping": { "label": "Economy", "value": "7.95", "code": "USD", "items": [ { "…": "trimmed" } ] },
			"surcharges": { "minimum": null, "value": "0.00", "code": "USD" },
			"discount": null,
			"salesTax": { "value": "6.86", "code": "USD", "isExempt": false, "items": [ { "…": "trimmed" } ] },
			"minimumSurcharge": null
		}
	},
	"validation": { "isValidForSubmit": true, "isValidDiscount": null },
	"metadata": {},
	"instructions": "",
	"shippingAddress": {
		"attn": null,
		"name": "John Doe",
		"email": null,
		"country": "US",
		"addr1": "3005 Eagandale Place",
		"addr2": null,
		"city": "Eagan",
		"state": "MN",
		"zip": "55121",
		"zipType": null,
		"phone": null
	},
	"guidance": [
		{ "code": "order.safe_use.result_ordering_unspecified", "message": "Result ordering is not guaranteed; correlate results by configuredProductId." },
		{ "code": "submission.pricing.total_excludes_tax", "message": "totalPrice.value includes tax; totalPrice.total.value is the pre-tax amount. Bill the customer totalPrice.value." }
	],
	"shippingType": "dropship",
	"shippingPriority": "dropship_lowest_cost",
	"packagingType": null,
	"options": {
		"shipping": [
			{ "id": "dropship_lowest_cost", "type": "dropship", "priority": "dropship_lowest_cost", "label": "Economy", "price": { "value": "7.95", "code": "USD" } },
			{ "id": "dropship_expedited", "type": "dropship", "priority": "dropship_expedited", "label": "Expedited", "price": { "value": "20.61", "code": "USD" } },
			{ "id": "dropship_one_day_standard", "type": "dropship", "priority": "dropship_one_day_standard", "label": "Standard One-Day", "price": { "value": "31.34", "code": "USD" } },
			{ "id": "dropship_one_day_priority", "type": "dropship", "priority": "dropship_one_day_priority", "label": "Priority One-Day", "price": { "value": "40.48", "code": "USD" } }
		]
	}
}

options.shipping[]

The shipping methods available for this basket and address, each with its price. Each entry has id, type, priority, label and price. To choose one, send its type and priority as shipping.type and shipping.priority. Only present when you send a shipping address, because the price depends on where it's going.

Every other field means the same as it does on the submission. An item with isValidForOrder: false is still priced in totalPrice.

Calculating with no shipping address

shipping.address is optional on calculate, so you can price a basket before checkout collects an address. Without one, WHCC doesn't guess a destination: anything that depends on where the order ships is left out, not shown as zero.

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
29
30
{
	"reference": "",
	"accountId": "<yourAccountId>",
	"items": [ { "…": "trimmed" } ],
	"totalPrice": {
		"value": "76.45",
		"code": "USD",
		"total": { "value": "76.45", "code": "USD" },
		"subtotals": {
			"product": { "value": "76.45", "code": "USD" },
			"packaging": { "label": "", "value": "0.00", "code": "USD", "items": [ { "…": "trimmed" } ] },
			"surcharges": { "minimum": null, "value": "0.00", "code": "USD" },
			"discount": null,
			"minimumSurcharge": null
		}
	},
	"validation": { "isValidForSubmit": false, "isValidDiscount": null },
	"metadata": {},
	"instructions": "",
	"guidance": [
		{ "code": "order.safe_use.result_ordering_unspecified", "message": "Result ordering is not guaranteed; correlate results by configuredProductId." },
		{
			"code": "submission.pricing.destination_required_for_full_total",
			"message": "No shipping address was supplied, so no destination is assumed: shipping, salesTax, the US MN/CO retail delivery fee and the Canada import surtax are omitted rather than defaulted or reported as zero. totalPrice reflects product, packaging, discount, surcharges and minimumSurcharge only -- it is a product-only subtotal, not a checkout total. Supply shipping.address to receive the full breakdown."
		}
	],
	"shippingType": "dropship",
	"shippingPriority": "dropship_lowest_cost",
	"packagingType": null
}

What's different: subtotals.shipping, subtotals.salesTax, shippingAddress and options are missing entirely, and isValidForSubmit reads false until you add an address. Treat this totalPrice as a product subtotal, not a checkout total. Shipping, tax and destination fees (such as the Minnesota and Colorado retail delivery fees and the Canada import surtax) are still to come. Send shipping.address to get the full picture.

Guidance codes

Calculate, a submission, and the submission list each carry a guidance[] array of { code, message } notes. They aren't errors, and the message says what to do. These are the codes you'll see today; new ones can appear, so show or ignore one you don't recognize rather than failing on it.

submission.pricing.total_excludes_tax

Read the code as naming totalPrice.total, which is the pre-tax amount. totalPrice.value includes tax, and that is what to bill. See Submission fields.

submission.pricing.destination_required_for_full_total

You calculated without an address, so shipping and tax are absent and totalPrice is a product subtotal. See Calculating with no shipping address.

submission.fanout.multiple_orders

One submission can become several WHCC orders, each with its own number and confirmation ID, shipping and tracking on its own. Track each entry in orders[].

submission.failure.rejected_by_whcc

WHCC's order system rejected the order. Contact support with the submission ID. See Errors & Rate Limits.

order.lifecycle.submission_enqueued

The submission is queued and not yet placed. Hear the outcome through an event subscription, and read the submission once after it settles.

order.correlation.order_id

An order's id is its permanent identifier. Use it to look the order up and to match it to your records.

order.correlation.confirmation_pending

The WHCC order number isn't assigned yet. Use id until orderNumber appears.

order.safe_use.result_ordering_unspecified

Items in a response aren't in a guaranteed order. Match them by configuredProductId, never by position.

order.tracking.available_on_ship

Shipment details appear once the order reaches order_shipped. Treat the order's status as the truth about whether it shipped; a shipped order can, rarely, carry no shipment detail.

order.support.cancellation_unavailable

Appears on orders that have reached fulfillment. Cancellation through the API is possible only while an order is eligible; see Canceling an order, and contact customer service otherwise.

Back to Top 👆
Get in Touch

Interested in integrating with WHCC? Tell us more about what you’re looking for and we’ll be in touch.