Shipping and tax are two distinct subsystems but always called together at checkout — the customer's address determines both the shipping rate and the tax rate. Both modules speak the same address shape and both round-trip the cart for cross-validation.
Shipping
The shipping module is provider-agnostic — Shippo, EasyPost, ShipStation, and direct UPS/FedEx/USPS connections plug into the same interface via the integrations module.
Rate quotes
/shipping/ratesNo auth/shipping/calculateNo auth/shipping/order/ratesJWT/shipping/estimated-deliveryNo auth/shipping/product-costNo authrates accepts cart line items and from/to addresses, returns quotes from every configured carrier. The checkout UI calls this and renders a picker. calculate is for showing "ships for $5" on a product page without a full cart. order/rates operates against an existing order rather than a cart shape.
const rates = await fetch('/api/shipping/rates', {
method: 'POST',
headers: { 'Content-Type': 'application/json', orgid: ORG_ID },
body: JSON.stringify({
cart: cartObject,
fromAddress: warehouseAddress,
toAddress: customerShippingAddress,
}),
}).then(r => r.json());
// {
// rates: [
// { carrier: 'usps', service: 'ground', price: 8.99, days: 5, rateId: 'rate-abc' },
// { carrier: 'ups', service: '2-day', price: 21.50, days: 2, rateId: 'rate-def' }
// ]
// }
Labels and tracking
/shipping/createJWT/shipping/labelJWT/shipping/label/:shippingId/:format?JWT/shipping/track/:trackingNumberNo authcreate purchases the rate (returns a label PDF/PNG and tracking number). label/:shippingId re-downloads the label later. track is public so a confirmation email's "track package" link works without auth.
Order-scoped operations
/shipping/order/createJWT/shipping/order/manualJWT/shipping/order/shipments/:orderNumberJWT/shipping/order/status/:orderNumberJWTorder/create ships an order through the configured rate; order/manual records a shipment fulfilled outside the platform (e.g. dropshipped, picked up in store). The status endpoint rolls up multi-package orders.
Address utilities
/shipping/address-autocompleteNo auth/shipping/place-details/:placeIdNo auth/shipping/verify-addressNo auth/shipping/update-addressJWTAutocomplete and place-details power the address-typeahead in checkout (Google Places). verify-address validates against the carrier's database — required by some carriers and a good UX win regardless.
Configuration (admin)
/shipping/admin/providersJWT/shipping/admin/integrationsJWT/shipping/admin/configsJWT/shipping/admin/configsJWT/shipping/admin/configs/:nameJWT/shipping/admin/configs/delete/:nameJWTConfigure which carriers are active, default packaging, weight units, and per-product overrides.
Available locations and methods
/shipping/locations/availableNo auth/shipping/methodsNo auth/shipping/listJWT/shipping/cancelJWTLocations are pickup/dropoff points — for buy-online-pickup-in-store flows. Methods are the shipping options exposed to customers (free, standard, express, same-day).
Tax
Tax rates depend on jurisdiction (country, state, county, city), product taxability, and customer exempt status. The module supports flat-rate, table-driven, and external-provider modes (Avalara, TaxJar, Stripe Tax via the integrations layer).
Calculation
/storefront/tax/calculateNo auth/storefront/tax/rateNo auth/storefront/tax/productNo authcalculate accepts the cart and the ship-to address and returns line-by-line tax. rate returns the single rate for an address (used for a "estimated tax" display before adding to cart). product returns the tax classification for a SKU.
// POST /storefront/tax/calculate
{
"cart": { ... },
"shippingAddress": { "country": "US", "state": "CA", "zip": "94110" }
}
// Response
{
"totalTax": 17.60,
"lines": [
{ "sku": "SNK-AIR-1-9-BLK", "tax": 7.45, "rate": 0.0825 },
{ "sku": "SHIRT-BLU-M", "tax": 10.15, "rate": 0.0825 }
],
"jurisdictions": [
{ "name": "California State", "rate": 0.06 },
{ "name": "San Francisco County", "rate": 0.0125 },
{ "name": "City of San Francisco", "rate": 0.01 }
]
}
Exemptions
/storefront/tax/check-exemptNo auth/storefront/tax/apply-exemptionJWT/storefront/tax/remove-exemptionJWTExemptions are per-customer (resale certificates, non-profit status). check-exempt returns whether a customer has any active exemption — the cart re-runs tax/calculate with the exemption to reflect $0 tax.
At checkout
The discount/calculate endpoint internally calls tax/calculate and shipping/rates, so most checkouts only need to call discounts/calculate and pass shipping/billing — the totals come back already correct. Use the per-module endpoints when you need finer control (e.g. preview tax on a product page before any cart exists).
For a US storefront with multi-state nexus, default to an external tax provider. The flat-rate engine is fine for single-state operations but won't track economic-nexus thresholds for you.