Orders & fulfilment
Live Carrier Rate APIs
Indicative price, not an offer: $399–899/year
Generated from docs/plan/paid/live-carrier-rate-apis/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Integrates with UPS, FedEx, DHL, and USPS APIs to fetch real-time shipping rates at checkout based on destination, weight, and carrier account. Merchants configure carrier credentials; system queries APIs and displays rates to customer.
The problem
I maintain shipping rates by hand in a spreadsheet and update them monthly. Customers see stale prices and I often lose money on heavy shipments. I need real-time rates from my carrier accounts.
What it does
- Create CarrierAccount entity: carrier_name (ups|fedex|dhl|usps), account_id, api_key, meter_id (UPS), shipper_id (FedEx), is_active, created_at
- Admin settings: configure one account per carrier. Test connection button validates credentials.
- Shipping rate calculation: at checkout, after zip/country entered, call carrier APIs for live rates. Return sorted list of available shipping methods (2-day, overnight, economy, etc.)
- Rate caching: cache rates for 30 minutes per origin/destination/weight combo to avoid 1000s of API calls; cache invalidates on new orders.
- Error handling: if carrier API is down, fall back to merchant’s manual rate table for that carrier. Admin sees alert that carrier X is unavailable.
- REST: POST /shipping/rates (body: {origin_zip, dest_zip, weight_g, carrier_name?}) → [{carrier, method, cost_minor, delivery_days, carrier_rate_id}]
- Order creation: order.shipping_carrier and order.shipping_method stored for later fulfillment.
- Webhook: shipping.rates_requested with origin, dest, weight, carriers_queried
- Rate logging: every rate query logged with origin, dest, weight, carrier response, cost, timestamp (for auditing carrier billing)
- Multi-currency: carrier APIs return rates in USD; system converts to order currency at checkout time
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Negotiated rate agreements: out of scope—system uses published rates from carrier APIs. Reason: per-account negotiations are merchant’s responsibility; system uses whatever rate account submits.
- Regional rate rules (e.g., Alaska/Hawaii surcharges): out of scope—merchant configures surcharges as manual overrides per carrier. Reason: complex rule engine; merchants add percentage markup in admin.
- Dimensional weight pricing: out of scope—system sends weight to carrier, carrier returns rate; if DimWeight applies, carrier API handles it. Reason: carrier-specific; merchant to verify with carrier.
- Postage meters and label printing: out of scope—shipping-label-generation handles printing. Reason: separate feature; rate fetching is read-only.
- Rate tiers / volume discounts: out of scope—system queries API with actual weight/dimensions; no tiering logic. Reason: carriers return tier-appropriate rate.
- International duty/tariff calculation: out of scope. Reason: outside carrier rate scope; merchant’s customs broker handles.
Data model
Migration: new CarrierAccount table/collection (account_id, carrier_name, api_key, meter_id, shipper_id, is_active, created_at, updated_at). New RateCache table/collection (cache_id, origin_zip, dest_zip, weight_g, carrier_name, rate_json, cached_at, expires_at, cost_minor). Add shipping_carrier, shipping_method to Order.
API
- POST /carrier-accounts (staff, body: {carrier_name, api_key, meter_id?, shipper_id?}) → {account_id, carrier_name, is_active}
- GET /carrier-accounts (staff) → [{account_id, carrier_name, is_active, last_rate_query_at}]
- PUT /carrier-accounts/:id (staff, body: {is_active, api_key?}) → {account_id}
- DELETE /carrier-accounts/:id (staff) → 204
- POST /shipping/rates (public, body: {origin_zip, dest_zip, weight_g, carrier_name?}) → [{carrier, method, cost_minor, delivery_days}]
- GET /shipping/rates/cache-status (staff) → {carrier_name, last_updated, hit_rate, misses_today}
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Carrier Integrations section: list configured accounts (UPS, FedEx, DHL, USPS) with active toggle, last query time, API key masked. Test connection button for each. Edit to update API key or toggle. Shipping settings show rate cache hit rate and last refresh time.
The seam — why this is paid
Core owns the rate fetching interface and cache. Paid pack owns: carrier credential management (per-country account setup), negotiated rate overrides, complex rule engine (surcharges, volume tiers, regional rules). Why: core provides honest read-only rate query; carrier credentials and rule complexity are paid.
Requires carrier API credentials (meter/shipper account) for each carrier. Ongoing support commitment for rate debugging, carrier API deprecations, and shipping calculation overrides.
Dependencies
- shipping (checkout must call rate API before displaying options)
- address-book-with-billing-shipping-split (rate query needs destination zip)
- settings (carrier account credentials stored as key/value)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- CarrierAccount created with carrier_name=‘ups’, api_key=‘test_key’; GET /carrier-accounts returns it with is_active=true
- POST /shipping/rates with origin_zip=‘10001’, dest_zip=‘90210’, weight_g=500, carrier_name=‘ups’ returns [{carrier: ‘ups’, method: ‘2day’, cost_minor: 1899, delivery_days: 2}]
- Second call to same origin/dest/weight within 30 min returns cached result, not API call
- Cache expires after 30 min; third call makes new API query
- Carrier API returns error (e.g., ‘invalid account’); system returns 503 with ‘carrier.rate_api_unavailable’ and falls back to manual rates
- Rate includes all carrier fees (fuel surcharge, etc.) in single cost_minor value
- POST /shipping/rates with unknown carrier_name=‘spacex’ returns 400 with ‘carrier.not_configured’
- Multi-currency order (EUR): rate API returns USD cost; system converts to EUR at spot rate stored in order
- Rate query logged: GET /audit-log filters shipping.rates_requested, shows origin, dest, carrier, cost
- Deleting CarrierAccount does not cascade-delete historical orders; but new rate queries cannot use deleted account
Risks
API credentials: if api_key is leaked in logs, attacker can drain merchant’s carrier account. Rate conversion: if currency conversion rate is stale (spot rate), merchant eats the loss on FX. Cache coherency: if rate cache is not invalidated on account deletion, orders could request rates from deleted account. Carrier API: if carrier API is slow (>5s), checkout hangs; must timeout and fallback. Overselling: if rate cache is not cleared and carrier rates drop, merchant might auto-accept unprofitable orders.
Commercial context
| Suggested price | $399–899/year |
| Rival anchor | Magento ships four carrier integrations (UPS, FedEx, DHL, USPS) with real-time rates free. |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.