Orders & fulfilment
Carrier Integrations (UPS, DHL, ELTA, DPD, Speedex)
Indicative price, not an offer: €99–299/month for all-5 bundle; or €20–60/month à-la-carte per carrier, plus per-label fees (€0.10–1.50 depending on service); owner sets
Generated from docs/plan/paid/carrier-integrations/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
A paid module providing real-time shipping rate quotes and label generation from major carriers (UPS, DHL, ELTA, DPD, Speedex). Handles credentials, carrier-specific compliance, label storage, and rate caching.
The problem
Merchants can’t offer real-time shipping rates from major carriers; they resort to manual quoting or lose customers to competitors with instant rate transparency. Building bespoke carrier API integrations is expensive and fragile across 5+ providers.
What it does
- Support UPS, DHL, ELTA, DPD, Speedex carrier APIs (one module per carrier)
- Accept shipment details (weight, destination, service level) and return live rate quote
- Generate shipping labels (PDF/ZPL) and store them in artifact storage
- Cache rates by route/weight for 24h to reduce API calls
- Override shipping-rules-engine rates with live carrier rates (if enabled)
- Track shipment status (in-transit, delivered, failed)
- Support label printing (queue for printer integration or download)
- Webhook support: receive tracking updates from carrier APIs
- Retry logic: if carrier API is down, fall back to shipping-rules-engine
- Per-carrier credential management (API key, account ID, password)
- Audit trail: log every rate lookup, label generation, credential rotation
- Support multiple carrier accounts per merchant (e.g., backup UPS account)
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.
- Per-shipment insurance — not included; merchant upsell. Reason: separate paid feature.
- Customs/international compliance — core module does not validate HS codes or generate customs forms. Reason: per-country obligation, separate paid feature.
- Carrier settlement/billing reconciliation — we don’t audit carrier invoices. Reason: financial reconciliation is outside scope.
- Pickup scheduling — automatic scheduling of carrier pickups not included. Reason: requires carrier-specific SLA negotiation.
- Hazmat handling — no validation of hazardous goods classification. Reason: per-country regulation, requires separate compliance module.
- Multi-leg delivery routing — single leg only; no optimization across carriers. Reason: routing optimization is paid (advanced logistics).
Data model
New tables: CarrierAccount {id, carrierId (‘ups’|‘dhl’|‘elta’|‘dpd’|‘speedex’), merchantId, credentials: {apiKey, accountNumber, …}, isActive, rotatedAt}; ShippingLabel {id, orderId, carrierId, shipmentTrackingNumber, labelPdf (artifact ID), labelZpl, generatedAt, status (‘pending’|‘printed’|‘in_transit’|‘delivered’|‘failed’)}; RateCache {id, carrierId, origin, destination, weight, serviceLevel, rate (minor units), expiresAt}; CarrierAuditLog {id, action (‘rate_lookup’|‘label_generated’|‘tracking_updated’), carrierId, details, timestamp}. No migration.
API
- POST /api/carriers/accounts
- GET /api/carriers/accounts
- DELETE /api/carriers/accounts/:accountId
- POST /api/carriers/quote
- POST /api/carriers/labels/generate
- GET /api/carriers/labels/:labelId
- POST /api/carriers/tracking
- POST /api/carriers/webhooks/:carrierId
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Carrier config page: add/remove carrier accounts, test API connection, view credential rotation history. Order fulfillment page: ‘Get Shipping Quote’ button, select carrier or auto-quote all, display rates, generate label, download/print. Label management: view generated labels, reprint, track shipment status in real-time. Audit log: all rate lookups, label generations, credential changes.
The seam — why this is paid
Core owns: shipping rules engine, order-level shipping calculation interface. Paid owns: carrier credentials, carrier API integration, real-time rate feeds, label generation, carrier-specific compliance and support.
The paid pack owns the credential (each carrier’s API key/account), per-country obligation (carrier-specific compliance and service agreements), and support commitment (handling carrier-specific bugs and rate changes).
Dependencies
- shipping-rules-engine (used as fallback if carrier API fails)
- Order/fulfillment context (shipment details)
- Artifact storage (to store PDF labels)
- Settings system (enable per-carrier, rate caching TTL)
- Webhook system (to receive tracking updates from carriers)
- Credential storage (secure key/value for API keys)
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.
- Registering a carrier account with invalid credentials returns {error: ‘invalid_credentials’} on test connection
- Getting a rate from UPS for weight=2kg, destination=‘GR’ returns {rate: 1500, carrierName: ‘UPS’, serviceLevel: ‘ground’, eta: ‘2-3 business days’}
- Rate cache hits: two identical rate requests within 1 hour return the same cached rate (second request does not call carrier API)
- Rate cache misses: after 24h expiry, third request calls carrier API again
- Generating a label for an order stores the PDF artifact and records ShippingLabel.labelPdf with artifact ID
- Label PDF is retrievable via GET /api/carriers/labels/:labelId and returns valid PDF (magic bytes %PDF)
- Tracking webhook from carrier updates ShippingLabel.status to ‘in_transit’ and inserts event into audit log
- If carrier API is down (500 response), rate quote falls back to shipping-rules-engine and returns rule-based rate with {rateSource: ‘rules_engine’, carrierUnavailable: true}
- Multiple carrier accounts for same carrier: merchant can select which account to use; system tries primary first, falls back to backup on credential error
- Credential rotation: rotating an API key changes CarrierAccount.credentials and updates rotatedAt; old key is not used on next request
Risks
Credential leak: API keys stored in plaintext or logged; mitigation: encrypt credentials at rest, audit log shows ‘credential_rotated’ but not key. Rate quote variance: merchant quotes rate, customer waits 2h, carrier price changes; mitigation: cache TTL is transparent. Label race condition: two admins generate labels for same order simultaneously; mitigation: order-level lock. Carrier API rate-limit: 1000 requests/hour; mitigation: client-side rate-limit, queue excess.
Commercial context
| Suggested price | €99–299/month for all-5 bundle; or €20–60/month à-la-carte per carrier, plus per-label fees (€0.10–1.50 depending on service); owner sets |
| Rival anchor | Shopify Shipping (free for Shopify partners, €0.50–1.50/label), Pirate Ship (free USPS-only), Stamps.com (€15–100/month), ShipStation (€9.99–40/month) |
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.