Skip to content

Shipping quotes and tracking

Sellers on United Pet Club price delivery themselves — from their own rate tables or from live carrier rates, with a markup and handling fee bounded by the platform. The shipping API exposes the parts of that a buyer-facing integration needs: quote a set of lines to a destination, confirm the option the buyer picked, and track a parcel. Everything a seller configures (carrier accounts, ship-from locations, shipping profiles, labels) is first-party only and is not available through this API.

All routes live under /api/fulfillment/v1 and are anonymous — no token is required. Responses use the platform envelope; read the payload from data. Money is always { amountMinorUnits, currencyCode }-style minor units next to an ISO 4217 code; times are UTC ISO 8601; enumerations travel as string codes.

POST /api/fulfillment/v1/quotes

One request prices every seller group in a cart to one destination. Lines carry the offer, the quantity, the line subtotal, the seller’s shipping-profile reference and the method being quoted, plus the per-unit package facts of the offer. All money in a group is in the group’s currencyCode.

{
"correlationId": "7f0e2f7e-5d0e-4d4b-9a3c-1b1e0c1d2e3f",
"localeCode": "en-CA",
"destination": {
"countryCode": "CA",
"subdivisionCode": "ON",
"postalCode": "M5V",
"latitude": null,
"longitude": null,
"isResidential": true
},
"groups": [
{
"storefrontId": "0f7a4c1e-1111-7000-8000-000000000001",
"sellerProfileId": "0f7a4c1e-2222-7000-8000-000000000002",
"currencyCode": "CAD",
"lines": [
{
"offerId": "0f7a4c1e-3333-7000-8000-000000000003",
"quantity": 2,
"lineSubtotalMinorUnits": 4500,
"shippingProfileId": "0f7a4c1e-4444-7000-8000-000000000004",
"methodCode": "GROUND_SHIP",
"handlingClassCode": "STANDARD",
"shipsSeparately": false,
"package": {
"weightGrams": 1200,
"lengthMillimeters": 300,
"widthMillimeters": 200,
"heightMillimeters": 100,
"declaredValue": null,
"referenceText": null,
"requiresAdultSignature": false,
"insuredMinorUnits": null
}
}
]
}
]
}
FieldNotes
destination.countryCoderequired, ISO 3166-1 alpha-2. subdivisionCode and postalCode (a prefix is enough) sharpen manual lanes and are required by most carriers for live rates
lines[].shippingProfileIdthe seller’s profile reference carried on the offer; null uses the seller’s default profile
lines[].methodCodeGROUND_SHIP, EXPRESS_SHIP, FREIGHT, COURIER or MAIL_ORDER_RX; null quotes every active method of the profile
lines[].shipsSeparatelytrue quotes this line as its own package instead of combining it with the group
lines[].packageper-unit weight (grams) and dimensions (millimetres) of the packed item — the parcel, not the product inside it. null leaves the line unmeasured: weight bands cannot price it and live carrier rating is skipped

Every line of a group that is not shipsSeparately is quoted as one parcel. Its weight is the sum of each line’s package.weightGrams × quantity. Its size is the largest footprint in the group, grown in height until the box holds the summed volume of its contents — so three 300 × 200 × 100 items quote as a 300 × 200 × 300 parcel, not as a 300 × 200 × 100 one. That matters because carriers bill the greater of actual and dimensional weight.

If any line in the group has no dimensions, the group’s size is left as the largest of what is known rather than inflated: the content volume is unknown, and guessing it would overcharge.

A line with shipsSeparately: true gets one parcel per unit, exactly as measured.

Response data:

