Documentation

Shipping and tax

Shipping rate calculation, label generation, address verification, and tax rules per jurisdiction.

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

POST/shipping/ratesNo auth
POST/shipping/calculateNo auth
POST/shipping/order/ratesJWT
POST/shipping/estimated-deliveryNo auth
POST/shipping/product-costNo auth

rates 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

POST/shipping/createJWT
POST/shipping/labelJWT
GET/shipping/label/:shippingId/:format?JWT
GET/shipping/track/:trackingNumberNo auth

create 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

POST/shipping/order/createJWT
POST/shipping/order/manualJWT
GET/shipping/order/shipments/:orderNumberJWT
GET/shipping/order/status/:orderNumberJWT

order/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

POST/shipping/address-autocompleteNo auth
GET/shipping/place-details/:placeIdNo auth
POST/shipping/verify-addressNo auth
POST/shipping/update-addressJWT

Autocomplete 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)

GET/shipping/admin/providersJWT
GET/shipping/admin/integrationsJWT
GET/shipping/admin/configsJWT
POST/shipping/admin/configsJWT
POST/shipping/admin/configs/:nameJWT
POST/shipping/admin/configs/delete/:nameJWT

Configure which carriers are active, default packaging, weight units, and per-product overrides.

Available locations and methods

GET/shipping/locations/availableNo auth
GET/shipping/methodsNo auth
GET/shipping/listJWT
POST/shipping/cancelJWT

Locations 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

POST/storefront/tax/calculateNo auth
POST/storefront/tax/rateNo auth
POST/storefront/tax/productNo auth

calculate 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

POST/storefront/tax/check-exemptNo auth
POST/storefront/tax/apply-exemptionJWT
POST/storefront/tax/remove-exemptionJWT

Exemptions 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.