Every call to POST /v1/commerce/submissions and POST /v1/commerce/submissions/calculate needs a shipping object, and both accept the same values. The one difference is that shipping.address is optional on calculate, so you can price a basket before checkout collects an address (see Response Bodies).
shipping objectaddress
object, required on submit
Where the order ships: name, addr1, addr2, city, state, zip, country (2-letter code), and optionally attn (an attention line), email and phone. There is no company field: sending one returns 400. International destinations may also need a phone in E.164 format, for example +15551234567. Optional on calculate only.
Every address needs name, addr1 and city. Send country every time too: an address without one is read as a US address, not reported as incomplete. A US address also needs a 2-letter state and a zip of at least 5 characters. Outside the US, state and zip are optional. Submit with a field missing and you get a 400 (submission.not_submittable) with one invalidParams[] entry per field, named like shipping.address.zip. Calculate with a partial address still answers 200, with validation.isValidForSubmit: false and the same entries in validation.invalidParams[], so you can check an address before you submit. A zip under 5 characters or a 1-letter state also makes isValidForSubmit false, with an entry whose reason gives the length rule (Must be at least 5 characters., Must be 2 characters.). See what WHCC checks.
type
string, optional
dropship ships straight to your customer. studio ships to you. Leave it out and it defaults to dropship, or follows priority when you send one: studio_* means studio, dropship_* means dropship. If you send both, they must agree. If your account can't use that type for these products, the request is rejected with a 400 naming shipping.type.
Studio shipping goes to US and Canadian street addresses only: not to a PO Box, and not outside the US and Canada.
priority
string, optional
The shipping method. Which methods a basket can use depends on the type, the address and the products in it, so take the value from options.shipping[] on a calculate response for that address (see below). Leave it out and WHCC uses the least expensive method the destination offers. A priority from the other type, or one the destination doesn't offer, is rejected with a 400 (see the note at the end of this page).
Call calculate with the basket, shipping.type and the address, and no priority. The response prices the basket with the least expensive method for that destination, and options.shipping[] lists every method the address offers, each with its price:
1
2
3
4
5
6
7
8
9
10
11
12
{
"shippingType": "dropship",
"shippingPriority": "dropship_lowest_cost",
"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" } }
]
}
}
Show your customer the label and price of each entry. To ship with one of them, send its priority as shipping.priority on submit, with the same type. To take the cheapest, leave priority out. Either way, shippingType and shippingPriority on the response tell you what the order is using, and totalPrice.subtotals.shipping.label names it. See Response Bodies.
A few things to expect from the list:
label can name the carrier.options.shipping[], because the choices depend on where the order is going (see calculating with no address).Calculate returns the current price for the items and address you send. The amount you're billed is set by WHCC's order system when it places the order, from the same pricing, so for most US addresses the two match. Nothing in the response marks a value as provisional, and the price isn't held between calculate and submit. For a US territory or an address outside the US, expect gaps: shipping to Puerto Rico can show 0.00 with the description "Actual Postage", and sales tax depends on the destination: money for a US state, and for Canada when the address has a province and a zip; 0.00 for Alaska and the US territories; and the string "TBD" for Canada without a province or zip, for an APO/FPO address, and for any other country, so totalPrice.value doesn't include it. An order to Canada adds the import surtax under subtotals.surcharges.delivery. See Response Bodies.
Calculate and submit check that the required fields are present. They don't verify that the address exists, that the zip belongs to the state, or that phone and email are well formed. Validate the address in your own checkout before you send it.
country left out or empty is treated as US, with no invalidParams[] entry for it. The US rules for state and zip then apply. A code that isn't a real country is not rejected: it is priced as an international destination.country and state are accepted in either case and returned as you sent them. Send codes, not names: a name such as United States or Minnesota returns 400.name, addr1, addr2 or city and doesn't screen their characters: markup, emoji and control characters come back as sent. Labels print from the Windows-1252 character set (see Metadata & Reference), so send plain text that fits on a label.state or zip WHCC can't place leaves sales tax as "TBD". A "TBD" response doesn't carry the submission.pricing.total_excludes_tax guidance note.addr1 is offered a single dropship method, and no studio method.If you send both type and priority, make them agree. A priority belongs to the type in its name: dropship_* to dropship, studio_* to studio. A pair that disagrees is rejected with a 400 whose invalidParams[] names both shipping.type and shipping.priority. A value that isn't shaped like any priority is rejected the same way, naming shipping.priority.
A well-formed priority that the destination doesn't offer (one from a Canadian address sent with a US address, say) is also rejected once you've sent an address: 400 with code: "submission.shipping_priority_unsupported", and the reason lists the priorities of the same shipping type that are available, if there are any. The list can be missing even when other priorities are on offer. Calculate without an address never fails this check. To avoid it, ask calculate what the address offers and pick from that.
See the submission shape this produces on Response Bodies, or how to carry your own order identifiers alongside it on Metadata & Reference.