{
"quoteId": "0f7a4c1e-5555-7000-8000-000000000005",
"expiresAtUtc": "2026-09-04T15:42:10Z",
"groups": [
{
"storefrontId": "0f7a4c1e-1111-7000-8000-000000000001",
"sellerProfileId": "0f7a4c1e-2222-7000-8000-000000000002",
"offerIds": ["0f7a4c1e-3333-7000-8000-000000000003"],
"options": [
{
"quoteOptionId": "0f7a4c1e-6666-7000-8000-000000000006",
"methodCode": "GROUND_SHIP",
"methodName": "Ground shipping",
"rateSourceKind": "MANUAL",
"carrierKind": null,
"carrierServiceCode": null,
"carrierServiceName": null,
"currencyCode": "CAD",
"baseMinorUnits": 1500,
"markupMinorUnits": 150,
"handlingMinorUnits": 200,
"capApplied": false,
"feeMinorUnits": 1850,
"handlingDaysMin": 1,
"handlingDaysMax": 2,
"transitDaysMin": 3,
"transitDaysMax": 6,
"estimatedDeliveryDateMin": "2026-09-08",
"estimatedDeliveryDateMax": "2026-09-11",
"providerKindCode": "SELLER"
}
],
"unavailable": [
{
"methodCode": "EXPRESS_SHIP",
"methodName": "Express shipping",
"reasonCode": "REGION_BLOCKED",
"detail": null
}
]
}
]
}

What to do with it:

  • Show feeMinorUnits as the delivery price. It already includes the seller’s markup and handling; baseMinorUnits, markupMinorUnits and handlingMinorUnits are the breakdown and capApplied tells you the platform cap trimmed the surcharge. Do not recompute the fee.
  • Name the option carrierServiceName ?? methodName — a live-rated option carries the carrier service (“UPS Ground”); a rate-table option carries the seller’s method name.
  • Keep quoteOptionId with the buyer’s selection; it is what you confirm at checkout.
  • unavailable lists methods the seller offers that could not be priced for this destination, with a reason code. Show them as unavailable, not as errors.
  • providerKindCode is SELLER or PLATFORM — who fulfils the label. Purely informational.
reasonCodeMeaning
REGION_BLOCKEDthe seller does not ship this method to the destination
NO_LANEno rate-table lane covers the destination
CARRIER_UNAVAILABLEthe live carrier rate could not be obtained; rate-table options still quote
ACCOUNT_INACTIVEthe seller’s carrier account (or platform authorization) is not active
ORIGIN_NOT_SUPPORTEDthe carrier does not ship from the seller’s origin country
NOT_ALL_OFFERSnot every line in the group offers this method
MIXED_CURRENCIESlines in one group disagree on currency
QUOTE_EXPIREDreturned by confirm when an option could not be re-priced

Two detail values say the seller’s side is not set up, so no destination will do better: NO_LANE with SHIP_FROM_MISSING (the seller has no ship-from location and no address on its profile yet) and CARRIER_UNAVAILABLE with SHIP_FROM_INCOMPLETE (the seller’s address has no postal code, so a live carrier rate cannot be asked for; its rate-table options still quote). A seller without a ship-from location ships from its profile’s address. The quote never returns that address or a distance to it.

Validity is set by the seller (rate-table options up to 30 minutes by default, live carrier options up to 15) and the quote’s expiresAtUtc is the earliest of its options.

Errors: 400 for a malformed request (missing destination country, empty groups, unknown method code); the envelope’s resultCode is Fulfillment_FulfillmentQuote_VL_Request_RJ.

GET /api/fulfillment/v1/quotes/{quoteId}

Returns the persisted quote in exactly the shape above. 404 (Fulfillment_FulfillmentQuote_GT_Quote_FL) when the id is unknown. An expired quote still reads for a while, so check expiresAtUtc before trusting the prices. Seven days after its last option expired, a quote nobody confirmed is deleted and reads 404 too. A confirmed quote is kept.

POST /api/fulfillment/v1/quotes/{quoteId}/confirm

{ "quoteOptionIds": ["0f7a4c1e-6666-7000-8000-000000000006"] }

Confirm the options the buyer selected immediately before taking payment. An option that is still valid comes back unchanged; an expired one is re-priced and returned with priceChanged: true and replacedQuoteOptionId pointing at the option it supersedes — surface the new price to the buyer and confirm again with the new id.

{
"quoteId": "0f7a4c1e-5555-7000-8000-000000000005",
"expiresAtUtc": "2026-09-04T16:02:10Z",
"options": [
{
"quoteOptionId": "0f7a4c1e-7777-7000-8000-000000000007",
"methodCode": "GROUND_SHIP",
"methodName": "Ground shipping",
"rateSourceKind": "MANUAL",
"carrierKind": null,
"carrierServiceCode": null,
"carrierServiceName": null,
"currencyCode": "CAD",
"baseMinorUnits": 1500,
"markupMinorUnits": 150,
"handlingMinorUnits": 200,
"capApplied": false,
"feeMinorUnits": 1850,
"handlingDaysMin": 1,
"handlingDaysMax": 2,
"transitDaysMin": 3,
"transitDaysMax": 6,
"estimatedDeliveryDateMin": "2026-09-08",
"estimatedDeliveryDateMax": "2026-09-11",
"providerKindCode": "SELLER",
"priceChanged": true,
"replacedQuoteOptionId": "0f7a4c1e-6666-7000-8000-000000000006"
}
]
}

410 (Fulfillment_FulfillmentQuote_UP_Expired_RJ) when an option can no longer be re-priced — start a new quote. A quote already deleted (seven days after its last option expired) answers 404 (Fulfillment_FulfillmentQuote_GT_Quote_FL): start a new quote there too.

GET /api/fulfillment/v1/tracking/{trackingNumber}

Returns the parcel’s normalized status and its scan events, oldest first, for any tracking number issued or recorded on the platform. No seller or buyer data is included. 404 for a number the platform does not know. This read is rate-limited per caller like every anonymous route — poll a parcel no more often than the platform itself does (hourly before the first scan, every few hours in transit, every 30 minutes out for delivery).

{
"trackingNumber": "1Z999AA10123456784",
"carrierKind": "UPS",
"status": "IN_TRANSIT",
"events": [
{
"occurredAtUtc": "2026-09-05T13:04:00Z",
"status": "SHIPPED",
"carrierStatusCode": "P",
"description": "Pickup scan",
"locationText": "Ottawa, ON, CA"
},
{
"occurredAtUtc": "2026-09-06T02:11:00Z",
"status": "IN_TRANSIT",
"carrierStatusCode": "I",
"description": "Departed from facility",
"locationText": "Mississauga, ON, CA"
}
]
}
statusMeaning
LABEL_CREATEDthe seller has a label; the carrier has not received the parcel
SHIPPEDfirst carrier scan
IN_TRANSITmoving between facilities
OUT_FOR_DELIVERYon the final vehicle
DELIVEREDdelivered
EXCEPTIONdelayed, address problem, held, or a delivery attempt failed
RETURNEDon its way back to the seller
LOSTdeclared lost

carrierStatusCode is the carrier’s own raw code — informational; key your logic on status. carrierKind is one of the carrier-kinds vocabulary below, or null for a parcel the seller recorded with a carrier outside that set. Events for those parcels, and for carriers the platform holds no account with, may stay empty while status still moves as the seller updates it.

GET /api/fulfillment/v1/lookups/{set}?locale=xx-XX

Localized names for the codes used above (the name follows the platform’s locale fallback). Sets: carrier-kinds, rate-source-kinds, amount-kinds, band-axis-kinds, region-scope-kinds, region-rule-kinds, shipment-statuses, label-statuses, carrier-account-statuses, label-charge-statuses, label-authorization-statuses, methods, environments.

[
{ "id": 1, "code": "UPS", "name": "UPS", "description": null, "sortOrder": 10 },
{ "id": 2, "code": "FEDEX", "name": "FedEx", "description": null, "sortOrder": 20 },
{ "id": 3, "code": "DHL_EXPRESS", "name": "DHL Express", "description": null, "sortOrder": 30 },
{ "id": 4, "code": "CANADA_POST", "name": "Canada Post", "description": null, "sortOrder": 40 },
{ "id": 5, "code": "USPS", "name": "USPS", "description": null, "sortOrder": 50 }
]

Key on code; display name. An unknown set is a 404.

Carrier accounts, ship-from locations, shipping profiles, rate tables, shipments and labels are managed by sellers in their United Pet Club dashboard and are first-party only — they are scoped to the signed-in seller and not exposed to partner applications. If you need to act on a seller’s behalf, the seller configures shipping in the dashboard and your integration reads the resulting prices through the quote route above.